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

support EC private key only for mTLS auth #5657

Merged
merged 10 commits into from
Jan 3, 2024
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* Fix #5584: Fix CRD generation when EnumMap is used
* Fix #5626: Prevent memory accumulation from informer usage
* Fix #5527: Unable to transfer file to pod if `/tmp` is read-only
* Fix #5656: Enable EC private key usage for mTLS auth

#### Improvements
* Fix #5429: moved crd generator annotations to generator-annotations instead of crd-generator-api. Using generator-annotations introduces no transitive dependencies.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

import io.fabric8.kubernetes.client.KubernetesClientException;
import io.fabric8.kubernetes.client.utils.Utils;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
Expand Down Expand Up @@ -180,22 +182,27 @@ private static PrivateKey handleECKey(InputStream keyInputStream) {
try {
return new Callable<PrivateKey>() {
@Override
public PrivateKey call() {
try {
if (Security.getProvider("BC") == null && Security.getProvider("BCFIPS") == null) {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
}
PEMKeyPair keys = (PEMKeyPair) new PEMParser(new InputStreamReader(keyInputStream)).readObject();
return new JcaPEMKeyConverter().getKeyPair(keys).getPrivate();
} catch (IOException exception) {
exception.printStackTrace();
public PrivateKey call() throws IOException {
if (Security.getProvider("BC") == null && Security.getProvider("BCFIPS") == null) {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
}
Object pemObject = new PEMParser(new InputStreamReader(keyInputStream)).readObject();
if (pemObject == null) {
throw new KubernetesClientException("Got null PEM object from EC key's input stream.");
} else if (pemObject instanceof PEMKeyPair) {
return new JcaPEMKeyConverter().getKeyPair((PEMKeyPair) pemObject).getPrivate();
} else if (pemObject instanceof PrivateKeyInfo) {
return BouncyCastleProvider.getPrivateKey((PrivateKeyInfo) pemObject);
} else {
throw new KubernetesClientException("Don't know what to do with a " + pemObject.getClass().getName());
}
return null;
}
}.call();
} catch (NoClassDefFoundError e) {
throw new KubernetesClientException(
"JcaPEMKeyConverter is provided by BouncyCastle, an optional dependency. To use support for EC Keys you must explicitly add this dependency to classpath.");
} catch (IOException e) {
throw new KubernetesClientException(e.getMessage());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package io.fabric8.kubernetes.client.internal;

import io.fabric8.kubernetes.client.KubernetesClientException;
import io.fabric8.kubernetes.client.utils.IOHelpers;
import io.fabric8.kubernetes.client.utils.Utils;
import org.junit.jupiter.api.AfterEach;
Expand All @@ -31,12 +32,14 @@
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.spec.InvalidKeySpecException;
import java.util.Base64;
import java.util.Collections;
import java.util.Objects;
import java.util.Properties;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.junit.Assert.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
Expand Down Expand Up @@ -184,6 +187,48 @@ void getInputStreamFromDataOrFileShouldDecodeBase64EncodedString() throws IOExce
assertEquals(inputStr, certDataReadFromInputStream);
}

@Test
void loadECkeys()
throws InvalidKeySpecException, CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException {
String privateKeyPath = Utils.filePath(getClass().getResource("/ssl-test/fabric8-ec.paired.key"));
String certPath = Utils.filePath(getClass().getResource("/ssl-test/fabric8-ec.cert"));

KeyStore trustStore = CertUtils.createKeyStore(null, certPath, null, privateKeyPath, "EC", "foo", null, null);

assertEquals(1, trustStore.size());
}

@Test
void loadECPrivateOnlyKey()
throws InvalidKeySpecException, CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException {
String privateKeyPath = Utils.filePath(getClass().getResource("/ssl-test/fabric8-ec.private-only.key"));
String certPath = Utils.filePath(getClass().getResource("/ssl-test/fabric8-ec.cert"));

KeyStore trustStore = CertUtils.createKeyStore(null, certPath, null, privateKeyPath, "EC", "foo", null, null);

assertEquals(1, trustStore.size());
}

@Test
void loadNothingError() {
String privateKeyPath = Utils.filePath(getClass().getResource("/ssl-test/empty"));
String certPath = Utils.filePath(getClass().getResource("/ssl-test/empty"));

assertThatExceptionOfType(KubernetesClientException.class)
.isThrownBy(() -> CertUtils.createKeyStore(null, certPath, null, privateKeyPath, "EC", "foo", null, null))
.withMessage("Got null PEM object from EC key's input stream.");
}

@Test
void loadUnknownError() {
String privateKeyPath = Utils.filePath(getClass().getResource("/ssl-test/multiple-certs.p7b"));
String certPath = Utils.filePath(getClass().getResource("/ssl-test/multiple-certs.p7b"));

assertThatExceptionOfType(KubernetesClientException.class)
.isThrownBy(() -> CertUtils.createKeyStore(null, certPath, null, privateKeyPath, "EC", "foo", null, null))
.withMessageContaining("Don't know what to do with a");
}

@Test
void storeKeyFallbacksToDefault() throws Exception {
// When
Expand Down
Empty file.
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-----
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-----
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQglAqmnWCvaPD+4fCx
rIAuhPoZcToa25FT3pKinym+9w6hRANCAAQr1Kmy52b6tabokBe3M8gO8Vi9N+Bc
BkTWaRt9NijEmB9uJNsJ835nVmvQEdjffk5nErGMcp0ytPJak27040+K
-----END PRIVATE KEY-----
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
-----BEGIN PKCS7-----
MIIGKQYJKoZIhvcNAQcCoIIGGjCCBhYCAQExADALBgkqhkiG9w0BBwGgggX8MIIC
6jCCAdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBtvcGVuc2hp
ZnQtc2lnbmVyQDE0ODkwNTI0ODEwHhcNMTcwMzA5MDk0MTIxWhcNMjIwMzA4MDk0
MTIyWjAmMSQwIgYDVQQDDBtvcGVuc2hpZnQtc2lnbmVyQDE0ODkwNTI0ODEwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHvEtorBUHnUErPhKKbRrpEd/3
Kl1UeMMowkHNxq9FMHcm6Mczuu7I0O+qtKbuW7arAZyaoSLO2ezW1lLRuGsuMSJ7
qUqiRus+RUCQWDxLeZhu4OXvcebR3T+drzd35lOwkZBqoNysmT6Uqs7RVZ05+jKK
VJpKDya99Zbr/9zTsYH+gzSAg4LOyMs9fd2tZ75pMHVS0MWMAhXLAPtP/JDMBXOx
iBoFMymsnKeHKThztjoA7dzS1XQTq4cGcCJqyj3wdvey76HBaLzMfu9ZaoA10J20
eRnk7K4oQEK9jE1rBof4huFa5vvBumSv8Ds/yMzpOA0qOjXPcmJzx0xwQQunAgMB
AAGjIzAhMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
DQEBCwUAA4IBAQA3hfVp3yf8k0dPr6p/H/xgDKxedWOUTbdfl3v+AWGgSUvDT30H
ruYqaC34FXCvoUIia4kxzZlDBDjMg9/9zOOCQdwQimgQM2tOujNUFR3HBEfgYLxt
Y106hxzJpLCO+HsYPX9rFOTAsvt2og/jflP10EHAodj40FSymL6HIV671kzbykoq
fM3Hp+fPfUmvldxZSkzCGJAlDylE8pF4NOMd7B0LKPJuQWM2wgFH1uWQqqq0gu6o
q6GoX+39CSlx5bfydd2D660+Xt0G4k2u6z4SFxqb1hKTpi6cGtB8TERa6sxbl/Kl
cfE/R05F+L34hITDjoKYSZUWe5r4weAdMtwpMIIDCjCCAfKgAwIBAgIBATANBgkq
hkiG9w0BAQsFADA2MTQwMgYDVQQDDCtvcGVuc2hpZnQtc2VydmljZS1zZXJ2aW5n
LXNpZ25lckAxNDg5MDUyNDgyMB4XDTE3MDMwOTA5NDEyMVoXDTIyMDMwODA5NDEy
MlowNjE0MDIGA1UEAwwrb3BlbnNoaWZ0LXNlcnZpY2Utc2VydmluZy1zaWduZXJA
MTQ4OTA1MjQ4MjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALKvcK+2
kKix24UR5U9en6IJ5yTYBS1ozLCTsKAKWrD4Ttpec3ON/SycoEfUXY6ao+pN2Q0Q
bH0/hi1+fFQgdHoQxo1yG4/SAwEu+n4uUse5ApCtNWLAv8jO+Y1o33CSIIM4Sm3P
uw7w/qzHAGmad6Nvg5v+ZOxdGNm7UU399zWzY6+igFo/rg8oacZVerchrnLE2cev
pjafguqy6kiuKFbfdIcYGfdmmypyC/SbpwgRABPWLBqk/AUfkNClKVi91Ysbaj5t
lFSXEu2i2z6aAhJCHddjGeq17cQybiYVLhqXPYXtdXH+khYfXY9cupqVftrXgV6c
/xWRzmRxDQyTqdMCAwEAAaMjMCEwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB/wQF
MAMBAf8wDQYJKoZIhvcNAQELBQADggEBADA5X9iNqR8JmXbHCFMovs5PIOExK6uR
1xDBMmD6op0giDXB4ljnIa4R7euo1BCIOjC9uX6YT0OEXl31IXPAzapZNHzp2sW9
8pyxDXBd78KD/WEeWfeO4rwL4CMACgqUT9LwNCdL01s74NEruNzWIfx74Az1WtlU
8YqE7RroCpcBaqZgLJih9jcBdOVuZN5gK/xJm2P1QminD5Z+YzU2yeCFOnRzueA9
tFveiPVRk454bflfsW8dfixTNeU9MuG3PbtZ9nqR0hBKWtPst1tJwLTOdNcAijt1
T4r6qoZAmGJcEp/EQ5b0beARM1STqNeO3GPOOg1Ec33+jUHYySkQ3JGhADEA
-----END PKCS7-----
Loading