Decode string with PKC7 in Java? - java

There is the same question here:
Decode string with PKC7 in Java
I tried the solution described there, but obviously it is a little outdated, because in the current bouncycastle library there is no
org.bouncycastle.openssl.PEMReader
class, but
org.bouncycastle.openssl.PEMParser.
and when i used it in the example:
PEMParser pr = new PEMParser(sr);
X509Certificate cert = (X509Certificate)pr.readObject();
i got an exception:
Exception in thread "main" java.lang.ClassCastException: class org.bouncycastle.asn1.cms.ContentInfo cannot be cast to class java.security.cert.X509Certificate
I would like just to ask someone to provide solution of decoding PKCS#7 with the current Bouncycastle library.
The Maven part for Bouncycastle looks like this
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.58</version>
</dependency>
This is the PKCS#7 that i am trying to decode:
-----BEGIN PKCS7-----
MIIC5wYJKoZIhvcNAQcCoIIC2DCCAtQCAQExADALBgkqhkiG9w0BBwGgggK8MIICuDCCAl6gAwIB
AgIQXCpspdawOszPHWPu6BzK7DAKBggqhkjOPQQDAjBAMQswCQYDVQQGEwJERTEUMBIGA1UEChML
U29mdHdhcmUgQUcxGzAZBgNVBAMTElFBIFRoaW4tRWRnZSBDQSBHMTAeFw0yMjA2MjIwNzM1MDJa
Fw0yNTA2MjIwNzM1MDJaMCUxIzAhBgNVBAMTGlJlcXVlc3RlZCBUZXN0IENlcnRpZmljYXRlMIIB
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjahV4/HclaS48gxI6ojGk1WZsBr+oS2PwAG5
abWG1ilmOGakfSoJt5exz3+vfbqX9Wewge7kE+2ehM3kyhLxtRLc9o2IbvbRsonyncIhb0SqJ+6j
oTK8b7Dd1XwTFNLMNpQK2trY6c0ScBrdJarPv0QKszlpJHpFCCjozlaExdFKeYINUzSN6UKMd9Kw
L6T/3QPOQSBLVAwKnBC8dZnxmoG8Nr0XOJE0FBDISnB/PkE70w8bTqquCCqdWInFZJkZog+QsYBu
LDZq5ODeoUfSrkfnrR1xudgS5/OYRsU5jfwU5h4D1bM1MSQAMseOalFO+Iv5tm4tszNF3HNwSn8N
KQIDAQABo4GJMIGGMBMGA1UdJQQMMAoGCCsGAQUFBwMCMBMGA1UdIwQMMAqACEhBtjq9T25cMAsG
A1UdDwQEAwIFoDBNBgNVHR8ERjBEMEKgQKA+hjxodHRwOi8vZHAuMzgucWEuZ28ubmV4dXNncm91
cC5jb20vY3JsL3FhX3RoaW4tZWRnZV9jYV9nMS5jcmwwCgYIKoZIzj0EAwIDSAAwRQIhALgVQ4GE
NDd3KkjDjDE1a3Kn5FzjTWVjDc4ZdCsJvm1yAiBHdeGu8Sklq57MM1eSMMS7C4KvAYepzPkM4/9s
vgRT8zEA
-----END PKCS7-----

