From c92c90e7ea31511f6cb6d60e00db98a0ddc8de7b Mon Sep 17 00:00:00 2001 From: Max Lambrecht Date: Mon, 22 Jun 2020 16:08:17 -0300 Subject: [PATCH] Addressing PR comments: - replace private key format and algorithm guessing by parameters - refactor a few ifs to switch case - add spiffeid and trust domain validations and tests - remove all imports wildcards - other minor changes to improve quality Signed-off-by: Max Lambrecht --- .../io/spiffe/bundle/jwtbundle/JwtBundle.java | 23 ++- .../spiffe/bundle/jwtbundle/JwtBundleSet.java | 6 +- .../bundle/x509bundle/X509BundleSet.java | 6 +- .../io/spiffe/internal/CertificateUtils.java | 126 ++++++++------- .../io/spiffe/internal/KeyFileFormat.java | 6 + .../java/io/spiffe/internal/KeyUsages.java | 24 +++ .../java/io/spiffe/spiffeid/SpiffeId.java | 37 ++++- .../io/spiffe/spiffeid/SpiffeIdUtils.java | 4 +- .../java/io/spiffe/spiffeid/TrustDomain.java | 9 +- .../io/spiffe/svid/x509svid/X509Svid.java | 24 ++- .../workloadapi/GrpcConversionUtils.java | 4 +- .../spiffe/workloadapi/WorkloadApiClient.java | 6 +- .../internal/SecurityHeaderInterceptor.java | 9 +- .../bundle/jwtbundle/JwtBundleSetTest.java | 6 +- .../bundle/jwtbundle/JwtBundleTest.java | 6 +- .../bundle/x509bundle/X509BundleSetTest.java | 7 +- .../bundle/x509bundle/X509BundleTest.java | 6 +- .../spiffe/internal/CertificateUtilsTest.java | 11 +- .../spiffe/internal/DummyX509Certificate.java | 13 +- .../java/io/spiffe/spiffeid/SpiffeIdTest.java | 149 ++++++++++-------- .../io/spiffe/spiffeid/SpiffeIdUtilsTest.java | 4 +- .../io/spiffe/spiffeid/TrustDomainTest.java | 10 +- .../io/spiffe/svid/x509svid/X509SvidTest.java | 27 +++- .../test/java/io/spiffe/utils/TestUtils.java | 13 +- .../utils/X509CertificateTestUtils.java | 8 +- .../spiffe/workloadapi/FakeWorkloadApi.java | 15 +- .../io/spiffe/workloadapi/JwtSourceTest.java | 4 +- .../workloadapi/WorkloadApiClientTest.java | 24 +-- .../io/spiffe/workloadapi/X509SourceTest.java | 4 +- .../workloadapi/retry/BackoffPolicyTest.java | 4 +- .../resources/testdata/workloadapi/bundle.der | Bin 0 -> 402 bytes .../resources/testdata/workloadapi/bundle.pem | 33 ---- .../resources/testdata/workloadapi/svid.der | Bin 0 -> 495 bytes .../resources/testdata/workloadapi/svid.key | 5 - .../testdata/workloadapi/svid.key.der | Bin 0 -> 138 bytes .../resources/testdata/workloadapi/svid.pem | 13 -- .../test/resources/testdata/x509svid/cert.der | Bin 0 -> 495 bytes .../resources/testdata/x509svid/keyEC.der | Bin 0 -> 138 bytes .../java/io/spiffe/helper/cli/Runner.java | 7 +- .../helper/keystore/FakeWorkloadApi.java | 6 +- .../helper/keystore/KeyStoreHelperTest.java | 4 +- .../spiffe/helper/keystore/KeyStoreTest.java | 5 +- .../src/test/resources/testdata/bundle.der | Bin 0 -> 402 bytes .../src/test/resources/testdata/svid.der | Bin 0 -> 495 bytes .../src/test/resources/testdata/svid.key.der | Bin 0 -> 138 bytes .../spiffe/provider/SpiffeKeyManagerTest.java | 4 +- .../provider/examples/mtls/HttpsServer.java | 8 +- .../examples/mtls/WorkloadThread.java | 7 +- 48 files changed, 432 insertions(+), 255 deletions(-) create mode 100644 java-spiffe-core/src/main/java/io/spiffe/internal/KeyFileFormat.java create mode 100644 java-spiffe-core/src/main/java/io/spiffe/internal/KeyUsages.java create mode 100644 java-spiffe-core/src/test/resources/testdata/workloadapi/bundle.der delete mode 100644 java-spiffe-core/src/test/resources/testdata/workloadapi/bundle.pem create mode 100644 java-spiffe-core/src/test/resources/testdata/workloadapi/svid.der delete mode 100644 java-spiffe-core/src/test/resources/testdata/workloadapi/svid.key create mode 100644 java-spiffe-core/src/test/resources/testdata/workloadapi/svid.key.der delete mode 100644 java-spiffe-core/src/test/resources/testdata/workloadapi/svid.pem create mode 100644 java-spiffe-core/src/test/resources/testdata/x509svid/cert.der create mode 100644 java-spiffe-core/src/test/resources/testdata/x509svid/keyEC.der create mode 100644 java-spiffe-helper/src/test/resources/testdata/bundle.der create mode 100644 java-spiffe-helper/src/test/resources/testdata/svid.der create mode 100644 java-spiffe-helper/src/test/resources/testdata/svid.key.der diff --git a/java-spiffe-core/src/main/java/io/spiffe/bundle/jwtbundle/JwtBundle.java b/java-spiffe-core/src/main/java/io/spiffe/bundle/jwtbundle/JwtBundle.java index 8b11c38..0f41b97 100644 --- a/java-spiffe-core/src/main/java/io/spiffe/bundle/jwtbundle/JwtBundle.java +++ b/java-spiffe-core/src/main/java/io/spiffe/bundle/jwtbundle/JwtBundle.java @@ -188,20 +188,17 @@ public class JwtBundle implements BundleSource { private static PublicKey getPublicKey(final JWK jwk) throws JOSEException, ParseException, KeyException { val family = Algorithm.Family.parse(jwk.getKeyType().getValue()); - PublicKey publicKey = null; - if (Algorithm.Family.EC.equals(family)) { - publicKey = ECKey.parse(jwk.toJSONString()).toPublicKey(); + PublicKey publicKey; + switch (family) { + case EC: + publicKey = ECKey.parse(jwk.toJSONString()).toPublicKey(); + break; + case RSA: + publicKey = RSAKey.parse(jwk.toJSONString()).toPublicKey(); + break; + default: + throw new KeyException(String.format("Key Type not supported: %s", jwk.getKeyType().getValue())); } - - if (Algorithm.Family.RSA.equals(family)) { - publicKey = RSAKey.parse(jwk.toJSONString()).toPublicKey(); - } - - if (publicKey == null) { - throw new KeyException(String.format("Key Type not supported: %s", jwk.getKeyType().getValue())); - } - return publicKey; - } } diff --git a/java-spiffe-core/src/main/java/io/spiffe/bundle/jwtbundle/JwtBundleSet.java b/java-spiffe-core/src/main/java/io/spiffe/bundle/jwtbundle/JwtBundleSet.java index 172cf6e..30e4181 100644 --- a/java-spiffe-core/src/main/java/io/spiffe/bundle/jwtbundle/JwtBundleSet.java +++ b/java-spiffe-core/src/main/java/io/spiffe/bundle/jwtbundle/JwtBundleSet.java @@ -7,8 +7,8 @@ import lombok.NonNull; import lombok.Value; import lombok.val; +import java.util.Collection; import java.util.Collections; -import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -27,10 +27,10 @@ public class JwtBundleSet implements BundleSource { /** * Creates a JWT bundle set from the list of JWT bundles. * - * @param bundles List of {@link JwtBundle} + * @param bundles Collection of {@link JwtBundle} * @return a {@link JwtBundleSet} */ - public static JwtBundleSet of(@NonNull final List bundles) { + public static JwtBundleSet of(@NonNull final Collection bundles) { Map bundleMap = new ConcurrentHashMap<>(); for (JwtBundle bundle : bundles) { bundleMap.put(bundle.getTrustDomain(), bundle); diff --git a/java-spiffe-core/src/main/java/io/spiffe/bundle/x509bundle/X509BundleSet.java b/java-spiffe-core/src/main/java/io/spiffe/bundle/x509bundle/X509BundleSet.java index dfb48d8..458c99f 100644 --- a/java-spiffe-core/src/main/java/io/spiffe/bundle/x509bundle/X509BundleSet.java +++ b/java-spiffe-core/src/main/java/io/spiffe/bundle/x509bundle/X509BundleSet.java @@ -7,8 +7,8 @@ import lombok.NonNull; import lombok.Value; import lombok.val; +import java.util.Collection; import java.util.Collections; -import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -27,10 +27,10 @@ public class X509BundleSet implements BundleSource { /** * Creates a new X.509 bundle set from a list of X.509 bundles. * - * @param bundles a list of {@link X509Bundle} + * @param bundles Collection of {@link X509Bundle} * @return a {@link X509BundleSet} initialized with the list of bundles */ - public static X509BundleSet of(@NonNull final List bundles) { + public static X509BundleSet of(@NonNull final Collection bundles) { Map bundleMap = new ConcurrentHashMap<>(); for (X509Bundle bundle : bundles) { bundleMap.put(bundle.getTrustDomain(), bundle); diff --git a/java-spiffe-core/src/main/java/io/spiffe/internal/CertificateUtils.java b/java-spiffe-core/src/main/java/io/spiffe/internal/CertificateUtils.java index faa586d..a2bb2a5 100644 --- a/java-spiffe-core/src/main/java/io/spiffe/internal/CertificateUtils.java +++ b/java-spiffe-core/src/main/java/io/spiffe/internal/CertificateUtils.java @@ -5,20 +5,38 @@ import io.spiffe.spiffeid.SpiffeId; import io.spiffe.spiffeid.TrustDomain; import lombok.NonNull; import lombok.val; -import org.apache.commons.lang3.RandomStringUtils; import java.io.ByteArrayInputStream; -import java.security.*; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Signature; +import java.security.SignatureException; +import java.security.cert.CertPathValidator; +import java.security.cert.CertPathValidatorException; import java.security.cert.Certificate; -import java.security.cert.*; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.CertificateParsingException; +import java.security.cert.PKIXParameters; +import java.security.cert.TrustAnchor; +import java.security.cert.X509Certificate; +import java.security.spec.EncodedKeySpec; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.util.Base64; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; -import static java.util.Collections.EMPTY_LIST; +import static io.spiffe.internal.KeyUsages.CRL_SIGN; +import static io.spiffe.internal.KeyUsages.DIGITAL_SIGNATURE; +import static io.spiffe.internal.KeyUsages.KEY_CERT_SIGN; import static org.apache.commons.lang3.StringUtils.startsWith; /** @@ -35,17 +53,6 @@ public class CertificateUtils { private static final String PUBLIC_KEY_INFRASTRUCTURE_ALGORITHM = "PKIX"; private static final String X509_CERTIFICATE_TYPE = "X.509"; - // X509Certificate Key Usage indexes - private static final int DIGITAL_SIGNATURE = 0; - private static final int NON_REPUDIATION = 1; - private static final int KEY_ENCIPHERMENT = 2; - private static final int DATA_ENCIPHERMENT = 3; - private static final int KEY_AGREEMENT = 4; - private static final int KEY_CERT_SIGN = 5; - private static final int CRL_SIGN = 6; - private static final int ENCIPHER_ONLY = 7; - private static final int DECIPHER_ONLY = 8; - private CertificateUtils() { } @@ -60,12 +67,7 @@ public class CertificateUtils { throw new CertificateParsingException("No certificates found"); } - CertificateFactory certificateFactory = null; - try { - certificateFactory = getCertificateFactory(); - } catch (CertificateException e) { - throw new IllegalStateException("Could not create Certificate Factory", e); - } + val certificateFactory = getCertificateFactory(); Collection certificates; try { @@ -87,17 +89,9 @@ public class CertificateUtils { * @throws InvalidKeySpecException * @throws NoSuchAlgorithmException */ - public static PrivateKey generatePrivateKey(final byte[] privateKeyBytes) throws InvalidKeySpecException, NoSuchAlgorithmException, InvalidKeyException { - PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(privateKeyBytes); - PrivateKey privateKey = null; - try { - privateKey = generatePrivateKeyWithSpec(kspec); - } catch (InvalidKeySpecException e) { - byte[] keyDer = toDerFormat(privateKeyBytes); - kspec = new PKCS8EncodedKeySpec(keyDer); - privateKey = generatePrivateKeyWithSpec(kspec); - } - return privateKey; + public static PrivateKey generatePrivateKey(final byte[] privateKeyBytes, Algorithm.Family algorithm, KeyFileFormat keyFileFormat) throws InvalidKeySpecException, NoSuchAlgorithmException, InvalidKeyException { + EncodedKeySpec kspec = getEncodedKeySpec(privateKeyBytes, keyFileFormat); + return generatePrivateKeyWithSpec(kspec, algorithm); } /** @@ -110,7 +104,7 @@ public class CertificateUtils { */ public static void validate(final List chain, final List trustedCerts) throws CertificateException, CertPathValidatorException { val certificateFactory = getCertificateFactory(); - PKIXParameters pkixParameters = null; + PKIXParameters pkixParameters; try { pkixParameters = toPkixParameters(trustedCerts); val certPath = certificateFactory.generateCertPath(chain); @@ -121,7 +115,7 @@ public class CertificateUtils { } /** - * Extracts the SPIFE ID from an X.509 certificate. + * Extracts the SPIFFE ID from an X.509 certificate. *

* It iterates over the list of SubjectAlternativesNames, read each entry, takes the value from the index * defined in SAN_VALUE_INDEX and filters the entries that starts with the SPIFFE_PREFIX and returns the first. @@ -165,12 +159,16 @@ public class CertificateUtils { */ public static void validatePrivateKey(final PrivateKey privateKey, final X509Certificate x509Certificate) throws InvalidKeyException { Algorithm.Family algorithm = Algorithm.Family.parse(privateKey.getAlgorithm()); - if (Algorithm.Family.RSA.equals(algorithm)) { - verifyKeys(privateKey, x509Certificate.getPublicKey(), SHA_512_WITH_RSA); - } else if (Algorithm.Family.EC.equals(algorithm)) { - verifyKeys(privateKey, x509Certificate.getPublicKey(), SHA_512_WITH_ECDSA); - } else { - throw new InvalidKeyException(String.format("Private Key algorithm not supported: %s", algorithm)); + + switch (algorithm) { + case RSA: + verifyKeys(privateKey, x509Certificate.getPublicKey(), SHA_512_WITH_RSA); + break; + case EC: + verifyKeys(privateKey, x509Certificate.getPublicKey(), SHA_512_WITH_ECDSA); + break; + default: + throw new InvalidKeyException(String.format("Private Key algorithm not supported: %s", algorithm)); } } @@ -180,23 +178,32 @@ public class CertificateUtils { public static boolean hasKeyUsageCertSign(final X509Certificate cert) { boolean[] keyUsage = cert.getKeyUsage(); - return keyUsage[KEY_CERT_SIGN]; + return keyUsage[KEY_CERT_SIGN.index()]; } public static boolean hasKeyUsageDigitalSignature(final X509Certificate cert) { boolean[] keyUsage = cert.getKeyUsage(); - return keyUsage[DIGITAL_SIGNATURE]; + return keyUsage[DIGITAL_SIGNATURE.index()]; } public static boolean hasKeyUsageCRLSign(final X509Certificate cert) { boolean[] keyUsage = cert.getKeyUsage(); - return keyUsage[CRL_SIGN]; + return keyUsage[CRL_SIGN.index()]; + } + + private static EncodedKeySpec getEncodedKeySpec(final byte[] privateKeyBytes, KeyFileFormat keyFileFormat) throws InvalidKeyException { + EncodedKeySpec keySpec; + if (keyFileFormat == KeyFileFormat.PEM) { + byte[] keyDer = toDerFormat(privateKeyBytes); + keySpec = new PKCS8EncodedKeySpec(keyDer); + } else { + keySpec = new PKCS8EncodedKeySpec(privateKeyBytes); + } + return keySpec; } private static void verifyKeys(final PrivateKey privateKey, final PublicKey publicKey, final String algorithm) throws InvalidKeyException { - final String randomString = RandomStringUtils.random(100, 0, 0, true, true, null, new SecureRandom()); - byte[] challenge = randomString.getBytes(); - + final byte[] challenge = new SecureRandom().generateSeed(100); try { Signature sig = Signature.getInstance(algorithm); sig.initSign(privateKey); @@ -214,7 +221,7 @@ public class CertificateUtils { private static List getSpiffeIds(final X509Certificate certificate) throws CertificateParsingException { if (certificate.getSubjectAlternativeNames() == null) { - return EMPTY_LIST; + return Collections.emptyList(); } return certificate.getSubjectAlternativeNames() .stream() @@ -223,12 +230,19 @@ public class CertificateUtils { .collect(Collectors.toList()); } - private static PrivateKey generatePrivateKeyWithSpec(final PKCS8EncodedKeySpec kspec) throws NoSuchAlgorithmException, InvalidKeySpecException { - try { - return KeyFactory.getInstance("EC").generatePrivate(kspec); - } catch (InvalidKeySpecException e) { - return KeyFactory.getInstance("RSA").generatePrivate(kspec); + private static PrivateKey generatePrivateKeyWithSpec(final EncodedKeySpec keySpec, Algorithm.Family algorithm) throws NoSuchAlgorithmException, InvalidKeySpecException { + PrivateKey privateKey; + switch (algorithm) { + case EC: + privateKey = KeyFactory.getInstance("EC").generatePrivate(keySpec); + break; + case RSA: + privateKey = KeyFactory.getInstance("RSA").generatePrivate(keySpec); + break; + default: + throw new NoSuchAlgorithmException(String.format("Private Key algorithm is not supported: %s", algorithm)); } + return privateKey; } // Create an instance of PKIXParameters used as input for the PKIX CertPathValidator @@ -250,8 +264,12 @@ public class CertificateUtils { } // Get the X.509 Certificate Factory - private static CertificateFactory getCertificateFactory() throws CertificateException { - return CertificateFactory.getInstance(X509_CERTIFICATE_TYPE); + private static CertificateFactory getCertificateFactory() { + try { + return CertificateFactory.getInstance(X509_CERTIFICATE_TYPE); + } catch (CertificateException e) { + throw new IllegalStateException("Could not create Certificate Factory", e); + } } // Given a private key in PEM format, encode it as DER diff --git a/java-spiffe-core/src/main/java/io/spiffe/internal/KeyFileFormat.java b/java-spiffe-core/src/main/java/io/spiffe/internal/KeyFileFormat.java new file mode 100644 index 0000000..ceb03bd --- /dev/null +++ b/java-spiffe-core/src/main/java/io/spiffe/internal/KeyFileFormat.java @@ -0,0 +1,6 @@ +package io.spiffe.internal; + +public enum KeyFileFormat { + PEM, + DER +} diff --git a/java-spiffe-core/src/main/java/io/spiffe/internal/KeyUsages.java b/java-spiffe-core/src/main/java/io/spiffe/internal/KeyUsages.java new file mode 100644 index 0000000..a12af38 --- /dev/null +++ b/java-spiffe-core/src/main/java/io/spiffe/internal/KeyUsages.java @@ -0,0 +1,24 @@ +package io.spiffe.internal; + +public enum KeyUsages { + + DIGITAL_SIGNATURE(0), + NON_REPUDIATION(1), + KEY_ENCIPHERMENT(2), + DATA_ENCIPHERMENT(3), + KEY_AGREEMENT(4), + KEY_CERT_SIGN(5), + CRL_SIGN(6), + ENCIPHER_ONLY(7), + DECIPHER_ONLY(8); + + private final int index; + + public int index() { + return index; + } + + KeyUsages(final int index) { + this.index = index; + } +} diff --git a/java-spiffe-core/src/main/java/io/spiffe/spiffeid/SpiffeId.java b/java-spiffe-core/src/main/java/io/spiffe/spiffeid/SpiffeId.java index 8e7dbd8..b7ada59 100644 --- a/java-spiffe-core/src/main/java/io/spiffe/spiffeid/SpiffeId.java +++ b/java-spiffe-core/src/main/java/io/spiffe/spiffeid/SpiffeId.java @@ -18,6 +18,7 @@ import java.util.stream.Collectors; public class SpiffeId { public static final String SPIFFE_SCHEME = "spiffe"; + public static final int SPIFFE_ID_MAX_LENGTH = 2048; TrustDomain trustDomain; @@ -59,10 +60,7 @@ public class SpiffeId { } val uri = URI.create(normalize(spiffeIdAsString)); - - if (!SPIFFE_SCHEME.equals(uri.getScheme())) { - throw new IllegalArgumentException("Invalid SPIFFE schema"); - } + validateUri(uri); val trustDomain = TrustDomain.of(uri.getHost()); val path = uri.getPath(); @@ -91,4 +89,35 @@ public class SpiffeId { private static String normalize(final String s) { return s.toLowerCase().trim(); } + + private static void validateUri(final URI uri) { + val scheme = uri.getScheme(); + if (!SpiffeId.SPIFFE_SCHEME.equals(scheme)) { + throw new IllegalArgumentException("SPIFFE ID: invalid scheme"); + } + + if (uri.getUserInfo() != null) { + throw new IllegalArgumentException("SPIFFE ID: user info is not allowed"); + } + + if (StringUtils.isBlank(uri.getHost())) { + throw new IllegalArgumentException("SPIFFE ID: trust domain is empty"); + } + + if (uri.getPort() != -1) { + throw new IllegalArgumentException("SPIFFE ID: port is not allowed"); + } + + if (StringUtils.isNotBlank(uri.getFragment())) { + throw new IllegalArgumentException("SPIFFE ID: fragment is not allowed"); + } + + if (StringUtils.isNotBlank(uri.getRawQuery())) { + throw new IllegalArgumentException("SPIFFE ID: query is not allowed"); + } + + if (uri.toString().length() > SPIFFE_ID_MAX_LENGTH) { + throw new IllegalArgumentException("SPIFFE ID: too long, maximum is 2048 bytes"); + } + } } diff --git a/java-spiffe-core/src/main/java/io/spiffe/spiffeid/SpiffeIdUtils.java b/java-spiffe-core/src/main/java/io/spiffe/spiffeid/SpiffeIdUtils.java index fa82544..3858a99 100644 --- a/java-spiffe-core/src/main/java/io/spiffe/spiffeid/SpiffeIdUtils.java +++ b/java-spiffe-core/src/main/java/io/spiffe/spiffeid/SpiffeIdUtils.java @@ -6,11 +6,11 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; -import static java.util.Collections.EMPTY_LIST; import static org.apache.commons.lang3.StringUtils.isBlank; /** @@ -49,7 +49,7 @@ public class SpiffeIdUtils { */ public static List toListOfSpiffeIds(final String spiffeIds, final char separator) { if (isBlank(spiffeIds)) { - return EMPTY_LIST; + return Collections.emptyList(); } val array = spiffeIds.split(String.valueOf(separator)); diff --git a/java-spiffe-core/src/main/java/io/spiffe/spiffeid/TrustDomain.java b/java-spiffe-core/src/main/java/io/spiffe/spiffeid/TrustDomain.java index 9c9eef5..469d55b 100644 --- a/java-spiffe-core/src/main/java/io/spiffe/spiffeid/TrustDomain.java +++ b/java-spiffe-core/src/main/java/io/spiffe/spiffeid/TrustDomain.java @@ -15,6 +15,7 @@ import java.net.URISyntaxException; @Value public class TrustDomain { + public static final int TRUST_DOMAIN_MAX_LENGTH = 255; String name; private TrustDomain(final String trustDomain) { @@ -37,11 +38,11 @@ public class TrustDomain { try { val normalized = normalize(trustDomain); uri = new URI(normalized); - validateUri(uri); } catch (URISyntaxException e) { throw new IllegalArgumentException(e.getMessage(), e); } + validateUri(uri); val host = uri.getHost(); validateHost(host); return new TrustDomain(host); @@ -81,7 +82,11 @@ public class TrustDomain { val port = uri.getPort(); if (port != -1) { - throw new IllegalArgumentException("Port is not allowed"); + throw new IllegalArgumentException("Trust Domain: port is not allowed"); + } + + if (uri.toString().length() > TRUST_DOMAIN_MAX_LENGTH) { + throw new IllegalArgumentException("Trust Domain: too long, maximum is 255 bytes"); } } diff --git a/java-spiffe-core/src/main/java/io/spiffe/svid/x509svid/X509Svid.java b/java-spiffe-core/src/main/java/io/spiffe/svid/x509svid/X509Svid.java index 60b7ce1..86fe62b 100644 --- a/java-spiffe-core/src/main/java/io/spiffe/svid/x509svid/X509Svid.java +++ b/java-spiffe-core/src/main/java/io/spiffe/svid/x509svid/X509Svid.java @@ -1,7 +1,9 @@ package io.spiffe.svid.x509svid; +import io.spiffe.Algorithm; import io.spiffe.exception.X509SvidException; import io.spiffe.internal.CertificateUtils; +import io.spiffe.internal.KeyFileFormat; import io.spiffe.spiffeid.SpiffeId; import lombok.NonNull; import lombok.Value; @@ -68,7 +70,7 @@ public class X509Svid implements X509SvidSource { } catch (IOException e) { throw new X509SvidException("Cannot read private key file", e); } - return createX509Svid(certsBytes, privateKeyBytes); + return createX509Svid(certsBytes, privateKeyBytes, KeyFileFormat.PEM); } /** @@ -81,7 +83,20 @@ public class X509Svid implements X509SvidSource { * @throws X509SvidException if the given certsBytes or privateKeyBytes cannot be parsed */ public static X509Svid parse(@NonNull final byte[] certsBytes, @NonNull final byte[] privateKeyBytes) throws X509SvidException { - return createX509Svid(certsBytes, privateKeyBytes); + return createX509Svid(certsBytes, privateKeyBytes, KeyFileFormat.PEM); + } + + /** + * Parses the X509-SVID from certificate and key bytes. The certificate must be ASN.1 DER (concatenated with + * no intermediate padding if there are more than one certificate). The key must be a PKCS#8 ASN.1 DER. + * + * @param certsBytes chain of certificates as a byte array + * @param privateKeyBytes private key as byte array + * @return a {@link X509Svid} parsed from the given certBytes and privateKeyBytes + * @throws X509SvidException if the given certsBytes or privateKeyBytes cannot be parsed + */ + public static X509Svid parseRaw(@NonNull final byte[] certsBytes, @NonNull final byte[] privateKeyBytes) throws X509SvidException { + return createX509Svid(certsBytes, privateKeyBytes, KeyFileFormat.DER); } /** @@ -91,7 +106,7 @@ public class X509Svid implements X509SvidSource { return chain.toArray(new X509Certificate[0]); } - private static X509Svid createX509Svid(final byte[] certsBytes, final byte[] privateKeyBytes) throws X509SvidException { + private static X509Svid createX509Svid(final byte[] certsBytes, final byte[] privateKeyBytes, KeyFileFormat keyFileFormat) throws X509SvidException { List x509Certificates; try { @@ -100,9 +115,10 @@ public class X509Svid implements X509SvidSource { throw new X509SvidException("Certificate could not be parsed from cert bytes", e); } + Algorithm.Family algorithm = Algorithm.Family.parse(x509Certificates.get(0).getPublicKey().getAlgorithm()); PrivateKey privateKey; try { - privateKey = CertificateUtils.generatePrivateKey(privateKeyBytes); + privateKey = CertificateUtils.generatePrivateKey(privateKeyBytes, algorithm, keyFileFormat); } catch (InvalidKeySpecException | InvalidKeyException | NoSuchAlgorithmException e) { throw new X509SvidException("Private Key could not be parsed from key bytes", e); } diff --git a/java-spiffe-core/src/main/java/io/spiffe/workloadapi/GrpcConversionUtils.java b/java-spiffe-core/src/main/java/io/spiffe/workloadapi/GrpcConversionUtils.java index 02a1fd9..9cd84e8 100644 --- a/java-spiffe-core/src/main/java/io/spiffe/workloadapi/GrpcConversionUtils.java +++ b/java-spiffe-core/src/main/java/io/spiffe/workloadapi/GrpcConversionUtils.java @@ -57,9 +57,7 @@ class GrpcConversionUtils { List x509SvidList = new ArrayList<>(); for (Workload.X509SVID x509SVID : x509SVIDResponse.getSvidsList()) { - val svid = X509Svid.parse( - x509SVID.getX509Svid().toByteArray(), - x509SVID.getX509SvidKey().toByteArray()); + val svid = X509Svid.parseRaw(x509SVID.getX509Svid().toByteArray(), x509SVID.getX509SvidKey().toByteArray()); x509SvidList.add(svid); if (!x509SVID.getSpiffeId().equals(svid.getSpiffeId().toString())) { diff --git a/java-spiffe-core/src/main/java/io/spiffe/workloadapi/WorkloadApiClient.java b/java-spiffe-core/src/main/java/io/spiffe/workloadapi/WorkloadApiClient.java index eb2b669..df2a3a9 100644 --- a/java-spiffe-core/src/main/java/io/spiffe/workloadapi/WorkloadApiClient.java +++ b/java-spiffe-core/src/main/java/io/spiffe/workloadapi/WorkloadApiClient.java @@ -4,7 +4,11 @@ import io.grpc.Context; import io.grpc.Status; import io.grpc.stub.StreamObserver; import io.spiffe.bundle.jwtbundle.JwtBundleSet; -import io.spiffe.exception.*; +import io.spiffe.exception.JwtBundleException; +import io.spiffe.exception.JwtSvidException; +import io.spiffe.exception.SocketEndpointAddressException; +import io.spiffe.exception.X509ContextException; +import io.spiffe.exception.X509SvidException; import io.spiffe.spiffeid.SpiffeId; import io.spiffe.svid.jwtsvid.JwtSvid; import io.spiffe.workloadapi.grpc.SpiffeWorkloadAPIGrpc; diff --git a/java-spiffe-core/src/main/java/io/spiffe/workloadapi/internal/SecurityHeaderInterceptor.java b/java-spiffe-core/src/main/java/io/spiffe/workloadapi/internal/SecurityHeaderInterceptor.java index cb5338e..fdd8c15 100644 --- a/java-spiffe-core/src/main/java/io/spiffe/workloadapi/internal/SecurityHeaderInterceptor.java +++ b/java-spiffe-core/src/main/java/io/spiffe/workloadapi/internal/SecurityHeaderInterceptor.java @@ -1,6 +1,13 @@ package io.spiffe.workloadapi.internal; -import io.grpc.*; +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.ClientCall; +import io.grpc.ClientInterceptor; +import io.grpc.ForwardingClientCall; +import io.grpc.ForwardingClientCallListener; +import io.grpc.Metadata; +import io.grpc.MethodDescriptor; /** * ClientInterceptor implementation to add a security header required to connect to the Workload API. diff --git a/java-spiffe-core/src/test/java/io/spiffe/bundle/jwtbundle/JwtBundleSetTest.java b/java-spiffe-core/src/test/java/io/spiffe/bundle/jwtbundle/JwtBundleSetTest.java index 8085251..340bc30 100644 --- a/java-spiffe-core/src/test/java/io/spiffe/bundle/jwtbundle/JwtBundleSetTest.java +++ b/java-spiffe-core/src/test/java/io/spiffe/bundle/jwtbundle/JwtBundleSetTest.java @@ -9,7 +9,11 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; class JwtBundleSetTest { diff --git a/java-spiffe-core/src/test/java/io/spiffe/bundle/jwtbundle/JwtBundleTest.java b/java-spiffe-core/src/test/java/io/spiffe/bundle/jwtbundle/JwtBundleTest.java index 8a10fa8..77e3136 100644 --- a/java-spiffe-core/src/test/java/io/spiffe/bundle/jwtbundle/JwtBundleTest.java +++ b/java-spiffe-core/src/test/java/io/spiffe/bundle/jwtbundle/JwtBundleTest.java @@ -20,7 +20,11 @@ import java.security.KeyPair; import java.security.PublicKey; import java.util.HashMap; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; class JwtBundleTest { diff --git a/java-spiffe-core/src/test/java/io/spiffe/bundle/x509bundle/X509BundleSetTest.java b/java-spiffe-core/src/test/java/io/spiffe/bundle/x509bundle/X509BundleSetTest.java index 17e6f9f..4869e34 100644 --- a/java-spiffe-core/src/test/java/io/spiffe/bundle/x509bundle/X509BundleSetTest.java +++ b/java-spiffe-core/src/test/java/io/spiffe/bundle/x509bundle/X509BundleSetTest.java @@ -1,15 +1,18 @@ package io.spiffe.bundle.x509bundle; import io.spiffe.exception.BundleNotFoundException; -import org.junit.jupiter.api.Test; import io.spiffe.internal.DummyX509Certificate; import io.spiffe.spiffeid.TrustDomain; +import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collections; import java.util.List; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; class X509BundleSetTest { diff --git a/java-spiffe-core/src/test/java/io/spiffe/bundle/x509bundle/X509BundleTest.java b/java-spiffe-core/src/test/java/io/spiffe/bundle/x509bundle/X509BundleTest.java index 2d4049b..d6c0c50 100644 --- a/java-spiffe-core/src/test/java/io/spiffe/bundle/x509bundle/X509BundleTest.java +++ b/java-spiffe-core/src/test/java/io/spiffe/bundle/x509bundle/X509BundleTest.java @@ -22,7 +22,11 @@ import java.security.cert.X509Certificate; import java.util.HashSet; import java.util.stream.Stream; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; public class X509BundleTest { diff --git a/java-spiffe-core/src/test/java/io/spiffe/internal/CertificateUtilsTest.java b/java-spiffe-core/src/test/java/io/spiffe/internal/CertificateUtilsTest.java index d28bb40..f4225c5 100644 --- a/java-spiffe-core/src/test/java/io/spiffe/internal/CertificateUtilsTest.java +++ b/java-spiffe-core/src/test/java/io/spiffe/internal/CertificateUtilsTest.java @@ -1,11 +1,12 @@ package io.spiffe.internal; import com.nimbusds.jose.jwk.Curve; +import io.spiffe.Algorithm; import io.spiffe.spiffeid.SpiffeId; import io.spiffe.spiffeid.TrustDomain; +import io.spiffe.utils.TestUtils; import lombok.val; import org.junit.jupiter.api.Test; -import io.spiffe.utils.TestUtils; import java.io.IOException; import java.net.URI; @@ -23,9 +24,11 @@ import java.security.spec.InvalidKeySpecException; import java.util.Arrays; import java.util.List; -import static org.junit.jupiter.api.Assertions.*; import static io.spiffe.utils.X509CertificateTestUtils.createCertificate; import static io.spiffe.utils.X509CertificateTestUtils.createRootCA; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; public class CertificateUtilsTest { @@ -71,7 +74,7 @@ public class CertificateUtilsTest { val keyBytes = Files.readAllBytes(keyPath); try { - PrivateKey privateKey = CertificateUtils.generatePrivateKey(keyBytes); + PrivateKey privateKey = CertificateUtils.generatePrivateKey(keyBytes, Algorithm.Family.RSA, KeyFileFormat.PEM); assertNotNull(privateKey); assertEquals("RSA", privateKey.getAlgorithm()); } catch (InvalidKeySpecException | InvalidKeyException | NoSuchAlgorithmException e) { @@ -85,7 +88,7 @@ public class CertificateUtilsTest { byte[] keyBytes = ecKeyPair.getPrivate().getEncoded(); try { - PrivateKey privateKey = CertificateUtils.generatePrivateKey(keyBytes); + PrivateKey privateKey = CertificateUtils.generatePrivateKey(keyBytes, Algorithm.Family.EC, KeyFileFormat.DER); assertNotNull(privateKey); assertEquals("EC", privateKey.getAlgorithm()); } catch (InvalidKeySpecException | InvalidKeyException | NoSuchAlgorithmException e) { diff --git a/java-spiffe-core/src/test/java/io/spiffe/internal/DummyX509Certificate.java b/java-spiffe-core/src/test/java/io/spiffe/internal/DummyX509Certificate.java index 010a4f2..3664575 100644 --- a/java-spiffe-core/src/test/java/io/spiffe/internal/DummyX509Certificate.java +++ b/java-spiffe-core/src/test/java/io/spiffe/internal/DummyX509Certificate.java @@ -1,8 +1,17 @@ package io.spiffe.internal; import java.math.BigInteger; -import java.security.*; -import java.security.cert.*; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Principal; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.X509Certificate; import java.util.Date; import java.util.Set; diff --git a/java-spiffe-core/src/test/java/io/spiffe/spiffeid/SpiffeIdTest.java b/java-spiffe-core/src/test/java/io/spiffe/spiffeid/SpiffeIdTest.java index 26d9894..472709f 100644 --- a/java-spiffe-core/src/test/java/io/spiffe/spiffeid/SpiffeIdTest.java +++ b/java-spiffe-core/src/test/java/io/spiffe/spiffeid/SpiffeIdTest.java @@ -2,11 +2,20 @@ package io.spiffe.spiffeid; import lombok.val; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; -import static org.junit.jupiter.api.Assertions.*; +import java.util.stream.Stream; + +import static io.spiffe.utils.TestUtils.getLongString; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class SpiffeIdTest { - + @Test void of_TrustDomainAndPathSegments_ReturnsSpiffeIdWithTrustDomainAndPathWithSegments() { val trustDomain = TrustDomain.of("trust-domain.org"); @@ -109,80 +118,82 @@ public class SpiffeIdTest { ); } + @Test + void parse_pathWithColons() { + val spiffeIdAsString = " spiffe://domain.test/pa:th/element: "; + + val spiffeId = SpiffeId.parse(spiffeIdAsString); + + assertAll("SpiffeId", + () -> assertEquals("domain.test", spiffeId.getTrustDomain().toString()), + () -> assertEquals("/pa:th/element:", spiffeId.getPath()) + ); + } @Test - void parse_aStringContainingInvalidSchema_throwsIllegalArgumentException() { - val invalidadSpiffeId = "siffe://trust-domain.org/path1/path2"; + void parse_pathWithAt() { + val spiffeIdAsString = "spiffe://domain.test/pa@th/element:"; + val spiffeId = SpiffeId.parse(spiffeIdAsString); + + assertAll("SpiffeId", + () -> assertEquals("domain.test", spiffeId.getTrustDomain().toString()), + () -> assertEquals("/pa@th/element:", spiffeId.getPath()) + ); + } + + @Test + void parse_pathHasEncodedSubdelims() { + val spiffeIdAsString = "spiffe://domain.test/p!a$t&h'/(e)l*e+m,e;n=t"; + + val spiffeId = SpiffeId.parse(spiffeIdAsString); + + assertAll("SpiffeId", + () -> assertEquals("domain.test", spiffeId.getTrustDomain().toString()), + () -> assertEquals("/p!a$t&h'/(e)l*e+m,e;n=t", spiffeId.getPath()) + ); + } + + @Test + void parse_spiffeId_maxLength() { + val path = "/" + getLongString(2027); + val spiffeIdAsString = "spiffe://domain.test" + path; + + val spiffeId = SpiffeId.parse(spiffeIdAsString); + + assertAll("SpiffeId", + () -> assertEquals("domain.test", spiffeId.getTrustDomain().toString()), + () -> assertEquals(path, spiffeId.getPath()) + ); + } + + @ParameterizedTest + @MethodSource("provideTestInvalidSpiffeIds") + void testParseTrustDomain(String input, Object expected) { + SpiffeId result; try { - SpiffeId.parse(invalidadSpiffeId); - fail("Should have thrown IllegalArgumentException"); - } catch (IllegalArgumentException e) { - assertEquals("Invalid SPIFFE schema", e.getMessage()); + result = SpiffeId.parse(input); + assertEquals(expected, result.toString()); + } catch (Exception e) { + assertEquals(expected, e.getMessage()); } } - @Test - void parse_aBlankString_throwsIllegalArgumentException() { - try { - SpiffeId.parse(""); - fail("Should have thrown IllegalArgumentException"); - } catch (IllegalArgumentException e) { - assertEquals("SPIFFE ID cannot be empty", e.getMessage()); - } + static Stream provideTestInvalidSpiffeIds() { + return Stream.of( + Arguments.of("", "SPIFFE ID cannot be empty"), + Arguments.of("192.168.2.2:6688", "Illegal character in scheme name at index 0: 192.168.2.2:6688"), + Arguments.of("http://domain.test/path/element", "SPIFFE ID: invalid scheme"), + Arguments.of("spiffe:///path/element", "SPIFFE ID: trust domain is empty"), + Arguments.of("spiffe://domain.test/path/element?query=1", "SPIFFE ID: query is not allowed"), + Arguments.of("spiffe://domain.test/path/element?#fragment-1", "SPIFFE ID: fragment is not allowed"), + Arguments.of("spiffe://domain.test:8080/path/element", "SPIFFE ID: port is not allowed"), + Arguments.of("spiffe://user:password@test.org/path/element", "SPIFFE ID: user info is not allowed"), + Arguments.of("spiffe:path/element", "SPIFFE ID: trust domain is empty"), + Arguments.of("spiffe:/path/element", "SPIFFE ID: trust domain is empty"), + Arguments.of("spiffe://domain.test/path/elem%5uent", "Malformed escape pair at index 30: spiffe://domain.test/path/elem%5uent"), + Arguments.of("spiffe://domain.test/"+getLongString(2028), "SPIFFE ID: too long, maximum is 2048 bytes") + ); } - @Test - void parse_Null_throwsIllegalArgumentException() { - try { - SpiffeId.parse(null); - fail("Should have thrown IllegalArgumentException"); - } catch (NullPointerException e) { - assertEquals("spiffeIdAsString is marked non-null but is null", e.getMessage()); - } - } - - @Test - void of_nullTrustDomain_throwsNullPointerException() { - try { - SpiffeId.of(null); - fail("Should have thrown IllegalArgumentException"); - } catch (NullPointerException e) { - assertEquals("trustDomain is marked non-null but is null", e.getMessage()); - } - } - - @Test - void of_nullTrustDomainNotNullPath_throwsIllegalArgumentException() { - try { - SpiffeId.of(null, "path"); - fail("Should have thrown IllegalArgumentException"); - } catch (NullPointerException e) { - assertEquals("trustDomain is marked non-null but is null", e.getMessage()); - } - } - - @Test - void equals_twoSpiffeIdsWithSameTrustDomainAndPath_returnsTrue() { - val spiffeId1 = SpiffeId.of(TrustDomain.of("example.org"), "path1"); - val spiffeId2 = SpiffeId.of(TrustDomain.of("example.org"), "path1"); - - assertEquals(spiffeId1, spiffeId2); - } - - @Test - void equals_twoSpiffeIdsWithSameTrustDomainAndDifferentPath_returnsFalse() { - val spiffeId1 = SpiffeId.of(TrustDomain.of("example.org"), "path1"); - val spiffeId2 = SpiffeId.of(TrustDomain.of("example.org"), "other"); - - assertNotEquals(spiffeId1, spiffeId2); - } - - @Test - void equals_twoSpiffeIdsWithDifferentTrustDomainAndSamePath_returnsFalse() { - val spiffeId1 = SpiffeId.of(TrustDomain.of("example.org"), "path1"); - val spiffeId2 = SpiffeId.of(TrustDomain.of("other.org"), "path1"); - - assertNotEquals(spiffeId1, spiffeId2); - } } diff --git a/java-spiffe-core/src/test/java/io/spiffe/spiffeid/SpiffeIdUtilsTest.java b/java-spiffe-core/src/test/java/io/spiffe/spiffeid/SpiffeIdUtilsTest.java index e07fd18..ae2b33c 100644 --- a/java-spiffe-core/src/test/java/io/spiffe/spiffeid/SpiffeIdUtilsTest.java +++ b/java-spiffe-core/src/test/java/io/spiffe/spiffeid/SpiffeIdUtilsTest.java @@ -10,7 +10,9 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; class SpiffeIdUtilsTest { diff --git a/java-spiffe-core/src/test/java/io/spiffe/spiffeid/TrustDomainTest.java b/java-spiffe-core/src/test/java/io/spiffe/spiffeid/TrustDomainTest.java index ef97bd9..9ba083f 100644 --- a/java-spiffe-core/src/test/java/io/spiffe/spiffeid/TrustDomainTest.java +++ b/java-spiffe-core/src/test/java/io/spiffe/spiffeid/TrustDomainTest.java @@ -7,6 +7,7 @@ import org.junit.jupiter.params.provider.MethodSource; import java.util.stream.Stream; +import static io.spiffe.utils.TestUtils.getLongString; import static org.junit.jupiter.api.Assertions.assertEquals; public class TrustDomainTest { @@ -37,6 +38,12 @@ public class TrustDomainTest { TrustDomain trustDomain = TrustDomain.of("test.domain"); assertEquals("test.domain", trustDomain.toString()); } + @Test + void testFromMaxLength() { + final String longString = getLongString(246); // 246 = 255(max) - 9('spiffe://' bytes) + TrustDomain trustDomain = TrustDomain.of(longString); + assertEquals(longString, trustDomain.toString()); + } @Test void testGetName() { @@ -58,7 +65,8 @@ public class TrustDomainTest { Arguments.of("://domain.test", "Expected scheme name at index 0: ://domain.test"), Arguments.of("spiffe:///path/element", "Trust domain cannot be empty"), Arguments.of("/path/element", "Trust domain cannot be empty"), - Arguments.of("spiffe://domain.test:80", "Port is not allowed") + Arguments.of("spiffe://domain.test:80", "Trust Domain: port is not allowed"), + Arguments.of(getLongString(256), "Trust Domain: too long, maximum is 255 bytes") ); } } diff --git a/java-spiffe-core/src/test/java/io/spiffe/svid/x509svid/X509SvidTest.java b/java-spiffe-core/src/test/java/io/spiffe/svid/x509svid/X509SvidTest.java index dfc395e..c0e1a8e 100644 --- a/java-spiffe-core/src/test/java/io/spiffe/svid/x509svid/X509SvidTest.java +++ b/java-spiffe-core/src/test/java/io/spiffe/svid/x509svid/X509SvidTest.java @@ -1,6 +1,8 @@ package io.spiffe.svid.x509svid; import io.spiffe.exception.X509SvidException; +import io.spiffe.spiffeid.SpiffeId; +import io.spiffe.spiffeid.TrustDomain; import lombok.Builder; import lombok.Value; import org.junit.jupiter.api.Test; @@ -8,9 +10,8 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.junit.platform.commons.util.StringUtils; -import io.spiffe.spiffeid.SpiffeId; -import io.spiffe.spiffeid.TrustDomain; +import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; @@ -19,7 +20,9 @@ import java.nio.file.Paths; import java.security.cert.X509Certificate; import java.util.stream.Stream; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; public class X509SvidTest { @@ -37,6 +40,8 @@ public class X509SvidTest { static String certMultiple = "testdata/x509svid/good-leaf-and-intermediate.pem"; static String corrupted = "testdata/x509svid/corrupted"; static String keyECDSAOther = "testdata/x509svid/key-ecdsa-other.pem"; + static String keyDER = "testdata/x509svid/keyEC.der"; + static String certDER = "testdata/x509svid/cert.der"; static Stream provideX509SvidScenarios() { return Stream.of( @@ -180,6 +185,22 @@ public class X509SvidTest { } } + @Test + void testParseRaw() throws URISyntaxException, IOException { + Path certPath = Paths.get(toUri(certDER)); + Path keyPath = Paths.get(toUri(keyDER)); + + byte[] certBytes = Files.readAllBytes(certPath); + byte[] keyBytes = Files.readAllBytes(keyPath); + + try { + X509Svid x509Svid = X509Svid.parseRaw(certBytes, keyBytes); + assertEquals("spiffe://example.org/workload-server", x509Svid.getSpiffeId().toString()); + } catch (X509SvidException e) { + fail(e); + } + } + @Test void testLoad_FailsCannotReadCertFile() throws URISyntaxException { Path keyPath = Paths.get(toUri(keyRSA)); diff --git a/java-spiffe-core/src/test/java/io/spiffe/utils/TestUtils.java b/java-spiffe-core/src/test/java/io/spiffe/utils/TestUtils.java index f985842..53634df 100644 --- a/java-spiffe-core/src/test/java/io/spiffe/utils/TestUtils.java +++ b/java-spiffe-core/src/test/java/io/spiffe/utils/TestUtils.java @@ -11,12 +11,19 @@ import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; import java.lang.reflect.Field; -import java.security.*; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; import java.security.spec.ECGenParameterSpec; import java.util.Date; import java.util.List; import java.util.Map; +import static java.util.stream.Collectors.joining; +import static java.util.stream.Stream.generate; + /** * Util methods for generating KeyPairs, tokens, and other functionality used only to be used in testing. */ @@ -96,6 +103,10 @@ public class TestUtils { .build(); } + public static String getLongString(int nBytes) { + return generate(() -> "a").limit(nBytes).collect(joining()); + } + public static void setEnvironmentVariable(String variableName, String value) throws Exception { Class processEnvironment = Class.forName("java.lang.ProcessEnvironment"); diff --git a/java-spiffe-core/src/test/java/io/spiffe/utils/X509CertificateTestUtils.java b/java-spiffe-core/src/test/java/io/spiffe/utils/X509CertificateTestUtils.java index b1e6509..e133e4e 100644 --- a/java-spiffe-core/src/test/java/io/spiffe/utils/X509CertificateTestUtils.java +++ b/java-spiffe-core/src/test/java/io/spiffe/utils/X509CertificateTestUtils.java @@ -5,7 +5,13 @@ import org.apache.commons.lang3.StringUtils; import org.bouncycastle.asn1.ASN1EncodableVector; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.x500.X500Name; -import org.bouncycastle.asn1.x509.*; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.GeneralNames; +import org.bouncycastle.asn1.x509.KeyPurposeId; +import org.bouncycastle.asn1.x509.KeyUsage; +import org.bouncycastle.asn1.x509.SubjectKeyIdentifier; import org.bouncycastle.cert.CertIOException; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; diff --git a/java-spiffe-core/src/test/java/io/spiffe/workloadapi/FakeWorkloadApi.java b/java-spiffe-core/src/test/java/io/spiffe/workloadapi/FakeWorkloadApi.java index 386a72c..88e3775 100644 --- a/java-spiffe-core/src/test/java/io/spiffe/workloadapi/FakeWorkloadApi.java +++ b/java-spiffe-core/src/test/java/io/spiffe/workloadapi/FakeWorkloadApi.java @@ -9,11 +9,11 @@ import io.grpc.Status; import io.grpc.StatusRuntimeException; import io.grpc.stub.StreamObserver; import io.spiffe.exception.JwtSvidException; -import org.junit.platform.commons.util.StringUtils; import io.spiffe.svid.jwtsvid.JwtSvid; import io.spiffe.utils.TestUtils; import io.spiffe.workloadapi.grpc.SpiffeWorkloadAPIGrpc.SpiffeWorkloadAPIImplBase; import io.spiffe.workloadapi.grpc.Workload; +import org.junit.platform.commons.util.StringUtils; import java.io.IOException; import java.net.URI; @@ -22,13 +22,18 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.KeyPair; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; class FakeWorkloadApi extends SpiffeWorkloadAPIImplBase { - final String privateKey = "testdata/workloadapi/svid.key"; - final String svid = "testdata/workloadapi/svid.pem"; - final String x509Bundle = "testdata/workloadapi/bundle.pem"; + final String privateKey = "testdata/workloadapi/svid.key.der"; + final String svid = "testdata/workloadapi/svid.der"; + final String x509Bundle = "testdata/workloadapi/bundle.der"; final String jwtBundle = "testdata/workloadapi/bundle.json"; diff --git a/java-spiffe-core/src/test/java/io/spiffe/workloadapi/JwtSourceTest.java b/java-spiffe-core/src/test/java/io/spiffe/workloadapi/JwtSourceTest.java index dfe2b9d..bcabe86 100644 --- a/java-spiffe-core/src/test/java/io/spiffe/workloadapi/JwtSourceTest.java +++ b/java-spiffe-core/src/test/java/io/spiffe/workloadapi/JwtSourceTest.java @@ -24,7 +24,9 @@ import io.spiffe.workloadapi.internal.SecurityHeaderInterceptor; import java.io.IOException; import java.util.Arrays; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; class JwtSourceTest { diff --git a/java-spiffe-core/src/test/java/io/spiffe/workloadapi/WorkloadApiClientTest.java b/java-spiffe-core/src/test/java/io/spiffe/workloadapi/WorkloadApiClientTest.java index 99657fb..ef64705 100644 --- a/java-spiffe-core/src/test/java/io/spiffe/workloadapi/WorkloadApiClientTest.java +++ b/java-spiffe-core/src/test/java/io/spiffe/workloadapi/WorkloadApiClientTest.java @@ -6,33 +6,39 @@ import io.grpc.Server; import io.grpc.inprocess.InProcessChannelBuilder; import io.grpc.inprocess.InProcessServerBuilder; import io.grpc.testing.GrpcCleanupRule; +import io.spiffe.bundle.jwtbundle.JwtBundle; +import io.spiffe.bundle.jwtbundle.JwtBundleSet; +import io.spiffe.bundle.x509bundle.X509Bundle; import io.spiffe.exception.BundleNotFoundException; import io.spiffe.exception.JwtBundleException; import io.spiffe.exception.JwtSvidException; import io.spiffe.exception.SocketEndpointAddressException; import io.spiffe.spiffeid.SpiffeId; import io.spiffe.spiffeid.TrustDomain; -import org.junit.Rule; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import io.spiffe.bundle.jwtbundle.JwtBundle; -import io.spiffe.bundle.jwtbundle.JwtBundleSet; -import io.spiffe.bundle.x509bundle.X509Bundle; import io.spiffe.svid.jwtsvid.JwtSvid; import io.spiffe.utils.TestUtils; import io.spiffe.workloadapi.grpc.SpiffeWorkloadAPIGrpc; import io.spiffe.workloadapi.internal.ManagedChannelWrapper; import io.spiffe.workloadapi.internal.SecurityHeaderInterceptor; import io.spiffe.workloadapi.retry.BackoffPolicy; +import org.junit.Rule; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.security.KeyPair; -import java.util.*; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; class WorkloadApiClientTest { diff --git a/java-spiffe-core/src/test/java/io/spiffe/workloadapi/X509SourceTest.java b/java-spiffe-core/src/test/java/io/spiffe/workloadapi/X509SourceTest.java index acca80b..8a07a3d 100644 --- a/java-spiffe-core/src/test/java/io/spiffe/workloadapi/X509SourceTest.java +++ b/java-spiffe-core/src/test/java/io/spiffe/workloadapi/X509SourceTest.java @@ -22,7 +22,9 @@ import io.spiffe.workloadapi.internal.SecurityHeaderInterceptor; import java.io.IOException; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; class X509SourceTest { diff --git a/java-spiffe-core/src/test/java/io/spiffe/workloadapi/retry/BackoffPolicyTest.java b/java-spiffe-core/src/test/java/io/spiffe/workloadapi/retry/BackoffPolicyTest.java index 86870c5..3eb550b 100644 --- a/java-spiffe-core/src/test/java/io/spiffe/workloadapi/retry/BackoffPolicyTest.java +++ b/java-spiffe-core/src/test/java/io/spiffe/workloadapi/retry/BackoffPolicyTest.java @@ -4,7 +4,9 @@ import org.junit.jupiter.api.Test; import java.time.Duration; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; class BackoffPolicyTest { diff --git a/java-spiffe-core/src/test/resources/testdata/workloadapi/bundle.der b/java-spiffe-core/src/test/resources/testdata/workloadapi/bundle.der new file mode 100644 index 0000000000000000000000000000000000000000..56d05912e54a5a904cfefe2069976f098991c83a GIT binary patch literal 402 zcmXqLV(c?$Vl-L6%*4pV#K>U4#m1r4=5fxJg_+4f&XC)HlZ`o)g-w_#G}w^efEUE! z5@rhy@N{!?HINhMH8L#nku-cmm;(Yw|=O?*!s-gW-QmWYF!TN)THulc1~wsuWGj?6Ui zyzJ@Aug!0Nq`&@4j6}tk4__818zdU=0bMK0&&c?n1?U;ZHUoYTUl_z^HDCr(2C^Ul zJ{B<+krygfCocVbK27qsP?m;Z`i}cA{Id+?LDGUO5(Z*zBEBwQl?7?8*%B0BfE7?}!ai($5FUKC6;z#;XtPj>dYg_O%b5^fGMICjIaR-=G5i~K;fhYB0T&yGR-4B;TNY*}135!( z15P&PP!={}rqEzRegj?*hfA0(IKb1*&DB6ooY%;}z|_dhz{1GX$TAAfwKOs{wu~~6 zMb*z|zys0G3e+FuY7l84%*GD3jENCyD>EZIvl9bL>lOq4jWbL??^x_+LHdI literal 0 HcmV?d00001 diff --git a/java-spiffe-core/src/test/resources/testdata/workloadapi/svid.key b/java-spiffe-core/src/test/resources/testdata/workloadapi/svid.key deleted file mode 100644 index f5896fb..0000000 --- a/java-spiffe-core/src/test/resources/testdata/workloadapi/svid.key +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg+p4+LW7wmMYquxWg -Z75Bwl5dA+mIrfSRbD2+gQuZkuehRANCAASFtDAPsZg187ijRjpKPv78HfshnAVx -rgdoCkxIs3OgoPPVULfvPslALF3sWQrLUxzIk33dQ/P46o9LsweBN2Hs ------END PRIVATE KEY----- diff --git a/java-spiffe-core/src/test/resources/testdata/workloadapi/svid.key.der b/java-spiffe-core/src/test/resources/testdata/workloadapi/svid.key.der new file mode 100644 index 0000000000000000000000000000000000000000..56bbd6763c88168ed0bce404ce283931c3d0f77c GIT binary patch literal 138 zcmV;50CoQ`frkPC05B5<2P%e0&OHJF1_&yKNX|V20S5$aFlzz<0R$lWo<1#Z@R-Ib zyA_~kzCprXT?6Tet@M#>J-&epnUd$BL<2$q1ckIP53!gv^SGl%I!ZqN{2lutoCR^N s2WSdRNV9XGp!3yGx9>j5KrCJCSqjTj9LSS>-9z*E>W@pa2Z1+X>_(9}CIA2c literal 0 HcmV?d00001 diff --git a/java-spiffe-core/src/test/resources/testdata/workloadapi/svid.pem b/java-spiffe-core/src/test/resources/testdata/workloadapi/svid.pem deleted file mode 100644 index 78c1427..0000000 --- a/java-spiffe-core/src/test/resources/testdata/workloadapi/svid.pem +++ /dev/null @@ -1,13 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIB6zCCAZKgAwIBAgIRANwM7+XYWJtefDH9WtDULHkwCgYIKoZIzj0EAwIwHjEL -MAkGA1UEBhMCVVMxDzANBgNVBAoTBlNQSUZGRTAeFw0yMDA1MjYwODI1MjlaFw0y -MDA1MjYwOTI1MzlaMB0xCzAJBgNVBAYTAlVTMQ4wDAYDVQQKEwVTUElSRTBZMBMG -ByqGSM49AgEGCCqGSM49AwEHA0IABIW0MA+xmDXzuKNGOko+/vwd+yGcBXGuB2gK -TEizc6Cg89VQt+8+yUAsXexZCstTHMiTfd1D8/jqj0uzB4E3YeyjgbEwga4wDgYD -VR0PAQH/BAQDAgOoMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNV -HRMBAf8EAjAAMB0GA1UdDgQWBBQ+TzaTKiKtWiSN5vya0vck9T8OfjAfBgNVHSME -GDAWgBQEOa83CNDa8BcLL/mU3ep//rxyNjAvBgNVHREEKDAmhiRzcGlmZmU6Ly9l -eGFtcGxlLm9yZy93b3JrbG9hZC1zZXJ2ZXIwCgYIKoZIzj0EAwIDRwAwRAIgQEcc -FThD3vJxp6QpOGIJaWxxSF3B2JqF4A2nc0M3Vz8CICYg750Fw8GCr0K8+Ip5dAcV -0k1Or5t/ev63uOGy9oIz ------END CERTIFICATE----- diff --git a/java-spiffe-core/src/test/resources/testdata/x509svid/cert.der b/java-spiffe-core/src/test/resources/testdata/x509svid/cert.der new file mode 100644 index 0000000000000000000000000000000000000000..5bbc36909cec05a0e9591af9afb3e281db151282 GIT binary patch literal 495 zcmXqLVtj4T#5idIGZP~dlOV$~Tg}49H#TlMEFtUoV`fFG0T&yGR-4B;TNY*}135!( z15P&PP!={}rqEzRegj?*hfA0(IKb1*&DB6ooY%;}z|6?V(Ad<#*fEZIvl9c0RYu6v1kQN}{{R2{YH_*X({PzR z)}~DTn_L3td%c$Wp4)2ny+?9fm)kNaY1HUTB?=4eriQxZb43}UVc%!etCXTc20g`if(agQCVsc za>z5gGZ?rqDTrA=wY>cQoM(rV%;CA8waQ}6Gk$N9J=eY2FW5`-gXv=?1^t)7H!9RG fPuR*ZW&iDxeG@hoIQ-|lbjD*&a?~}k4*^pEU{aW$ literal 0 HcmV?d00001 diff --git a/java-spiffe-core/src/test/resources/testdata/x509svid/keyEC.der b/java-spiffe-core/src/test/resources/testdata/x509svid/keyEC.der new file mode 100644 index 0000000000000000000000000000000000000000..48966f3e0919b57816bf6c091a7d9fb8f8a64a3f GIT binary patch literal 138 zcmV;50CoQ`frkPC05B5<2P%e0&OHJF1_&yKNX|V20S5$aFlzz<0R$k7cV<(lCPs-< z_6el#0Vx}?bGrfm{{j7{bV);~BA0!kL<2$q1UhI`m0$^;!2kdA>Z5lNU4#m1r4=5fxJg_+4f&XC)HlZ`o)g-w_#G}w^efEUE! z5@rhy@N{!?HINhMH8L#nku-cmm;(Yw|=O?*!s-gW-QmWYF!TN)THulc1~wsuWGj?6Ui zyzJ@Aug!0Nq`&@4j6}tk4__818zdU=0bMK0&&c?n1?U;ZHUoYTUl_z^HDCr(2C^Ul zJ{B<+krygfCocVbK27qsP?m;Z`i}cA{Id+?LDGUO5(Z*zBEBwQl?7?8*%B0BfE7?}!ai($5FUKC6;z#;XtPj>dYg_O%b5^fGMICjIaR-=G5i~K;fhYB0T&yGR-4B;TNY*}135!( z15P&PP!={}rqEzRegj?*hfA0(IKb1*&DB6ooY%;}z|_dhz{1GX$TAAfwKOs{wu~~6 zMb*z|zys0G3e+FuY7l84%*GD3jENCyD>EZIvl9bL>lOq4jWbL??^x_+LHdI literal 0 HcmV?d00001 diff --git a/java-spiffe-helper/src/test/resources/testdata/svid.key.der b/java-spiffe-helper/src/test/resources/testdata/svid.key.der new file mode 100644 index 0000000000000000000000000000000000000000..56bbd6763c88168ed0bce404ce283931c3d0f77c GIT binary patch literal 138 zcmV;50CoQ`frkPC05B5<2P%e0&OHJF1_&yKNX|V20S5$aFlzz<0R$lWo<1#Z@R-Ib zyA_~kzCprXT?6Tet@M#>J-&epnUd$BL<2$q1ckIP53!gv^SGl%I!ZqN{2lutoCR^N s2WSdRNV9XGp!3yGx9>j5KrCJCSqjTj9LSS>-9z*E>W@pa2Z1+X>_(9}CIA2c literal 0 HcmV?d00001 diff --git a/java-spiffe-provider/src/test/java/io/spiffe/provider/SpiffeKeyManagerTest.java b/java-spiffe-provider/src/test/java/io/spiffe/provider/SpiffeKeyManagerTest.java index 4d3697b..52afe04 100644 --- a/java-spiffe-provider/src/test/java/io/spiffe/provider/SpiffeKeyManagerTest.java +++ b/java-spiffe-provider/src/test/java/io/spiffe/provider/SpiffeKeyManagerTest.java @@ -17,7 +17,9 @@ import java.nio.file.Paths; import java.security.cert.CertificateException; import static io.spiffe.provider.SpiffeProviderConstants.DEFAULT_ALIAS; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.Mockito.when; public class SpiffeKeyManagerTest { diff --git a/java-spiffe-provider/src/test/java/io/spiffe/provider/examples/mtls/HttpsServer.java b/java-spiffe-provider/src/test/java/io/spiffe/provider/examples/mtls/HttpsServer.java index eb787c5..547f160 100644 --- a/java-spiffe-provider/src/test/java/io/spiffe/provider/examples/mtls/HttpsServer.java +++ b/java-spiffe-provider/src/test/java/io/spiffe/provider/examples/mtls/HttpsServer.java @@ -2,10 +2,14 @@ package io.spiffe.provider.examples.mtls; import io.spiffe.exception.SocketEndpointAddressException; import io.spiffe.exception.X509SourceException; -import io.spiffe.provider.*; +import io.spiffe.provider.SpiffeKeyManager; +import io.spiffe.provider.SpiffeProviderException; +import io.spiffe.provider.SpiffeSslContextFactory; +import io.spiffe.provider.SpiffeSslContextFactory.SslContextOptions; +import io.spiffe.provider.SpiffeTrustManager; +import io.spiffe.provider.X509SourceManager; import io.spiffe.workloadapi.X509Source; import lombok.val; -import io.spiffe.provider.SpiffeSslContextFactory.SslContextOptions; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLServerSocket; diff --git a/java-spiffe-provider/src/test/java/io/spiffe/provider/examples/mtls/WorkloadThread.java b/java-spiffe-provider/src/test/java/io/spiffe/provider/examples/mtls/WorkloadThread.java index fc89e2d..0dc903c 100644 --- a/java-spiffe-provider/src/test/java/io/spiffe/provider/examples/mtls/WorkloadThread.java +++ b/java-spiffe-provider/src/test/java/io/spiffe/provider/examples/mtls/WorkloadThread.java @@ -7,7 +7,12 @@ import lombok.extern.java.Log; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; -import java.io.*; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; import java.security.cert.X509Certificate; import java.util.logging.Level;