Skip to content

Commit

Permalink
Add support for password-based encryption scheme 2 params (PBES2) (#1…
Browse files Browse the repository at this point in the history
…3539)

Motivation:

Add support for password-based encryption scheme 2 params (PBES2)

Modification:

Describe the modifications you've done.

Result:

Fixes #13536

Co-authored-by: Norman Maurer <norman_maurer@apple.com>
Co-authored-by: Chris Vest <christianvest_hansen@apple.com>
  • Loading branch information
3 people committed Aug 22, 2023
1 parent 260ec54 commit 0c7cf3f
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 5 deletions.
41 changes: 36 additions & 5 deletions handler/src/main/java/io/netty5/handler/ssl/SslContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@
import io.netty5.util.DefaultAttributeMap;
import io.netty5.util.internal.EmptyArrays;



import javax.crypto.Cipher;
import javax.crypto.EncryptedPrivateKeyInfo;
import javax.crypto.NoSuchPaddingException;
Expand All @@ -48,6 +46,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyException;
Expand Down Expand Up @@ -103,6 +102,8 @@ public abstract class SslContext {

private final boolean startTls;
private final AttributeMap attributes = new DefaultAttributeMap();
private static final String OID_PKCS5_PBES2 = "1.2.840.113549.1.5.13";
private static final String PBES2 = "PBES2";

/**
* Returns the default server-side implementation provider currently in use.
Expand Down Expand Up @@ -1094,16 +1095,39 @@ protected static PKCS8EncodedKeySpec generateKeySpec(char[] password, byte[] key
}

EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(key);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName());
String pbeAlgorithm = getPBEAlgorithm(encryptedPrivateKeyInfo);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(pbeAlgorithm);
PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
SecretKey pbeKey = keyFactory.generateSecret(pbeKeySpec);

Cipher cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName());
Cipher cipher = Cipher.getInstance(pbeAlgorithm);
cipher.init(Cipher.DECRYPT_MODE, pbeKey, encryptedPrivateKeyInfo.getAlgParameters());

return encryptedPrivateKeyInfo.getKeySpec(cipher);
}

private static String getPBEAlgorithm(EncryptedPrivateKeyInfo encryptedPrivateKeyInfo) {
AlgorithmParameters parameters = encryptedPrivateKeyInfo.getAlgParameters();
String algName = encryptedPrivateKeyInfo.getAlgName();
// Java 8 ~ 16 returns OID_PKCS5_PBES2
// Java 17+ returns PBES2
if (parameters != null &&
(OID_PKCS5_PBES2.equals(algName) || PBES2.equals(algName))) {
/*
* This should be "PBEWith<prf>And<encryption>".
* Relying on the toString() implementation is potentially
* fragile but acceptable in this case since the JRE depends on
* the toString() implementation as well.
* In the future, if necessary, we can parse the value of
* parameters.getEncoded() but the associated complexity and
* unlikeliness of the JRE implementation changing means that
* Tomcat will use to toString() approach for now.
*/
return parameters.toString();
}
return encryptedPrivateKeyInfo.getAlgName();
}