PKCS7 and its successor CMS is a wide-ranging standard that includes many types of messages. Some of these messages can contain one or more (X.509) certificate(s). If your PKCS7/CMS is actually a SignedData containing certificate(s), you don't need BouncyCastle at all; you can directly read the certificate(s) with standard Java CertificateFactory as described in the introductory section of the javadoc.
That is actually what the hacky code in the Q you link does; by putting a false PEM label of BEGIN/END CERTIFICATE on data that is in fact NOT a certificate, but actually a PKCS7/CMS SignedData containing a certificate, it causes the now-obsolete PEMReader code to call CertificateFactory internally. With more recent PEMParser that wouldn't work; you must parse it as PKCS7/CMS and then resolve it yourself. (Even 1.58 isn't exactly current, it's from 2017.) If you want to go that way, the following example reads the data from that Q (correctly labelled as BEGIN/END PKCS7, NOT faked as CERTIFICATE) and extracts the one cert:
String in = new String(Files.readAllBytes(Paths.get(args[0])) );
// import org.bouncycastle.asn1.* and ...cms.* (not superseded ...pkcs.*)
// and of course org.bouncycastle.openssl.PEMParser
#SuppressWarnings("resource") // I don't bother closing the PEMParser
ContentInfo ci = (ContentInfo) new PEMParser(new StringReader(in)).readObject();
if( !ci.getContentType().equals(CMSObjectIdentifiers.signedData) )
throw new Exception ("not SignedData"); // or other handling as suitable
SignedData sd = SignedData.getInstance( ci.getContent() );
// this takes the first/only cert and fails if none; could handle missing
// and/or loop over multiple looking for or selecting the desired one or ones
byte[] raw = ((ASN1Sequence)sd.getCertificates().getObjects().nextElement()) .getEncoded();
X509Certificate cert = (X509Certificate) CertificateFactory.getInstance("X.509")
.generateCertificate(new ByteArrayInputStream(raw));
System.out.println (cert.toString());
Numerous variations are possible depending on exactly what you need to handle.
Note SignedData.certs is an ASN.1 SET and arbitrarily ordered. If used for e.g. a rootlist this doesn't matter, but if used for a cert chain -- as many CAs did in ancient times -- the end-entity or 'leaf' cert, although logically first in the chain, may not be physically first in the PKCS7/CMS. In recent years CAs more often just use a sequence of (individual) PEM certs, which does preserve order -- and which CertificateFactory and keytool etc also handles.
Added: you now disclose you (also?) want the certificate in PEM form (the textlike form beginning -----BEGIN CERTIFICATE----- and ending -----END CERTIFICATE-----). This is trivial with Bouncy; just do
JcaPEMWriter w = new JcaPEMWriter (/*suitable Writer to a file or whatever */)
w.writeObject(cert);
w.close(); // or w.flush(); to keep underlying writer/stream open
or if you only need the PEM and aren't doing anything cryptoish with the cert object, more simply
PemWriter w = new PemWriter (/*suitable Writer as above */);
w.writeObject (new PemObject ("CERTIFICATE",raw));
w.close(); // ditto
For the standard-Java case you must do some of the work yourself:
// create or obtain a suitable Writer to file or whatever as w
w.write("-----BEGIN CERTIFICATE-----" + eol
+ Base64.getMimeEncoder().encodeToString(cert.getEncoded()) + eol
+ "-----END CERTIFICATE-----" + eol);
// w.flush() or w.close() if needed depending on the Writer
// or for a PrintStream (like System.out) optionally like
p.println("-----BEGIN CERTIFICATE-----");
p.write(Base64.getMimeEncoder().encode(cert.getEncoded());
p.println();
p.println("-----END CERTIFICATE-----");
// in principle eol should be appropriate for your output
// e.g. System.getProperty("line.separator") for a local file
// but possibly different for data sent someplace else;
// in practice PEM data usually works with either CRLF or LF
// on _all_ systems (and if not are usually easy to fix)
// _some_ systems even accept the base64 on one unbroken line
// as from Base64.getEncoder() rather than .getMimeEncoder()
// but I wouldn't suggest relying on that, it is nonstandard

Related

Create X509Certificate from PKCS7 PEM formatted certificate using BouncyCastle in Java

I have a PKCS7 certificate chain in PEM format. Basically I’d like to perform following openssl command in Java using BouncyCastle library.
openssl pkcs7 -in client-certificate_pkcs7.pem -out client-certificate_chain.pem
I was following this pdf provided from BouncyCastle (https://www.bouncycastle.org/fips-java/BCFipsIn100.pdf), but couldn’t really find anything that works on my need.
I’ve found that CMSSignedData is used for pkcs7 related operations in BouncyCastle so I tried using it, but I was getting an error and I assume it’s because I was using a raw String.
String pkcs7Pem = "-----BEGIN PKCS7-----\nMIIR...WTEA\n-----END PKCS7-----\n";
CMSSignedData data = new CMSSignedData(pkcs7Pem.getBytes());
Store certStore = data.getCertificates();
SignerInformationStore signerInfos = data.getSignerInfos();
Collection<SignerInformation> signers = signerInfos.getSigners();
List<X509Certificate> x509Certificates = new ArrayList<>();
for (SignerInformation signer : signers) {
Collection<X509CertificateHolder> matches = certStore.getMatches(signer.getSID());
for (X509CertificateHolder holder : matches) {
x509Certificates.add(new JcaX509CertificateConverter().setProvider("BC").getCertificate(holder));
}
}
This is the error I got on new CMSSignedData(pkcs7Pem.getBytes());
org.bouncycastle.cms.CMSException: IOException reading content.
I also have tried using PEMParser, but parser.readObject() below returns null.
String pkcs7Pem = "-----BEGIN PKCS7-----\nMIIR...WTEA\n-----END PKCS7-----\n";
PEMParser parser = new PEMParser(new StringReader(pkcs7Pem));
parser.readObject();
Any help would be appreciated!
openssl pkcs7 -in client-certificate_pkcs7.pem -out client-certificate_chain.pem
That command only copies its input (if it is PKCS7 PEM) to its output without any change. That's not useful, nor do you need BouncyCastle to do the same in Java. I suspect you actually meant
openssl pkcs7 -print_certs -in (pkcs7) -out (chain)
which extracts the individual certificate(s), as a sequence of separate (PEM) objects.
I’ve found that CMSSignedData is used for pkcs7 related operations in BouncyCastle
Only some -- there are lots of PKCS7/CMS formats and operations that can be done, and lots of different BC classes to do them. But the PKCS7/CMS format used to carry a certificate chain, often labelled p7b or p7c, is the SignedData type and is implemented by the BouncyCastle CMSSignedData class.
I also have tried using PEMParser, but parser.readObject() below returns null.
It shouldn't, if you give it valid PEM input not the mangled version you posted. For me if I give it a valid PEM PKCS7 containing a p7b/c cert chain I get a ContentInfo which can then be decoded as follows (I use file I/O for convenience, but any Java Reader and Writer will work the same):
static void SO70048115PKCS7Certs (String[] args) throws Exception {
PEMParser p = new PEMParser(new FileReader(args[0]));
CMSSignedData sd = new CMSSignedData( (ContentInfo)p.readObject() );
p.close();
JcaPEMWriter w = new JcaPEMWriter(new FileWriter(args[1]));
for( X509CertificateHolder ch : sd.getCertificates().getMatches(null) ){
// optionally put subject,issuer as 'comments' like OpenSSL does
w.writeObject( new PemObject("CERTIFICATE",ch.getEncoded()) );
}
w.close();
}
Note a p7b/c by convention does not contain any signature(s) (in PKCS7/CMS terms, SignerInfo(s)) so the code you posted -- which is designed to find the certificate(s) associated with the signature(s) -- is inappropriate and does not work. You need to simply use the certificate(s) and not expect any SignerInfo(s).

Can't reproduce openssl command with BouncyCastle

I am trying, for already a couple of weeks to reproduce some openssl commands using bouncycastle and java.
After following a lot of samples and trying a lot of examples from Stackoverflow, I still can't make it work, thats why I'm asking for help now.
The openssl commands I have to reproduce are :
openssl smime -sign -in fileToSign.eml -out signedFile.step2 -passin pass:« password» -binary -nodetach -signer myprivatecert.pem -certfile mypubliccert.pem
This first commands takes 3 files, the file to sign, a private certificate and a public certificate.
It returns a file looking like :
MIME-Version: 1.0 Content-Disposition: attachment;
filename="smime.p7m" Content-Type: application/x-pkcs7-mime;
smime-type=signed-data; name="smime.p7m" Content-Transfer-Encoding:
base64
MIJAYAYJKoZIhvcNAQcCoIJAUTCCQE0CAQExDzANBglghkgBZQMEAgEFADCCNTUG
CSqGSIb3DQEHAaCCNSYEgjUiQ29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7
CmJvdW5kYXJ5PSItLS0tPUxPR0lQT0xfTUlGXzE1NDY4NTAwNDc4MTYiCi0tLS0t
LT1MT0dJUE9MX01JRl8xNTQ2ODUwMDQ3ODE2DQpDb250ZW50LVR5cGU6IHRleHQv
WE1MOw0KbmFtZT0iUERBX1A5MDAxMjZfMDA1XzIwMTkwMTA3LjA5MzIwMF8wMDAw
MV9JTklULnhtbCI7IGZpbGVuYW1lPSJQREFfUDkwMDEyNl8wMDVfMjAxOTAxMDcu
MDkzMjAwXzAwMDAxX0lOSVQueG1sIg0KQ29udGVudC1UcmFuc2Zlci1FbmNvZGlu
ZzogYmFzZTY0DQoNClBEOTRiV3dnZG1WeWMybHZiajBpTVM0d0lpQmxibU52Wkds
dVp6MGlWVlJHTFRnaVB6NDhUVWxHVmtGUFNXNW1iMGx1YVhScFlXeGwNClBnbzhT
VzVtYjNNK0NqeFdaWEp6YVc5dVBqSXVPVHd2Vm1WeWMybHZiajRLUEVodmNtOWtZ
WFJsUGpJd01Ua3RNREV0TURkVU1EazYNCk16UTZNRGM4TDBodmNtOWtZWFJsUGdv
OFUyRnBjMmxsU0c5eWIyUmhkR1UrTWpBeE9TMHdNUzB3TjFRd09Ub3pNam93TUR3...
Second command I have to use is :
openssl smime -encrypt -in signedFile.step2 -out encryptedFile.P7M -outform DER -binary anotherpubliccertificate.pub.pem
This command takes 2 files, the file signed by the previous command and a public certificate, different than the one used in the previous command.
This returns a binary file, an encrypted file generated from step 2.
Any of the examples I found over the internet helped me to get a file looking like these previous ones, not even close.
I hope someone can help
Edit
A few examples of what I tried, or refered to, until now
sign file with bouncy castle in java -> This returned a signed file that do not correspond to the signed file generated with openssl
AES encrypt/decrypt with Bouncy Castle provider -> Again, this isn't working, the result doesn't correspond to the encrypted file I generate with openssl
https://studylibfr.com/doc/3898805/cryptographie-avec-bouncy-castle---zenk -> Have been following the whole tutorial, not getting the expected result
X509 RSA bouncy castle sign and verify plain text in Java -> Signed file not corresponding too
https://github.com/bcgit/bc-java/blob/master/mail/src/main/java/org/bouncycastle/mail/smime/examples/CreateSignedMultipartMail.java -> This class generate something that looks alike to what I'm trying to get, but I couldn't test it's validity as I have to encrypt it and still can't make encrytion work
https://github.com/bcgit/bc-java/blob/master/mail/src/main/java/org/bouncycastle/mail/smime/examples/ReadSignedMail.java -> Same as previous class
https://github.com/bcgit/bc-java/blob/master/mail/src/main/java/org/bouncycastle/mail/smime/examples/SendSignedAndEncryptedMail.java -> This method of encryption isn't returning the same result as openssl, so it isn't working
For sure, I've tried to keep working on these samples classes from bouncycastle, but without success.
Any help would be appreciated
Edit 2
The answer of the following question Sign and encrypt a file using S/MIME returns a Base64 encoded file that might correspond to what I generate with openssl. But the problem is that my entry file is about 25kb and the signed file generated is only 3kb, I don't understand why, I noticed that at this line :
CMSTypedData content = new CMSProcessableByteArray(buffer);
CMSSignedData signedData = signGen.generate(content, false);
byte[] signeddata = signedData.getEncoded();
the getEncoded() method returns me a byte array much smaller than the buffer I send to the CMSSignedData.
Does anyone knows the reason ?
For the signing, you were fairly close with org.bouncycastle.mail.smime.examples.CreateSignedMultipartMail except that
it does multipart data, which openssl smime doesn't do; start from CreateSignedMail instead
it does multipart signing, aka clear-signing, which openssl smime also defaults to, but -nodetach changes this to embedded aka encapsulated
it includes a full cert chain, but a self-generated one of length only 2 whereas nearly all 'real' certs are longer, whereas openssl by default includes only the signer cert
by default it uses some signedattributes different than openssl
For the encryption (or more exactly enveloping) openssl smime -outform der in spite of the name doesn't do SMIME at all, it does CMS (originally and still also known as PKCS7). Bouncy uses the full OO goodness of Java to put CMS and SMIME, which are very similar but not the same, into different classes that are related but not the same, so you need the CMS class(es).
Putting these together (plus a minimal test harness) I present to you:
// for test, (own) signing key+certchain and (peer) encryption cert in file
KeyStore ks = KeyStore.getInstance("JKS"); ks.load(new FileInputStream(args[0]),args[1].toCharArray());
PrivateKey signkey = (PrivateKey) ks.getKey(args[2], args[1].toCharArray());
Certificate[] signcert = ks.getCertificateChain(args[2]);
Certificate encrcert = ks.getCertificate(args[3]);
// and data in file
byte[] data = Files.readAllBytes(new File(args[4]).toPath());
// adapted from org.bouncycastle.mail.smime.examples.CreateSignedMail
// OpenSSL uses this rather silly capability list; may not be needed
SMIMECapabilityVector caps = new SMIMECapabilityVector();
caps.addCapability(SMIMECapability.aES256_CBC);
caps.addCapability(SMIMECapability.aES192_CBC);
caps.addCapability(SMIMECapability.aES128_CBC);
caps.addCapability(SMIMECapability.dES_EDE3_CBC);
caps.addCapability(SMIMECapability.rC2_CBC, 128);
caps.addCapability(SMIMECapability.rC2_CBC, 64);
caps.addCapability(SMIMECapability.dES_CBC);
caps.addCapability(SMIMECapability.rC2_CBC, 40);
ASN1EncodableVector signedAttrs = new ASN1EncodableVector();
signedAttrs.add(new SMIMECapabilitiesAttribute(caps));
// Bouncy default adds RFC6211 in addition to standard ctype, stime, mdgst
// and changing this is complicated; recipient _should_ ignore unneeded attr
SMIMESignedGenerator gen = new SMIMESignedGenerator();
gen.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder()//.setProvider("BC") not needed
.setSignedAttributeGenerator(new AttributeTable(signedAttrs))
.build("SHA1withRSA", signkey, (X509Certificate) signcert[0]) );
// change sigalg if key not RSA and/or want better hash
// OpenSSL by default includes only signer cert; recipient may want more
gen.addCertificates(new JcaCertStore (Arrays.asList (new Certificate[]{signcert[0]}) ));
MimeBodyPart msg = new MimeBodyPart();
msg.setText(new String(data, "ISO-8859-1")); // OpenSSL doesn't know charsets
ByteArrayOutputStream temp = new ByteArrayOutputStream();
gen.generateEncapsulated(msg).writeTo(temp); // OpenSSL -nodetach is encapsulated
// Bouncy uses BER here (unlike OpenSSL DER)
// and I don't see a simple way to change it but it _should_ not matter
byte[] signedblob = temp.toByteArray();
// now CMS (not SMIME) enveloping
CMSEnvelopedDataGenerator edgen = new CMSEnvelopedDataGenerator();
edgen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator((X509Certificate) encrcert));
CMSEnvelopedData edmsg = edgen.generate( new CMSProcessableByteArray(signedblob),
new JceCMSContentEncryptorBuilder(CMSAlgorithm.DES_EDE3_CBC).build() );
byte[] encrblob = edmsg.toASN1Structure().getEncoded(ASN1Encoding.DER); // OpenSSL is DER though std doesn't require it
// for test, write to a file
Files.write(new File(args[5]).toPath(), encrblob);
On, and 'does anyone know the reason'
CMSSignedData signedData = signGen.generate(content, false);
byte[] signeddata = signedData.getEncoded();
is smaller than the content? See the javadoc -- with encapsulate (the second parameter) set to false you told it to not include the content in the signature (more exactly SignedData), and it did as you demanded.

