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-----