OpenSSL always fails verifying signature generated from Bouncy Castle in Java - java

I'am trying to generate signatures from files, in order to verify them by clients using OpenSSL, so to implement the corresponding openSSL command in Java using Bouncy Castle :
openssl.exe dgst -sha256 -sign privateKey.pem -out \data.txt.sig \data.txt
using bouncy castle 1.57 and java, we get bytes array signature from file, which i could verify it in the code. Private, public and certificate are generated from openSSL.
so to generate certificates :
read the private key from the pem private key file:
PEMParser pemParser = new PEMParser(new FileReader(PRIVATE_FILE_PATH));
PKCS8EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = (PKCS8EncryptedPrivateKeyInfo) pemParser.readObject();
JceOpenSSLPKCS8DecryptorProviderBuilder jce = new JceOpenSSLPKCS8DecryptorProviderBuilder();
jce.setProvider("BC");
InputDecryptorProvider decProv = jce.build(password.toCharArray());
PrivateKeyInfo info = encryptedPrivateKeyInfo.decryptPrivateKeyInfo(decProv);
JcaPEMKeyConverter pemKeyConverter = new JcaPEMKeyConverter();
PrivateKey pk = pemKeyConverter.getPrivateKey(info);
and generate the RSA SHA 256 signature and write the result in the signature file:
byte[] data = Files.readAllBytes(Paths.get(txtFileToSignPath));
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initSign(pk);
sig.update(data);
byte[] signature = sig.sign();
FileOutputStream dfis = new FileOutputStream(SignaturefilePath);
dfis.write(bytesToWrite);
dfis.close();
programmatically i could verify signatures generated from the code above as well as from open SSL:
Signature verifySignature = Signature.getInstance("SHA256withRSA");
byte[] signatureBytes =
Files.readAllBytes(Paths.get(SignaturefilePath);
verifySignature.initVerify(getPublicKeyFromCertFile(CERT_PEM));
verifySignature.update(data);
verifySignature.verify(signatureBytes);
on the other hand openSSL is getting always "verification failure" by verifiying signatures generated from code.
is OpenSSL able to verify array of bytes signatures directly, or am I missing anything ?

code above works fine, was verifying the signature against another file. Question maitained for learning purpose.

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.

How to decrypt a pkcs8 encrypted private key using bouncy castle?

i am trying to decrypt a pkcs8 encrypted private key using bouncy castle library. I parsed the file containing the private key using PEMParser provided by bouncy castle. I got PKCS8EncryptedPrivateKeyInfo object. I am unable to get the PrivateKeyInfo object from this.
I am getting the following exception while trying to decrypt.
org.bouncycastle.pkcs.PKCSException: unable to read encrypted data: 1.2.840.113549.1.5.13 not available: No such provider: 1.2.840.113549.1.5.13
here is the code which I used
PEMParser parser = new PEMParser(br);
PKCS8EncryptedPrivateKeyInfo pair = (PKCS8EncryptedPrivateKeyInfo)parser.readObject();
JceOpenSSLPKCS8DecryptorProviderBuilder jce = new JceOpenSSLPKCS8DecryptorProviderBuilder();
jce.setProvider("1.2.840.113549.1.5.13");
InputDecryptorProvider decProv = jce.build(password.toCharArray());
PrivateKeyInfo info = pair.decryptPrivateKeyInfo(decProv);
Have you tried with jce.setProvider("BC"); instead of jce.setProvider("1.2.840.113549.1.5.13");
Edit to add solution provided by #PeterDettman :
In addition to use jce.setProvider("BC"); also install the BC provider bouncycastle.org/wiki/display/JA1/Provider+Installation
Security.addProvider(new BouncyCastleProvider());
thats whats missing

JAVA: How to save a private key in a pem file with password protection

I am trying to save a private key in a pem file, protected with a password. The problem is, the pem file is created and I
can even open it with openssl but, no password is asked!
Here is the code:
KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA");
keygen.initialize(2048);
KeyPair keypair = keygen.generateKeyPair();
PrivateKey privKey = keypair.getPrivate();
PKCS8Generator encryptorBuilder = new PKCS8Generator(privKey);
encryptorBuilder.setPassword("testing".toCharArray());
PEMWriter writer = new PEMWriter(new FileWriter(new File("pk.pem")));
PemObject obj = encryptorBuilder.generate();
writer.writeObject(obj);
writer.flush();
writer.close();
After it executes, I try to open the pk.pem file
openssl rsa -in pk.pem -check
and it gives:
RSA key ok
writing RSA key
-----BEGIN RSA PRIVATE KEY-----
(... some key appears here ...)
-----END RSA PRIVATE KEY-----
It was suppose to ask for the password before giving access to the private key!
Can some one please help me?
Well you should read the BouncyCastle documentation carefully. It states for the constructor you use:
// Constructor for an unencrypted private key PEM object.
PKCS8Generator(java.security.PrivateKey key)
// Constructor for an encrypted private key PEM object.
PKCS8Generator(java.security.PrivateKey key, java.lang.String algorithm, java.lang.String provider)
Hence you are using the constructor for creating an creates an unencrypted PKCS8Generator instance. The password you set as no effect.
Use one of the other constructors instead that create an encrypting instance according to the documentation.
Note: The code in the question requires an outdated version of BouncyCastle (1.4x?), because the current version (1.5x) has different constructors, incompatible with those presented in this answer.
For newer versions use:
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
JcaPEMWriter writer = new JcaPEMWriter(new PrintWriter(System.out));
writer.writeObject(sk);
writer.close();
possibly replacing the PrintWriter with any other Writer of course.

Categories

Resources