java.security.InvalidKeyException: invalid key format while generating public, private key from PEM file

I have gone through many similar threads but no luck!!
I want to generate public and private keys using a PEM file. Following is the code I am using for the same:
String pemFileNme = "C:\\Users\\amitmm\\Desktop\\clean\\key.pem";
File pubKeyFile = new File(pemFileNme);
File privKeyFile = new File(pemFileNme);
// read public key DER file
DataInputStream dis = new DataInputStream(new
FileInputStream(pubKeyFile));
byte[] pubKeyBytes = new byte[(int)pubKeyFile.length()];
dis.readFully(pubKeyBytes);
dis.close();
// read private key DER file
dis = new DataInputStream(new FileInputStream(privKeyFile));
byte[] privKeyBytes = new byte[(int)privKeyFile.length()];
dis.read(privKeyBytes);
dis.close();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
// decode public key
X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(pubKeyBytes);
RSAPublicKey pubKey = (RSAPublicKey)
keyFactory.generatePublic(pubSpec);
// decode private key
PKCS8EncodedKeySpec privSpec = new
PKCS8EncodedKeySpec(privKeyBytes);
RSAPrivateKey privKey = (RSAPrivateKey)
keyFactory.generatePrivate(privSpec);
Exception:
Exception in thread "main" java.security.spec.InvalidKeySpecException:
java.security.InvalidKeyException: invalid key format
at
sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:205)
at java.security.KeyFactory.generatePublic(KeyFactory.java:334)
at main.java.me.txedo.security.Main2.f1(Main2.java:47)
at main.java.me.txedo.security.Main2.main(Main2.java:20)
Caused by: java.security.InvalidKeyException: invalid key format
at sun.security.x509.X509Key.decode(X509Key.java:387)
PEM File Content:
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAwnEEodFEf86+Ae+wYyI//u1kekIWnA3RfzbAwWD77uG7D9Ci
9vVNbPO4XT2hKL03/q7d7KTgrA1sjBltfaOzVfA56x1S/0cYVk4xI440dpLo0F+m
RIqRw5fh8IuUlUIr3I4A7ESkDQQsZbDpdgCiNbrlADqLotcZyB4rU4uURW8QUI/W
eqsD6TOQs4bI+3o3xAKkky2kXujSaaa3tDxgUPTmSQ0Buk7Hx/IVzwyV7qjWiR4U
C46rHpnWxfF0DWuJUOYgJmBQ8xFOQwt4Ec/u+0m8top8cqQF+gpBn9iLXpbtahA3
pqyvLuNXRH9yn8mlEneBrjjl6U0H3W/AV7/dGwIBAwKCAQEAgaCtwTYtqonUAUp1
l2wqqfOYUYFkaAk2VM8rK5X9SevSCosXT04znffQPikWGyjP/x8+ncNAcrOdsrue
U8J3jqAmnL43VNoQOYl2F7Qi+bdF4D/ELbG2gmVBSwe4Y4FykwlV8thtXgLIQ8tG
TqsWznyYqtGybI9mhWlyN7Ji2POMDZP5Lwx7M01pMezwpnsZSmPVL9TgVrtWv4xt
C0vPyuy9THlFWtkOdHItNK+vOTcpuHn29rFUJI/D3R+SQjcdqj3aaqljOtdeBxgd
yDl2/Z4rUyetgzcZMfNTt/NRT0hOJ6R6/2S7gFCTtxMHBh3vVCH+pLLnQyJvcPQu
AsORSwKBgQDhOPr1x/8BioqaasoXvO9NsGktCgPDjbC4d3jR8n6lCa42X/eIahaD
xi1VGWyQhdO7aMXiDmzOtox7xHcMRh+a5ySIs9gTsHkMB2hqwIUNg25INRkQ3Vr3
eWnoTBGsfJqC1TEME3ocKwmyz57ZAe4yyR/ZRdDX5DUt9qCCFeA8uQKBgQDdAzbq
7BlJkbTYfdlIRNJEJAO3wWqQTx8X0ttCMMwDluOT9l+RR/KuUxl85ph+kwJci6E/
ixfeMTW1NcsMY/lB6mTP0oooalU1MP7gpPSu+24zhLXnUHZotbNbv9nk6w/1WWhz
FBt5w2DG4kQPFK6LSySqcVuzIGQyvWD5PbpGcwKBgQCWJfyj2qoBBwcRnIa6ffTe
dZtzXAKCXnXQT6XhTFRuBnQkP/pa8WRX2XOOEPMLA+J88IPsCZ3fJF2n2E9dhBUR
722wd+VidaYIBPBHKwNeV57azhC16OdPpkaa3WEdqGcB43YIDPwSx1vMimnmAUl3
ML/mLos6mCNz+cBWuUAoewKBgQCTV3nx8ruGYSM6/pDa2IwtbVfP1kcK32oP4eeB
dd1Xue0NTupg2qHJjLuombr/DKw9smt/sg/pdiPOI9yy7VDWnEM1NwbFnDjOIKnr
GKMfUkl3rc6aNaRFzneSf+aYnLVOO5r3Yrz715XZ7C1fYx8Hh23G9j0iFZgh05X7
fnwu9wKBgHyC0X26KZQ0ukan5jDSiz4dapUp2d3F+vnRzZa2AOsmo995gsXLdfsJ
n0o4Z3LsQJUDRI3tQ4dXe/5jS4oFrOdxALOAw6YmvEv/3oHwsCYPDhqLNfIJ9I6m
Dt3yG61pUJiCArhPaYG17NQoCxF6Xi6GUajRsECbr8DdyGMAu5eE
-----END RSA PRIVATE KEY-----
I have tried removing file header and footer manually. I tried code from bouncycastle, no luck, same error.
Python code which works with this file:
def t2e_enc(plaintext, pk_pem_file = './2017-12-04T062008Z.pem'):
'''
Function for encryption of Track2 credit card data.
This function uses private key to derivate public part used for encryption
'''
with open(pk_pem_file, 'rb') as pk:
private_key = serialization.load_pem_private_key(pk.read(),
password=None, backend=default_backend())
public_key = serialization.load_pem_public_key(
private_key.public_key().public_bytes(
serialization.Encoding.PEM,
serialization.PublicFormat.SubjectPublicKeyInfo),
backend=default_backend()
)
ciphertext = public_key.encrypt(
plaintext,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA1()),
algorithm=hashes.SHA1(),
label=None
)
)
b64ciphertext=base64.b64encode(ciphertext)
return b64ciphertext
I am doing this for the first time, so bear with me if there is any silly mistake.
Partial dupe Load RSA public key from file
So, you 'wrote' (I assume, copied) code that clearly says you need two files, in DER form, containing PKCS8 and 'X509' encodings. (What Java calls X.509 here is really the SubjectPublicKeyInfo component of X.509.) You give it one file, in PEM form not DER, containing a PKCS1 encoding not PKCS8 or X509 -- and you're surprised it doesn't work? Python works because it calls OpenSSL, and OpenSSL supports over a dozen encodings and formats for privatekeys, including this one; Java supports only one (outside of keystores) which isn't this one. (Bare) publickeys are a little better; internally libcrypto supports multiple forms, but in practice only two of them are used, and one of them matches Java -- although many publickeys are distributed, stored, and used in the form of X.509 certificates, which provides several more forms to worry about.
There are approximately 7 solutions to your situation:
the simplest is to use OpenSSL commandline to convert your one file to the two files Java wants:
# (corrected! pkey is inconsistent!)
openssl pkcs8 -topk8 -nocrypt -in input.pem -outform der -out private.der
openssl pkey -in input.pem -pubout -outform der -out public.der
# or for very old versions (should not be needed now)
openssl rsa -in input.pem -pubout -outform der -out public.der
Those files can now be read by the code you posted (except with the filenames separated). Note this conversion doesn't have to be done on the same system; if necessary you can do it elsewhere and copy the files, if you use a method that works for binary files i.e. NOT cut&paste.
if you really want only one file, but it can be converted, create the private.der file as above and read it with only the privatekey-related parts of your code, then do:
RSAPrivateCrtKey priv2 = (RSAPrivateCrtKey)privKey;
PublicKey pubkey = keyFactory.generatePublic(new RSAPublicKeySpec(priv2.getModulus(), priv2.getPublicExponent()));
you could convert the files to PKCS8 and 'X509' PEM by omitting -outform der from the above conversions, then read those files and manually 'de-PEM' by removing the header and trailer lines and converting the base64 to binary (removing or skipping the linebreaks); this results in binary PKCS8 and X509 encodings you can run through your existing code. This is as much work on the openssl side and more work on the Java side so there is no apparent advantage, except that PEM files are valid text and can be cut&pasted if necessary.
combining these, you could convert to PKCS8 PEM only, read that per bullet 3 (de-PEM then the privatekey parts of your code), then extract publickey from privatekey per bullet 2
one way to use the format you have (unconverted) in plain Java is to de-PEM per bullet 3 giving you a PKCS1 encoding, then manually construct the PKCS8 encoding, then proceed as before to run the PKCS8 through the KeyFactory and extract publickey per bullet 2. See my answer at Java: Convert DKIM private key from RSA to DER for JavaMail for a really ugly way to do this (including one de-PEM method). There is a better way if you use BouncyCastle (which has a class for this ASN.1 type), but if you use BouncyCastle it's better not to use this method at all, see below.
another way to use the unconverted format in plain Java is to de-PEM per bullet 3, then parse the ASN.1 structure of PKCS1 and construct an RSAPrivateCrtKeySpec which you can run through your KeyFactory instead of a PKCS8 encoding then extract publickey per bullet 2. This is even more complicated, although I think I have seen it somewhere; will add if I find it. Again BouncyCastle can improve this method, but doesn't need to, see below.
finally, if you have BouncyCastle it's dead easy. You don't say what you tried with 'no luck', but the following BouncyCastle code is all you need and does work:
try( Reader r = new FileReader(filename) ){
KeyPair pair = new JcaPEMKeyConverter().getKeyPair((PEMKeyPair)new PEMParser(r).readObject());
}
Note this gives you a KeyPair with both privatekey and publickey objects from the one file.

