[#2718] Add private key decryption to JdkSslServerContext

Backported: d9cccccbb3

Motivation:

Currently it is not possible to load an encrypted private key when
creating a JDK based SSL server context.

Modifications:

- Added static method to JdkSslServerContext which handles key spec
  generation for (encrypted) private keys and make use of it.
- Added tests for creating a SSL server context based on a (encrypted)
  private key.

Result:

It is now possible to create a JDK based SSL server context with an
encrypted (password protected) private key.
This commit is contained in:
Trustin Lee 2014-08-04 10:43:27 -07:00
parent c44be8e468
commit 9412ace4ee
5 changed files with 164 additions and 3 deletions

View File

@ -19,13 +19,23 @@ package org.jboss.netty.handler.ssl;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBufferInputStream;
import javax.crypto.Cipher;
import javax.crypto.EncryptedPrivateKeyInfo;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSessionContext;
import java.io.File;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
@ -136,7 +146,9 @@ public final class JdkSslServerContext extends JdkSslContext {
ChannelBuffer encodedKeyBuf = PemReader.readPrivateKey(keyFile);
byte[] encodedKey = new byte[encodedKeyBuf.readableBytes()];
encodedKeyBuf.readBytes(encodedKey);
PKCS8EncodedKeySpec encodedKeySpec = new PKCS8EncodedKeySpec(encodedKey);
char[] keyPasswordChars = keyPassword.toCharArray();
PKCS8EncodedKeySpec encodedKeySpec = generateKeySpec(keyPasswordChars, encodedKey);
PrivateKey key;
try {
@ -150,11 +162,11 @@ public final class JdkSslServerContext extends JdkSslContext {
certChain.add(cf.generateCertificate(new ChannelBufferInputStream(buf)));
}
ks.setKeyEntry("key", key, keyPassword.toCharArray(), certChain.toArray(new Certificate[certChain.size()]));
ks.setKeyEntry("key", key, keyPasswordChars, certChain.toArray(new Certificate[certChain.size()]));
// Set up key manager factory to use our key store
KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
kmf.init(ks, keyPassword.toCharArray());
kmf.init(ks, keyPasswordChars);
// Initialize the SSLContext to work with our key managers.
ctx = SSLContext.getInstance(PROTOCOL);
@ -186,4 +198,39 @@ public final class JdkSslServerContext extends JdkSslContext {
public SSLContext context() {
return ctx;
}
/**
* Generates a key specification for an (encrypted) private key.
*
* @param password characters, if {@code null} or empty an unencrypted key is assumed
* @param key bytes of the DER encoded private key
*
* @return a key specification
*
* @throws IOException if parsing {@code key} fails
* @throws NoSuchAlgorithmException if the algorithm used to encrypt {@code key} is unkown
* @throws NoSuchPaddingException if the padding scheme specified in the decryption algorithm is unkown
* @throws InvalidKeySpecException if the decryption key based on {@code password} cannot be generated
* @throws InvalidKeyException if the decryption key based on {@code password} cannot be used to decrypt {@code
* key}
* @throws InvalidAlgorithmParameterException if decryption algorithm parameters are somehow faulty
*/
private static PKCS8EncodedKeySpec generateKeySpec(char[] password, byte[] key)
throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException,
InvalidKeyException, InvalidAlgorithmParameterException {
if (password == null || password.length == 0) {
return new PKCS8EncodedKeySpec(key);
}
EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(key);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName());
PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
SecretKey pbeKey = keyFactory.generateSecret(pbeKeySpec);
Cipher cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName());
cipher.init(Cipher.DECRYPT_MODE, pbeKey, encryptedPrivateKeyInfo.getAlgParameters());
return encryptedPrivateKeyInfo.getKeySpec(cipher);
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package org.jboss.netty.handler.ssl;
import org.junit.Test;
import javax.net.ssl.SSLException;
import java.io.File;
public class JdkSslServerContextTest {
@Test
public void testJdkSslServerWithEncryptedPrivateKey() throws SSLException {
File keyFile = new File(getClass().getResource("test_encrypted.pem").getFile());
File crtFile = new File(getClass().getResource("test.crt").getFile());
new JdkSslServerContext(crtFile, keyFile, "12345");
}
@Test
public void testJdkSslServerWithUnencryptedPrivateKey() throws SSLException {
File keyFile = new File(getClass().getResource("test_unencrypted.pem").getFile());
File crtFile = new File(getClass().getResource("test.crt").getFile());
new JdkSslServerContext(crtFile, keyFile, "");
new JdkSslServerContext(crtFile, keyFile, null);
}
}

View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIC/jCCAeagAwIBAgIIIMONxElm0AIwDQYJKoZIhvcNAQELBQAwPjE8MDoGA1UE
AwwzZThhYzAyZmEwZDY1YTg0MjE5MDE2MDQ1ZGI4YjA1YzQ4NWI0ZWNkZi5uZXR0
eS50ZXN0MCAXDTEzMDgwMjA3NTEzNloYDzk5OTkxMjMxMjM1OTU5WjA+MTwwOgYD
VQQDDDNlOGFjMDJmYTBkNjVhODQyMTkwMTYwNDVkYjhiMDVjNDg1YjRlY2RmLm5l
dHR5LnRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDb+HBO3C0U
RBKvDUgJHbhIlBye8X/cbNH3lDq3XOOFBz7L4XZKLDIXS+FeQqSAUMo2otmU+Vkj
0KorshMjbUXfE1KkTijTMJlaga2M2xVVt21fRIkJNWbIL0dWFLWyRq7OXdygyFkI
iW9b2/LYaePBgET22kbtHSCAEj+BlSf265+1rNxyAXBGGGccCKzEbcqASBKHOgVp
6pLqlQAfuSy6g/OzGzces3zXRrGu1N3pBIzAIwCW429n52ZlYfYR0nr+REKDnRrP
IIDsWASmEHhBezTD+v0qCJRyLz2usFgWY+7agUJE2yHHI2mTu2RAFngBilJXlMCt
VwT0xGuQxkbHAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAEv8N7Xm8qaY2FgrOc6P
a1GTgA+AOb3aU33TGwAR86f+nLf6BSPaohcQfOeJid7FkFuYInuXl+oqs+RqM/j8
R0E5BuGYY2wOKpL/PbFi1yf/Kyvft7KVh8e1IUUec/i1DdYTDB0lNWvXXxjfMKGL
ct3GMbEHKvLfHx42Iwz/+fva6LUrO4u2TDfv0ycHuR7UZEuC1DJ4xtFhbpq/QRAj
CyfNx3cDc7L2EtJWnCmivTFA9l8MF1ZPMDSVd4ecQ7B0xZIFQ5cSSFt7WGaJCsGM
zYkU4Fp4IykQcWxdlNX7wJZRwQ2TZJFFglpTiFZdeq6I6Ad9An1Encpz5W8UJ4tv
hmw=
-----END CERTIFICATE-----

View File

@ -0,0 +1,29 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIE9jAoBgoqhkiG9w0BDAEDMBoEFDBlaUwB8TQ9ImbApCmAyVRTTX+kAgIIAASC
BMhC8QFNyn0VbVp7I+R9Yvmr+Ksl0xZshGg3zaUN8/HRblNSS3gPiP673rmnhcU3
PfSNFR9hOrTqdtd5i6Qq4HznECs81KBlqRNB9ihgy++ByFkf6GTzdfBA6zJInhNx
qSWjUwpFtV4or1w/N23bTcpdGmjfdCSFBMQdbkIDgT7GaWxd3mCLxSbfVzF64tev
x+V22nA/TR0VWnG+aj7aVbReK6VpepiCX7ZmQ5KehXAeB0SDrgT89kcz2VIfDxvE
hkCymNTcJY/ETdPfTSiR+DSZvVJMgVmfk7j1toZZSnoMwl4IhlXmIPmDOUE465l3
sNWLygkNKymTmMI5FTT1hChAIdsmeVTfDmVzNPK4HQi5gfEnTCy0uxj9U3HCZWr1
Zlzmw7/430TRqNYSEJ/XkhFaV5V+6LfeZOyuwf2VJAs+CwNo+UYzEQqkW11JMqhA
i9fz8bCNoy4/dyWbE/wEK8UPGif1rzCpoodBYeWTt0QtHcIokE3ylXWyTTarz7jV
u9Rnbq4HAXYYEwPjLmWFQ6NeD/rx/t44oEAyekxS+ZPIHNTVXRLBH5Tl/LDkpK15
x0FoIZ0vrDiFbmtHCq/TeDyFtudSbmihnn0Of6PtXKZJpXgEADQBnak/P4IE39/d
1hWd3H635goC6OkqHv9IAAyLlCNZCOVqC5Wa8TvyZdaKi5A2mZfGrpxPrUQDlnqN
8d3xlysNCaRH1hSMw4hGHu0xxGJaK4DQtklxfZB7IMMw5MkQh6Rim5TOXfopmzmK
PISJge1atiHbVIBP6sr3Egik3h6v0j7xXVmwj3UUQRaSBznZ43ShlYieLnin9sh8
x/gLyvQrtJRvScN6skgrXFKVH3Jojxut9if64jjLo4C61UgNrvuka05treRTI+jT
hHB3GLy7hwSHnbsOvwvYbG3WgyePPq6jIM+LV4Vm3fPX6NPNI/jZMebROGwjTL0C
2403yvgeIpEOQyZpKsDBqAwgKB91Na53K05qGSbr8AgcZvgFflJdLzai+5Cg7hNg
YTEff0NKPeYnk4u3xQ8EqxI2jwdqfgzd0RcPcx60CHRBTULaKOU2sAYTSpwQmApj
+TnJNcQnWRAEcZ35b/b+oGlVH/BUmvjSdu2qvvU3g4GoHL7MuVGvzk0Cgo1Esktt
S6gO/pTQPaKGJ1ztxoHu2zzi7/URaus3sqI5qV9krWMSa35BMG21Eik/y9rou6LC
yT0EtMLOCxSrfM1I26XTU/7qPIEJlVZg0CJ39niZ7EEm1Hef0cmT8Aq9t5cRTyvR
BqbqBCJpcsgeIZUMH6RJ1zv616eJvY7wjd13Sl0Tbj9+nNS482D9PIlaXSD8UySh
mZ0bMPhCeyOsmRmz2qT1X+Zct8XtdXc/NPKBA6rnOtH8vJAHn7S120le5XIn5t9l
rDiO1Hozhb+0xcTk+SNc/vIORA6KrBoZrNpJpmyL3BzRp+/VLbR+/S3ikTDkYj7J
sktK2ap6vK7u50Jnrt9C/wynVACzGx1tlDVxiVerDmwjfQWL08qCXHlouEdjh9dD
L5XyVlT2FxEXXLRgKGHxFaSQw3Fzzug/o4SgizbNjKffJU5xQlC0aq3WX5+/l3Ic
LWTalgdli3edsR/9RGuu8EsZ11dmNh3csGs=
-----END ENCRYPTED PRIVATE KEY-----

View File

@ -0,0 +1,24 @@
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDb+HBO3C0URBKvDUgJHbhIlBye
8X/cbNH3lDq3XOOFBz7L4XZKLDIXS+FeQqSAUMo2otmU+Vkj0KorshMjbUXfE1KkTijTMJlaga2M
2xVVt21fRIkJNWbIL0dWFLWyRq7OXdygyFkIiW9b2/LYaePBgET22kbtHSCAEj+BlSf265+1rNxy
AXBGGGccCKzEbcqASBKHOgVp6pLqlQAfuSy6g/OzGzces3zXRrGu1N3pBIzAIwCW429n52ZlYfYR
0nr+REKDnRrPIIDsWASmEHhBezTD+v0qCJRyLz2usFgWY+7agUJE2yHHI2mTu2RAFngBilJXlMCt
VwT0xGuQxkbHAgMBAAECggEBAJJdKaVfXWNptCDkLnVaYB9y5eRgfppVkhQxfiw5023Vl1QjrgjG
hYH4zHli0IBMwXA/RZWZoFVzZ3dxoshk0iQPgGKxWvrDEJcnSCo8MGL7jPvh52jILp6uzsGZQBji
bTgFPmOBS7ShdgZiQKD9PD2psrmqHZ1yTwjIm5cGfzQM8Y6tjm0xLBn676ecJNdS1TL10y9vmSUM
Ofdkmeg9Z9TEK95lP2fF/NIcxCo0LF9JcHUvTuYBDnBH0XMZi0w0ZcRReMSdAZ2lLiXgBeCO53el
2NIrtkRx+qOvLua9UfwO2h/0rs66ZeV0YuFCjv067nytyZf2zhU/QbCHRypzfrkCgYEA/facuAJs
6MQKsNvhozoBeDRMkrZPMh8Sb0w50EqzIGz3pdms6UvCiggoMbhxKOwuYWZ689fBPGwm7x0RdwDO
jyUuEbFnQFe+CpdHy6VK7vIQed1SwAcdTMDwCYbkJNglqHEB7qUYYTFLr8okGyWVdthUoh4IAubU
TR3TFbGraDUCgYEA3bwJ/UNA5pHtb/nh4/dNL7/bRMwXyPZPpC5z+gjjgUMgsSRBz8+iPNTB4iSQ
1j9zm+pnXGi35zWZcI4jvIcFusb08eS7xcZDb+7X2r2wenLNmyuTOa1812y233FicU+ah91fa9aD
yUfTjj3GFawbgNNhMyWa3aEMV+c73t6sKosCgYEA35oQZhsMlOx2lT0jrzlVLeauPMZzeCfPbVrp
1DDRAg2vBcFf8pCXmjyQVyaTy3oXY/585tDh/DclGIa5Z9O4CmSr6TwPMqGOW3jS58SC81sBkqqB
Pz2EWJ3POjQgDyiYD3RgRSPrETf78azCmXw/2sGh0pMqbpOZ/MPzpDgoOLkCgYEAsdv4g09kCs75
Dz34hRzErE2P+8JePdPdlEuyudhRbUlEOvNjWucpMvRSRSyhhUnGWUWP/V7+TRcAanmJjtsbrHOU
3Udlm0HqrCmAubQ4kC/wXsx4Pua7Yi2RDvBrT4rT4LGgreaXNWhI+Srx7kZslUx5Bkbez3I0bXpM
2vvwS/sCgYAducNt1KC4W7jzMWUivvuy5hQQmX/G0JHtu1pfv9cmA8agnc1I/r7xoirftuSG25Pm
r+eP5SKbKb8ZQlp10JeBkNnk8eAG8OkQyBaECYDBadEr1/LK2LmIEjYKzKAjYQ4cX2KMtY271jjX
WrzzXNqBdThFfMHiJE8k9xYmaLDKhQ==
-----END PRIVATE KEY-----