/**
* Generates a new {@link KeyStore}.
*
Expand Down Expand Up @@ -1131,12 +1155,19 @@ protected static PrivateKey toPrivateKey(File keyFile, String keyPassword) throw
NoSuchPaddingException, InvalidKeySpecException,
InvalidAlgorithmParameterException,
KeyException, IOException {
return toPrivateKey(keyFile, keyPassword, true);
}

static PrivateKey toPrivateKey(File keyFile, String keyPassword, boolean tryBouncyCastle)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
InvalidAlgorithmParameterException,
KeyException, IOException {
if (keyFile == null) {
return null;
}

// try BC first, if this fail fallback to original key extraction process
if (BouncyCastlePemReader.isAvailable()) {
if (tryBouncyCastle && BouncyCastlePemReader.isAvailable()) {
PrivateKey pk = BouncyCastlePemReader.getPrivateKey(keyFile, keyPassword);
if (pk != null) {
return pk;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,13 @@ public void testPkcs8Des3EncryptedRsa() throws Exception {
assertNotNull(key);
}

@Test
public void testPkcs8Pbes2() throws Exception {
PrivateKey key = SslContext.toPrivateKey(new File(getClass().getResource("rsa_pbes2_enc_pkcs8.key")
.getFile()), "12345678", false);
assertNotNull(key);
}

@Test
public void testPkcs1UnencryptedRsaEmptyPassword() throws Exception {
assertThrows(IOException.class, new Executable() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,15 @@ openssl gendsa -out dsa_pkcs1_unencrypted.key dsaparam.pem
openssl gendsa -des3 -out dsa_pkcs1_des3_encrypted.key -passout pass:example dsaparam.pem
openssl gendsa -aes128 -out dsa_pkcs1_aes_encrypted.key -passout pass:example dsaparam.pem

# PBES2
openssl genrsa -out rsa_pbes2.key
openssl req -new -subj "/CN=NettyTest" -key rsa_pbes2.key -out rsa_pbes2.csr
openssl x509 -req -days 36500 -in rsa_pbes2.csr -signkey rsa_pbes2.key -out rsa_pbes2.crt
openssl pkcs8 -topk8 -inform PEM -in rsa_pbes2.key -outform pem -out rsa_pbes2_enc_pkcs8.key -v2 aes-256-cbc -passin pass:12345678 -passout pass:12345678

# Clean up intermediate files
rm intermediate.csr
rm mutual_auth_ca.key mutual_auth_invalid_client.key mutual_auth_client.key mutual_auth_server.key mutual_auth_invalid_intermediate_ca.key mutual_auth_intermediate_ca.key
rm mutual_auth_invalid_client.pem mutual_auth_client.pem mutual_auth_server.pem mutual_auth_client_cert_chain.pem mutual_auth_invalid_intermediate_ca.pem mutual_auth_intermediate_ca.pem mutual_auth_invalid_client_cert_chain.pem
rm dsaparam.pem
rm rsa_pbes2.crt rsa_pbes2.csr rsa_pbes2.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFHzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIJddddGJqvzACAggA
MB0GCWCGSAFlAwQBKgQQ0SIWTVvrPdriTZKRZuWb7QSCBNDoU6Y1qeABJPV726zv
gymMUAZdfxqBlbS865q3RlnHO2xm4082l7VwFK9QGVFIxURx4QU76qjhsoJfmGiv
pAvkZCln90iguY0ssnAzBFi8B2AgBcZrIG1OMQpR0L/hjveIvorg4/vb5rIgyIdn
+3u7I077yF3udPnt0jUYcfonzsOglwa3FVun2yqM7/gAzGkQu/CeOshSQaJ9EV/0
ZQmemVCJbmm7I2iEq6RUXgBW8hPjo1cwuQznE2jBx3SlhQoSrWTGdy9WXLqwxrHX
e2W6LqaXqFOrgmtPz5h3JzhUh0AGtNS0SsB6AsyKD548NP7NGRlmbUXo8iQXS/Z6
QmwIh1bt67VecHKka17ZYGBQt/2/zcQddRvlVeT3SbRgGCGQeDnXWhfJIMb6bFDe
vdr0L44zyW6/OwYQU1RNBFclrjtFIAVQEI8L/BVciowsJ7J6W2UsxR3hrLtPjO9o
zH7bp85TlSaSZW3T2HfAPu6isMECzVzZ+x8qnpuDoHjIOZID630Amw1v2/gfFZTA
mXn4gld1JKUBrvBfXN4cjTDC1eO1zvzEZB6VZw6ePhggWijySoLUO+E/mJpP5mNA
4OQSMxNQ+UcZH+MT093cOJeOleOlbc9weIeNhXgONX/pbnq1gV23tN3mZbGIJojY
GZoH32ft76x+DxrMZrjtjL7dOUL5QjjUlpJ6319aaLFf6Z/AIWXBOyHC1i9l+lKv
2hpZ/YS+eyExy/axwx0J4eH+7csDrz63F1A9hRrIxx2wKdYjsKWR21Hb/mst/U4z
1MGWeIX1hqAe7VYUiZBZldnOYdmNG/sRtcHMrW9zqkJUuW25YjuGm9gVbIVw/YOY
lOd/9puSiRzuJLX02p1o1PN17+5rzMkpE4bVpd6Pvbex+oMSUVvd+V845bCB0qCt
eA5TUBi2gbLvj7TqoA+C0zoXGtXD1Ea/7hnmwC5Gzl4m6YmvZRpxXA6Mzs62CYgt
KWJSiuuTfSop/2B8+nnkZQAGKxvXkpFLXyGXfP+Y22X00BB43GtMB2ZPzmliQ+TA
bFePkPBoXqytJR9vrbWJIIzcxaYWTwqN1vZJzIgdFjK2yOiqopGi0sG0zjn8xryv
ZqraVnTznb5xUGojezIbtWwMNIRrmNU9b1HMtpMsnuNNPQy/UhgDqgM6bQOh23Q8
7DQRqXGlNqJc22ne1E/gN5IxdbrgoE6jnnoAzOlFRD1XdhLBW3hb2DpzTFAveDKi
ti5UHucXrURILD1ee9CtyKcYQajr3XNp0tMJJQFCybnX+zOgH6HXsrfXFT8CghDt
aeo7TBfhVC4MadvggLNWmyjyGJd7KeDGuiXVDsWr9icesDCUgC/T2Lq58boT70BM
T14pABDOqSylJdL0qWV7m8yZ0fNAkrTB/+2qdi4B632NIQ+ZGRZy5WK09jRUtVm0
+EZMoX6pAjBQR4RwgbyNTJDIt/tXOopBAoEjRc8qWotlJozc3RkpGBHgT8POZT60
Jxu3QaE9rJq8kfrO/e5gq7zDy4AK/ck1+aVxfiwM1D7vPvFt0WwztdvEGz3Hjpkv
qtj8ePpDBrBo1ISJqVmTi3pvRz/kuKFYvoYdWq2Wluz1NGZdCETwNYIao1/hfP52
NydFNZTmdrun22ezf6yVGiCaEw==
-----END ENCRYPTED PRIVATE KEY-----

0 comments on commit 0c7cf3f

Please sign in to comment.