PEMParser hanging with no exception thrown
I'm no expert, but what you are showing does not look like a key in PEM format.
Compare with: http://www.herongyang.com/Cryptography/Certificate-Format-PEM-on-Certificates.html
This may help: https://sycure.wordpress.com/2008/05/15/tips-using-openssl-to-extract-private-key-pem-file-from-pfx-personal-information-exchange/
Reading encrypted private key in PKCS#8 format through bouncycastle, Java failing in docker container
Like @Bragolgirith suspected, BouncyCastle seems to have problems with OpenJ9. I guess it is not a Docker issue, because I can reproduce it on GitHub Actions, too. It is also not limited to BouncyCastle 1.64 or 1.70, it happens in both versions. It also happens on OpenJ9 JDK 11, 14, 17 on Windows, MacOS and Linux, but for the same matrix of Java and OS versions it works on Adopt-Hotspot and Zulu.
Here is an example Maven project and a failed matrix build. So if you select another JVM type, you should be fine. I know that @Bragolgirith already suggested that, but I wanted to make the problem reproducible for everyone and also provide an MCVE, in case someone wants to open a BC or OpenJ9 issue.
P.S.: It is also not a character set issue with the InputStreamReader
. This build fails exactly the same as before after I changed the constructor call.
Update: I have created BC-Java issue #1099. Let's see what the maintainers can say about this.
Update 2: The solution to your problem is to explicitly set the security provider to BC for your input decryptor provider. Thanks to David Hook for his helpful comment in #1099.
BouncyCastleProvider securityProvider = new BouncyCastleProvider();
Security.addProvider(securityProvider);
// (...)
InputDecryptorProvider pkcs8Prov = new JceOpenSSLPKCS8DecryptorProviderBuilder()
// Explicitly setting security provider helps to avoid ambiguities
// which otherwise can cause problems, e.g. on OpenJ9 JVMs
.setProvider(securityProvider)
.build(passphrase.toCharArray());
See this commit and the corresponding build, now passing on all platforms, Java versions and JVM types (including OpenJ9).
Because @Bragolgirith mentioned it in his answer: If you want to avoid the explicit new JceOpenSSLPKCS8DecryptorProviderBuilder().setProvider(securityProvider)
, the call Security.insertProviderAt(securityProvider, 1)
instead of simply Security.addProvider(securityProvider)
would in this case also solve the problem. But this holds true only as long as no other part of your code or any third-party library sets another provider to position 1 afterwards, as explained in the Javadoc. So maybe it is not a good idea to rely on that.
Bouncy castle create a certificate from openssl csr in java
The answer you linked tells you what the code returns: "a valid PEM-encoded signedData object containing a signed Certificate chain (of the type that can be imported by keytool)". Actually it's not quite valid: (1) it doesn't put any linebreaks in the base64 as PEM officially requires -- which you fixed, although you did it at 63 not 64 as standard; (2) it uses BEGIN/END PKCS #7 SIGNED DATA
whereas the standard is BEGIN/END PKCS7
-- or in most cases BEGIN/END CMS
because CMS is basically a superset of PKCS7. keytool ignores both of these flaws when reading a 'CA reply' (i.e. -importcert
to an existing privateKey
entry), but other software doesn't -- like BouncyCastle and OpenSSL. You already added line breaks; if you change the BEGIN/END labels to PKCS7 and put the result in a file you can read it with among other things
openssl pkcs7 -in $file -print_certs # add -text for expanded details
However, calling this 'the type that can be imported by keytool' implies it is the only type which is false. keytool can read PKCS7 containing certs -- either PEM or DER -- but it can also read 'bare' X.509 certificates in either PEM or DER, and that is usually more convenient. To do that, replace everything from CMSSignedDataGenerator
onward with something like for DER:
Files.write(Paths.get("outputfile"), certencoded)
or for PEM since you have Bouncy:
try( PemWriter w = new PemWriter(new FileWriter("outputfile")) ){
w.writeObject(new PemObject("CERTIFICATE",certencoded));
}
or if you prefer to do it yourself:
try( Writer w = new FileWriter("outputfile") ){
w.write("-----BEGIN CERTIFICATE-----\r\n"
+ Base64.getMimeEncoder().encodeToString(certencoded)
// MimeEncoder does linebreaks at 76 by default which
// is close enough and less work than doing it by hand
+ "\r\n-----END CERTIFICATE-----\r\n");
}
Note: you may not need the \r
-- PEM doesn't actually specify CRLF vs LF linebreaks -- but MimeEncoder uses them on the interior breaks and I like to be consistent.
This -- just the certificate -- is what your openssl x509 -req -CA*
alternative produces, and that's why it's much smaller -- one cert is smaller than three certs plus some overhead.
Several more points:
PEMParser can handle CSR just fine; you don't need the PemReader+PemObject detour
using the entire SubjectPublicKeyInfo for the SubjectKeyIdentifier extension is wrong. OTOH nothing uses the SKI extension in a leaf cert anyway, so simplest to just delete this rather than fix it.
in
certgen.build()
if you useJcaContentSignerBuilder
instead of theBcRSA
variant, you can passcakey
directly without going throughPrivateKeyFactory
and the signature scheme directly without going through twoAlgorithmIdentifierFinder
s -- as the/*cms-sd*/generator.addSignerInfoGenerator
later in the code already does.speaking of the signature scheme, don't use SHA1 for cert signatures (or most others). It was already deprecated in 2013 when that answer was written, and in 2017 was publicly broken for collision (see https://shattered.io) after which many systems either reject it entirely or nag mercilessly for such use, and some related ones like git. In fact many authorities and checklisters now prohibit it for any use, even though some (like HMAC and PBKDF) aren't actually broken. Your
openssl x509 -req
alternative used SHA256 which is fine*.and lastly, issuing a cert is NOT 'sign[ing] {the|a} CSR'. You can just look at the cert contents and see it's not at all the same as the CSR or even the CSR body. You create (or generate as used in the Bouncy names) a cert and sign the cert -- in response to a CSR.
* at least for now; if and when quantum cryptanalysis works a lot of currently used schemes, including probably SHA256-RSA signature, will be in trouble and will have to be replaced by 'post-quantum' schemes which people are now working to develop. But if this happens, you'll see it on every news channel and site in existence.
Loading previously stored Keys fails in BouncyCastle with Java
Bouncy castle is writting PEM file in PKCS8 format, not PKCS1, and it never let you know that it didn't do what you were expecting.
I don't know how to write PEM file in PKCS1 format. I would love to know, because I periodically spend an afternoon trying to do so before convincing myself that PCKS8 is just so better and superior, and no one need PKCS1 really, and just nobody talk me about PKCS1 again.
So if you use the openssl command:
openssl pkcs8 -topk8 -nocrypt -in private.pem
in place of the one I suppose you tried (openssl rsa -in private.pem -check
), you will get the same content as what bouncy castle wrote. Here again, we have a command which is just a little to smart about what it does, and don't tell you that "yeah, you tell me to read pcks1 RSA file, but look, headers are always lying, and I DO see that it's in fact pkcs8, so I will just read that as pkcs8 and everybody is happy right?"
And so, your code should be adapted to read PCKS8 to something like (I didn't do Java since 10y, so perhaps there's some little things to adapt):
public static PemObject createPrivateObject(KeyPair key) throws Exception {
return new PemObject("PRIVATE KEY", key.getPrivate().getEncoded());
}
[...]
public static KeyPair readKeyPair(String path) {
File privateKeyFile = new File(path);
try (PEMParser pemParser = new PEMParser(new FileReader(privateKeyFile))){
PrivateKeyInfo privkeyInfo = (PrivateKeyInfo)pemParser.readObject();
PKCS8EncodedKeySpec keyspec = new PKCS8EncodedKeySpec(privkeyInfo.getEncoded);
RSAPrivateKey privKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(keyspec)
pemParser.close();
return kp;
} catch { ....
Related Topics
N-Gram Generation from a Sentence
Checked VS Unchecked Exceptions in Java
Java.Sql.Sqlexception: No Suitable Driver Found for Jdbc:Mysql://Localhost:3306/Dbname
Gwt: Timer and Scheduler Classes
Java Inheritance - Calling Superclass Method
How to Double Buffer in Java for a Game
Java - Scroll to Specific Text Inside Jtextarea
Blank PDF Even with the Simplest Jasperreport Jrxml
Create Bar Chart in Excel with Apache Poi
How to Create a Stream of Regex Matches
Logarithmic Axis Labels/Ticks Customization
Using Javafx.Beans Properties in Model Classes
Java Array with More Than 4Gb Elements