From 12980cf01a524e6507418558346f9a1241558460 Mon Sep 17 00:00:00 2001 From: Brendan Burns Date: Thu, 22 Feb 2018 15:58:22 -0800 Subject: [PATCH] Improve key loading and add more tests. --- util/pom.xml | 10 ++++ .../io/kubernetes/client/util/SSLUtils.java | 58 ++++++++++++++----- .../ClientCertificateAuthentication.java | 11 +++- .../ClientCertificateAuthenticationTest.java | 37 ++++++++++++ .../test/resources/clientauth-ec-fixed.key | 5 ++ util/src/test/resources/clientauth-ec.cert | 18 ++++++ util/src/test/resources/clientauth-ec.key | 5 ++ util/src/test/resources/clientauth-rsa.key | 15 +++++ util/src/test/resources/clientauth.key | 31 +++++----- 9 files changed, 160 insertions(+), 30 deletions(-) create mode 100644 util/src/test/resources/clientauth-ec-fixed.key create mode 100644 util/src/test/resources/clientauth-ec.cert create mode 100644 util/src/test/resources/clientauth-ec.key create mode 100644 util/src/test/resources/clientauth-rsa.key diff --git a/util/pom.xml b/util/pom.xml index 6f9f9e11be..de5c5ecf77 100644 --- a/util/pom.xml +++ b/util/pom.xml @@ -56,6 +56,16 @@ logback-classic 1.2.3 + + org.bouncycastle + bcprov-ext-jdk15on + 1.59 + + + org.bouncycastle + bcpkix-jdk15on + 1.59 + junit diff --git a/util/src/main/java/io/kubernetes/client/util/SSLUtils.java b/util/src/main/java/io/kubernetes/client/util/SSLUtils.java index ef2479efdd..62d1ffd17e 100644 --- a/util/src/main/java/io/kubernetes/client/util/SSLUtils.java +++ b/util/src/main/java/io/kubernetes/client/util/SSLUtils.java @@ -32,6 +32,7 @@ import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; +import java.security.Security; import java.security.UnrecoverableKeyException; import java.security.cert.Certificate; import java.security.cert.CertificateException; @@ -43,8 +44,15 @@ import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import org.apache.commons.codec.binary.Base64; +import org.bouncycastle.openssl.PEMKeyPair; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; public class SSLUtils { + static { + Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); + } + public static boolean isNotNullOrEmpty(String val) { return val != null && val.length() > 0; } @@ -91,6 +99,42 @@ public static KeyStore createKeyStore( } } + private static PrivateKey loadKey(InputStream keyInputStream, String clientKeyAlgo) + throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { + + // Try PKCS7 / EC + if (clientKeyAlgo.equals("EC")) { + Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); + PEMKeyPair keys = + (PEMKeyPair) new PEMParser(new InputStreamReader(keyInputStream)).readObject(); + return new JcaPEMKeyConverter().getKeyPair(keys).getPrivate(); + } + + byte[] keyBytes = decodePem(keyInputStream); + + // Try PKCS1 / RSA + if (clientKeyAlgo.equals("RSA")) { + RSAPrivateCrtKeySpec keySpec = decodePKCS1(keyBytes); + return KeyFactory.getInstance("RSA").generatePrivate(keySpec); + } + + // Try PKCS8 + // TODO: There _has_ to be a better way to do this, but I spent > + // 2 hours trying to find it and failed... + PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes); + try { + return KeyFactory.getInstance("RSA").generatePrivate(spec); + } catch (InvalidKeySpecException ex) { + // ignore if it's not RSA + } + try { + return KeyFactory.getInstance("ECDSA").generatePrivate(spec); + } catch (InvalidKeySpecException ex) { + // ignore if it's not DSA + } + throw new InvalidKeySpecException("Unknown type of PKCS8 Private Key, tried RSA and ECDSA"); + } + public static KeyStore createKeyStore( InputStream certInputStream, InputStream keyInputStream, @@ -103,19 +147,7 @@ public static KeyStore createKeyStore( CertificateFactory certFactory = CertificateFactory.getInstance("X509"); X509Certificate cert = (X509Certificate) certFactory.generateCertificate(certInputStream); - byte[] keyBytes = decodePem(keyInputStream); - - PrivateKey privateKey; - - KeyFactory keyFactory = KeyFactory.getInstance(clientKeyAlgo); - try { - // First let's try PKCS8 - privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(keyBytes)); - } catch (InvalidKeySpecException e) { - // Otherwise try PKCS1 - RSAPrivateCrtKeySpec keySpec = decodePKCS1(keyBytes); - privateKey = keyFactory.generatePrivate(keySpec); - } + PrivateKey privateKey = loadKey(keyInputStream, clientKeyAlgo); KeyStore keyStore = KeyStore.getInstance("JKS"); if (keyStoreFile != null && keyStoreFile.length() > 0) { diff --git a/util/src/main/java/io/kubernetes/client/util/credentials/ClientCertificateAuthentication.java b/util/src/main/java/io/kubernetes/client/util/credentials/ClientCertificateAuthentication.java index d76a75c401..09a8da5986 100644 --- a/util/src/main/java/io/kubernetes/client/util/credentials/ClientCertificateAuthentication.java +++ b/util/src/main/java/io/kubernetes/client/util/credentials/ClientCertificateAuthentication.java @@ -25,9 +25,16 @@ public ClientCertificateAuthentication(final byte[] certificate, final byte[] ke @Override public void provide(ApiClient client) { + String dataString = new String(key); + String algo = ""; + if (dataString.indexOf("BEGIN EC PRIVATE KEY") != -1) { + algo = "EC"; + } + if (dataString.indexOf("BEGIN RSA PRIVATE KEY") != -1) { + algo = "RSA"; + } try { - final KeyManager[] keyManagers = - SSLUtils.keyManagers(certificate, key, "RSA", "", null, null); + final KeyManager[] keyManagers = SSLUtils.keyManagers(certificate, key, algo, "", null, null); client.setKeyManagers(keyManagers); } catch (NoSuchAlgorithmException | UnrecoverableKeyException diff --git a/util/src/test/java/io/kubernetes/client/util/credentials/ClientCertificateAuthenticationTest.java b/util/src/test/java/io/kubernetes/client/util/credentials/ClientCertificateAuthenticationTest.java index 9d9dbae047..9e2fd3c959 100644 --- a/util/src/test/java/io/kubernetes/client/util/credentials/ClientCertificateAuthenticationTest.java +++ b/util/src/test/java/io/kubernetes/client/util/credentials/ClientCertificateAuthenticationTest.java @@ -8,7 +8,20 @@ public class ClientCertificateAuthenticationTest { private static final String CLIENT_CERT_PATH = Resources.getResource("clientauth.cert").getPath(); + private static final String CLIENT_EC_CERT_PATH = + Resources.getResource("clientauth-ec.cert").getPath(); + + // RSA key in PKCS8 format private static final String CLIENT_KEY_PATH = Resources.getResource("clientauth.key").getPath(); + // EC key in PKCS8 format + private static final String CLIENT_EC_KEY_PATH = + Resources.getResource("clientauth-ec-fixed.key").getPath(); + // RSA key in PKCS1 format + private static final String CLIENT_KEY_OLD_PATH = + Resources.getResource("clientauth-rsa.key").getPath(); + // EC key in PKCS7 format + private static final String CLIENT_EC_KEY_OLD_PATH = + Resources.getResource("clientauth-ec.key").getPath(); @Test public void testValidCertificates() throws Exception { @@ -23,4 +36,28 @@ public void testInvalidCertificates() { final ApiClient client = new ApiClient(); new ClientCertificateAuthentication(new byte[] {}, new byte[] {}).provide(client); } + + @Test + public void testValidECCertificates() throws Exception { + try { + final ApiClient client = new ApiClient(); + final byte[] certificate = Files.readAllBytes(Paths.get(CLIENT_EC_CERT_PATH)); + final byte[] key = Files.readAllBytes(Paths.get(CLIENT_EC_KEY_PATH)); + new ClientCertificateAuthentication(certificate, key).provide(client); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + @Test + public void testValidOldECCertificates() throws Exception { + try { + final ApiClient client = new ApiClient(); + final byte[] certificate = Files.readAllBytes(Paths.get(CLIENT_EC_CERT_PATH)); + final byte[] key = Files.readAllBytes(Paths.get(CLIENT_EC_KEY_OLD_PATH)); + new ClientCertificateAuthentication(certificate, key).provide(client); + } catch (Exception ex) { + ex.printStackTrace(); + } + } } diff --git a/util/src/test/resources/clientauth-ec-fixed.key b/util/src/test/resources/clientauth-ec-fixed.key new file mode 100644 index 0000000000..12c6342dc9 --- /dev/null +++ b/util/src/test/resources/clientauth-ec-fixed.key @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgA+GiZiz2t2c2AS2v +YUgHWsENie4qpXhBUBgFfjJGoAShRANCAARQcGDs68qRKmsCWNBwJylcWez+kw0O +RAlm6iiP3sDTTreKkGtnNt3FNy5VwGMU0kL9OfaR41JA/6b1BtCZB+Yz +-----END PRIVATE KEY----- diff --git a/util/src/test/resources/clientauth-ec.cert b/util/src/test/resources/clientauth-ec.cert new file mode 100644 index 0000000000..44c0dc3626 --- /dev/null +++ b/util/src/test/resources/clientauth-ec.cert @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC7TCCAdWgAwIBAgIUD0Q+lZUpqmvTUxmCbhsPx93hYI4wDQYJKoZIhvcNAQEL +BQAwczELMAkGA1UEBhMCVVMxFjAUBgNVBAgTDVNhbiBGcmFuY2lzY28xCzAJBgNV +BAcTAkNBMRgwFgYDVQQKEw9NeSBDb21wYW55IE5hbWUxEzARBgNVBAsTCk9yZyBV +bml0IDIxEDAOBgNVBAMTB0t1YmUgQ0EwHhcNMTgwMjEzMDc1MzAwWhcNMjMwMjEy +MDc1MzAwWjBCMQswCQYDVQQGEwJVUzEWMBQGA1UECBMNU2FuIEZyYW5jaXNjbzEL +MAkGA1UEBxMCQ0ExDjAMBgNVBAMTBWFkbWluMFkwEwYHKoZIzj0CAQYIKoZIzj0D +AQcDQgAEUHBg7OvKkSprAljQcCcpXFns/pMNDkQJZuooj97A0063ipBrZzbdxTcu +VcBjFNJC/Tn2keNSQP+m9QbQmQfmM6N1MHMwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud +JQQMMAoGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFMBq0KJaRKoC ++a8R5la/B8tJahD3MB8GA1UdIwQYMBaAFOuZHF+ex0WLX3UX9LJoFHbI9cPNMA0G +CSqGSIb3DQEBCwUAA4IBAQC47D3zYMTg/C9gOYu20xEmw6eTEhJ1wWG9jSdHM9G8 +0F3mD4+bG3skx5kaCgtJ3KqbMSGPJ234Ju9VHCNiyiasZS41a8wuagJ6v5pTItLL +BmQ3OhT2HJZz+lDbsb3jLDzesQ5UCct08/e8ST4hnZUcSrz2geD1hSYQEhadsI87 +A+wVAvGfhCyiDUiClA2vwosWfomshkWphRzy+s5zFLuxlBAxj8g2oAbCJfhfJS5A +O8W5Nu+ddO5+7PB+y28Bue1GWABIjytmyLdvic0vkKzZkSzPOwqCT1ofZ9HU56h7 +jKOjtBacCcl/7pLgvaA8Ng1qfjQTiveDVfI8rNWiEhRm +-----END CERTIFICATE----- \ No newline at end of file diff --git a/util/src/test/resources/clientauth-ec.key b/util/src/test/resources/clientauth-ec.key new file mode 100644 index 0000000000..19ea28412c --- /dev/null +++ b/util/src/test/resources/clientauth-ec.key @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIAPhomYs9rdnNgEtr2FIB1rBDYnuKqV4QVAYBX4yRqAEoAoGCCqGSM49 +AwEHoUQDQgAEUHBg7OvKkSprAljQcCcpXFns/pMNDkQJZuooj97A0063ipBrZzbd +xTcuVcBjFNJC/Tn2keNSQP+m9QbQmQfmMw== +-----END EC PRIVATE KEY----- \ No newline at end of file diff --git a/util/src/test/resources/clientauth-rsa.key b/util/src/test/resources/clientauth-rsa.key new file mode 100644 index 0000000000..fa6f6b2b2e --- /dev/null +++ b/util/src/test/resources/clientauth-rsa.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDz2XhYxigM1TVl6+O+nl4lM+uTNs4T8h2n2bUc7GVgjSN3Xj6L +6cuId+w6o9jKEC90Zgbe2UkWAkxSG3bGzhlTHM6Qh4zi8o9ybg+Yt/fYzR6KhsIt +2y1D/yiyMC4FCc3oARXh7djhk3qZuS4sbeX/DsqE61Sr9NzWR6RvqgimDwIDAQAB +AoGBAKnZs6MaO3Fc3TnmChePVgJR3OgIx5hLD+8HjMjdvGt5Q9f0dFqeed/PsGLU +F7//cB6Cpox5Cxhid2jFqoEls6q9jnnVGYGDJHDQ7u//xigVfHf9lJzmdxQWId6G +F6VoU0N6Oh2AtG4L/SQqnbacG1JVXZun/8J8bBon6C4PeacZAkEA/YaNeLcLtv22 +992UJaj3Rd9i5Sxs6dH6PDoABvkbipqWtj4osiMt6a5h32Xy/811CYDI1wvcVDDr +9HAQP1pfPQJBAPY6vcA+JuKdg9KSZzCh77aE5LGXqz62rcMlUHPtRchNfcFo06MC +ehKA+YIBmT5we0GrdyAXsYGIVypK0l8irzsCQEkr43r6wavP8FX7or131d5Zye5A +8zJNAz8MsmNQ1G0djvAMYqx/UMoIJYFXqFnCD8xtWgoPB0lZUVCcY2QVjjUCQGYk +A+alYZgL400MckXYRwodooiQ8/Z17SrQZclRGet3Sb1bcL9kHaNjYR0u8JTYMCkT +qbzkVzv2hMIEe7P/PVUCQQCxLiT5qDxQawP1/ZFnp4KCHTKTQ1Mvw8cGajEG+DKP +2GzD8K03HaJU5eN4Z1Uo1TYPQY1tRD3nM+sDH9EcLvqP +-----END RSA PRIVATE KEY----- diff --git a/util/src/test/resources/clientauth.key b/util/src/test/resources/clientauth.key index fa6f6b2b2e..4b30571a25 100644 --- a/util/src/test/resources/clientauth.key +++ b/util/src/test/resources/clientauth.key @@ -1,15 +1,16 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXQIBAAKBgQDz2XhYxigM1TVl6+O+nl4lM+uTNs4T8h2n2bUc7GVgjSN3Xj6L -6cuId+w6o9jKEC90Zgbe2UkWAkxSG3bGzhlTHM6Qh4zi8o9ybg+Yt/fYzR6KhsIt -2y1D/yiyMC4FCc3oARXh7djhk3qZuS4sbeX/DsqE61Sr9NzWR6RvqgimDwIDAQAB -AoGBAKnZs6MaO3Fc3TnmChePVgJR3OgIx5hLD+8HjMjdvGt5Q9f0dFqeed/PsGLU -F7//cB6Cpox5Cxhid2jFqoEls6q9jnnVGYGDJHDQ7u//xigVfHf9lJzmdxQWId6G -F6VoU0N6Oh2AtG4L/SQqnbacG1JVXZun/8J8bBon6C4PeacZAkEA/YaNeLcLtv22 -992UJaj3Rd9i5Sxs6dH6PDoABvkbipqWtj4osiMt6a5h32Xy/811CYDI1wvcVDDr -9HAQP1pfPQJBAPY6vcA+JuKdg9KSZzCh77aE5LGXqz62rcMlUHPtRchNfcFo06MC -ehKA+YIBmT5we0GrdyAXsYGIVypK0l8irzsCQEkr43r6wavP8FX7or131d5Zye5A -8zJNAz8MsmNQ1G0djvAMYqx/UMoIJYFXqFnCD8xtWgoPB0lZUVCcY2QVjjUCQGYk -A+alYZgL400MckXYRwodooiQ8/Z17SrQZclRGet3Sb1bcL9kHaNjYR0u8JTYMCkT -qbzkVzv2hMIEe7P/PVUCQQCxLiT5qDxQawP1/ZFnp4KCHTKTQ1Mvw8cGajEG+DKP -2GzD8K03HaJU5eN4Z1Uo1TYPQY1tRD3nM+sDH9EcLvqP ------END RSA PRIVATE KEY----- +-----BEGIN PRIVATE KEY----- +MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAPPZeFjGKAzVNWXr +476eXiUz65M2zhPyHafZtRzsZWCNI3dePovpy4h37Dqj2MoQL3RmBt7ZSRYCTFIb +dsbOGVMczpCHjOLyj3JuD5i399jNHoqGwi3bLUP/KLIwLgUJzegBFeHt2OGTepm5 +Lixt5f8OyoTrVKv03NZHpG+qCKYPAgMBAAECgYEAqdmzoxo7cVzdOeYKF49WAlHc +6AjHmEsP7weMyN28a3lD1/R0Wp5538+wYtQXv/9wHoKmjHkLGGJ3aMWqgSWzqr2O +edUZgYMkcNDu7//GKBV8d/2UnOZ3FBYh3oYXpWhTQ3o6HYC0bgv9JCqdtpwbUlVd +m6f/wnxsGifoLg95pxkCQQD9ho14twu2/bb33ZQlqPdF32LlLGzp0fo8OgAG+RuK +mpa2PiiyIy3prmHfZfL/zXUJgMjXC9xUMOv0cBA/Wl89AkEA9jq9wD4m4p2D0pJn +MKHvtoTksZerPratwyVQc+1FyE19wWjTowJ6EoD5ggGZPnB7Qat3IBexgYhXKkrS +XyKvOwJASSvjevrBq8/wVfuivXfV3lnJ7kDzMk0DPwyyY1DUbR2O8AxirH9Qyggl +gVeoWcIPzG1aCg8HSVlRUJxjZBWONQJAZiQD5qVhmAvjTQxyRdhHCh2iiJDz9nXt +KtBlyVEZ63dJvVtwv2Qdo2NhHS7wlNgwKROpvORXO/aEwgR7s/89VQJBALEuJPmo +PFBrA/X9kWengoIdMpNDUy/DxwZqMQb4Mo/YbMPwrTcdolTl43hnVSjVNg9BjW1E +Pecz6wMf0Rwu+o8= +-----END PRIVATE KEY-----