Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve key loading and add more tests. #198

Merged
merged 1 commit into from
Feb 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions util/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,16 @@
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-ext-jdk15on</artifactId>
<version>1.59</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.59</version>
</dependency>
<!-- test dependencies -->
<dependency>
<groupId>junit</groupId>
Expand Down
58 changes: 45 additions & 13 deletions util/src/main/java/io/kubernetes/client/util/SSLUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
Expand Down Expand Up @@ -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,
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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();
}
}
}
5 changes: 5 additions & 0 deletions util/src/test/resources/clientauth-ec-fixed.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgA+GiZiz2t2c2AS2v
YUgHWsENie4qpXhBUBgFfjJGoAShRANCAARQcGDs68qRKmsCWNBwJylcWez+kw0O
RAlm6iiP3sDTTreKkGtnNt3FNy5VwGMU0kL9OfaR41JA/6b1BtCZB+Yz
-----END PRIVATE KEY-----
18 changes: 18 additions & 0 deletions util/src/test/resources/clientauth-ec.cert
Original file line number Diff line number Diff line change
@@ -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-----
5 changes: 5 additions & 0 deletions util/src/test/resources/clientauth-ec.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIAPhomYs9rdnNgEtr2FIB1rBDYnuKqV4QVAYBX4yRqAEoAoGCCqGSM49
AwEHoUQDQgAEUHBg7OvKkSprAljQcCcpXFns/pMNDkQJZuooj97A0063ipBrZzbd
xTcuVcBjFNJC/Tn2keNSQP+m9QbQmQfmMw==
-----END EC PRIVATE KEY-----
15 changes: 15 additions & 0 deletions util/src/test/resources/clientauth-rsa.key
Original file line number Diff line number Diff line change
@@ -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-----
31 changes: 16 additions & 15 deletions util/src/test/resources/clientauth.key
Original file line number Diff line number Diff line change
@@ -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-----