Convert certificate string to byte array

I got a string represents PEM certificate:
-----BEGIN CERTIFICATE-----
MIICUTCCAfugAwIBAgIBADANBgkqhkiG9w0BAQQFADBXMQswCQYDVQQGEwJDTjEL
MAkGA1UECBMCUE4xCzAJBgNVBAcTAkNOMQswCQYDVQQKEwJPTjELMAkGA1UECxMC
VU4xFDASBgNVBAMTC0hlcm9uZyBZYW5nMB4XDTA1MDcxNTIxMTk0N1oXDTA1MDgx
NDIxMTk0N1owVzELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAlBOMQswCQYDVQQHEwJD
TjELMAkGA1UEChMCT04xCzAJBgNVBAsTAlVOMRQwEgYDVQQDEwtIZXJvbmcgWWFu
ZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCp5hnG7ogBhtlynpOS21cBewKE/B7j
V14qeyslnr26xZUsSVko36ZnhiaO/zbMOoRcKK9vEcgMtcLFuQTWDl3RAgMBAAGj
gbEwga4wHQYDVR0OBBYEFFXI70krXeQDxZgbaCQoR4jUDncEMH8GA1UdIwR4MHaA
FFXI70krXeQDxZgbaCQoR4jUDncEoVukWTBXMQswCQYDVQQGEwJDTjELMAkGA1UE
CBMCUE4xCzAJBgNVBAcTAkNOMQswCQYDVQQKEwJPTjELMAkGA1UECxMCVU4xFDAS
BgNVBAMTC0hlcm9uZyBZYW5nggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEE
BQADQQA/ugzBrjjK9jcWnDVfGHlk3icNRq0oV7Ri32z/+HQX67aRfgZu7KWdI+Ju
Wm7DCfrPNGVwFWUQOmsPue9rZBgO
-----END CERTIFICATE-----
I assigned the above string to String variable String myCertStr.
What is the proper way to convert myCertStr to DER encoded byte[]?
(I am using Java 7, and I am not interested to use 3rd party library for this, I am seeking for a JDK7 way of doing it.)
IMPORTANT
As #dave_thompson_085 has pointed out in the comments, SunJCE CertificateFactory is indeed capable of parsing PEM files.
So you can just use that to get the Certificate object
as detailed at How to load public certificate from pem file..? (which is an earlier answer by #dave on the same topic, so please upvote it, instead of this one, if you find this useful !), and then access its encoded (DER) form.
However, if your PEM file is a raw "RSA PUBLIC KEY" (like the one that was attached to this question), or some other entity which SunJCE implementation can not parse directly, you can still parse and decode it manually, as detailed below.
Technically what you have here is not a certificate, but just a public key.
You can decode it to DER bytes as simple as that:
byte[] derBytes = Base64.getDecoder().decode(
pemText.replaceAll("-----(BEGIN|END) RSA PUBLIC KEY-----", "").replaceAll("\n", "")
);
Note, that what you will get will be a raw RSA (PKCS#1) key:
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER -- e
}
You can use the same technique to decode X.509 certificates or private keys.
E.g. the code to decode the X.509 certificate:
byte[] certificateBytes = Base64.getDecoder().decode(
pemText.replaceAll("-----(BEGIN|END) CERTIFICATE-----", "").replaceAll("\n", "").getBytes("UTF-8")
);
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate)(certificateFactory.generateCertificate(
new ByteArrayInputStream(certificateBytes)
)
);
UPDATE
The code above uses Java 8 Base64 decoder.
As question has been updated asking for a Java 7 solution,
here is a link to an excellent thread, discussing various options available: Base64 Java encode and decode a string.
E.g. java.xml.bind method described there does not require any extra libraries on Java 7 (which seems to match what OP wants)

Creating PKCS7 signature from etoken

I am trying to generate a digital signature using an etoken.
I am using BouncyCastle library and the signature is being generated.
The problem is that I want it in pkcs7 format which also contains tags like '-----BEGIN PKCS7-----' and ends with '-----END PKCS7-----'.But the signature that I generate doesn't contain these tags.
I am not sure where I'm going wrong.
Here is my code..
CMSProcessable content = new CMSProcessableByteArray(contentbytes);
CMSSignedData signedData = signGen.generate(content, securityProviderName);
byte[] signeddata = signedData.getEncoded();
BASE64Encoder encoder = new BASE64Encoder();
digitalSignature = encoder.encode(signeddata);
Now I need the data to be in pkcs7 format.Is there anything wrong with the code? Or do I need to add anything to this? Please help.. Thank you.
PKCS#7, usually called CMS (Cryptographic Message Syntax) is a container format that is specified using ASN.1 and encoded using BER/DER (Basic/Distinguished Encoding Rules). BER/DER are methods of performing binary encoding.
What you are talking about is PEM format, which was specified for Privacy Enhanced Mail. It is sometimes also called an ASCII armor as it makes it possible for binary data to be send over text interfaces. It consists of a header and footer that also identify the data, as well as the encoding to base 64 of the DER encoded blob.
What you can do is to implement the header and footer generation yourself, or you can take a look at the PemWriter class within the Bouncy Castle for Java functionality. The specifications of the format can be found here.

Categories

Resources