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 <maxlambrecht@gmail.com>
This commit is contained in:
parent
538be3fa09
commit
c92c90e7ea
|
|
@ -188,20 +188,17 @@ public class JwtBundle implements BundleSource<JwtBundle> {
|
|||
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 publicKey;
|
||||
switch (family) {
|
||||
case EC:
|
||||
publicKey = ECKey.parse(jwk.toJSONString()).toPublicKey();
|
||||
}
|
||||
|
||||
if (Algorithm.Family.RSA.equals(family)) {
|
||||
break;
|
||||
case RSA:
|
||||
publicKey = RSAKey.parse(jwk.toJSONString()).toPublicKey();
|
||||
}
|
||||
|
||||
if (publicKey == null) {
|
||||
break;
|
||||
default:
|
||||
throw new KeyException(String.format("Key Type not supported: %s", jwk.getKeyType().getValue()));
|
||||
}
|
||||
|
||||
return publicKey;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<JwtBundle> {
|
|||
/**
|
||||
* 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<JwtBundle> bundles) {
|
||||
public static JwtBundleSet of(@NonNull final Collection<JwtBundle> bundles) {
|
||||
Map<TrustDomain, JwtBundle> bundleMap = new ConcurrentHashMap<>();
|
||||
for (JwtBundle bundle : bundles) {
|
||||
bundleMap.put(bundle.getTrustDomain(), bundle);
|
||||
|
|
|
|||
|
|
@ -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<X509Bundle> {
|
|||
/**
|
||||
* 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<X509Bundle> bundles) {
|
||||
public static X509BundleSet of(@NonNull final Collection<X509Bundle> bundles) {
|
||||
Map<TrustDomain, X509Bundle> bundleMap = new ConcurrentHashMap<>();
|
||||
for (X509Bundle bundle : bundles) {
|
||||
bundleMap.put(bundle.getTrustDomain(), bundle);
|
||||
|
|
|
|||
|
|
@ -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<? extends Certificate> 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<X509Certificate> chain, final List<X509Certificate> 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.
|
||||
* <p>
|
||||
* 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,11 +159,15 @@ 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)) {
|
||||
|
||||
switch (algorithm) {
|
||||
case RSA:
|
||||
verifyKeys(privateKey, x509Certificate.getPublicKey(), SHA_512_WITH_RSA);
|
||||
} else if (Algorithm.Family.EC.equals(algorithm)) {
|
||||
break;
|
||||
case EC:
|
||||
verifyKeys(privateKey, x509Certificate.getPublicKey(), SHA_512_WITH_ECDSA);
|
||||
} else {
|
||||
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<String> 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 {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
package io.spiffe.internal;
|
||||
|
||||
public enum KeyFileFormat {
|
||||
PEM,
|
||||
DER
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<SpiffeId> toListOfSpiffeIds(final String spiffeIds, final char separator) {
|
||||
if (isBlank(spiffeIds)) {
|
||||
return EMPTY_LIST;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
val array = spiffeIds.split(String.valueOf(separator));
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<X509Certificate> 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,9 +57,7 @@ class GrpcConversionUtils {
|
|||
List<X509Svid> 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())) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,17 @@ 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 {
|
||||
|
||||
|
|
@ -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<Arguments> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Arguments> 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));
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,33 +0,0 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIBjjCCATSgAwIBAgIBADAKBggqhkjOPQQDAjAeMQswCQYDVQQGEwJVUzEPMA0G
|
||||
A1UEChMGU1BJRkZFMB4XDTIwMDUxNjE3MDUyNFoXDTIwMDUyMzE3MDUzNFowHjEL
|
||||
MAkGA1UEBhMCVVMxDzANBgNVBAoTBlNQSUZGRTBZMBMGByqGSM49AgEGCCqGSM49
|
||||
AwEHA0IABCQyshZ+HhyfaordIzoupU4qFd07uTRNysO6z9i0WMGzhIAA06z6JXat
|
||||
rFBsHJYXbmuXp9afh+Ivr/RcGHj08PSjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNV
|
||||
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBToJDrI0vnnlhn7EmooEWe43+hPajAfBgNV
|
||||
HREEGDAWhhRzcGlmZmU6Ly9leGFtcGxlLm9yZzAKBggqhkjOPQQDAgNIADBFAiEA
|
||||
+mM9GONpM1L6QYw8c+IvWgXgr+aGoOVpmo0wWcZbc7oCIBiF1NN8p5DeU12wxoUy
|
||||
ycQCammceo4hcYLQAYGi/5Q5
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBjjCCATSgAwIBAgIBADAKBggqhkjOPQQDAjAeMQswCQYDVQQGEwJVUzEPMA0G
|
||||
A1UEChMGU1BJRkZFMB4XDTIwMDUyMDE3MDc1N1oXDTIwMDUyNzE3MDgwN1owHjEL
|
||||
MAkGA1UEBhMCVVMxDzANBgNVBAoTBlNQSUZGRTBZMBMGByqGSM49AgEGCCqGSM49
|
||||
AwEHA0IABO3/qXKapLzDi3wgqW8Lkjm35WrJclRr8aN7IF8Px2jeJpV4KG+wdLa7
|
||||
rXSOJH8xCotu9QnQcGo4FuinMsJPlZKjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNV
|
||||
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQEOa83CNDa8BcLL/mU3ep//rxyNjAfBgNV
|
||||
HREEGDAWhhRzcGlmZmU6Ly9leGFtcGxlLm9yZzAKBggqhkjOPQQDAgNIADBFAiBC
|
||||
RTRaKR1nphUMjFcLfopHk+VJgB97yZ8TEZRlNF8vLQIhAJchfcPmlOk9OFiAnSoU
|
||||
th2m6yJcLC3axw94n1fg0qcd
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBjjCCATSgAwIBAgIBADAKBggqhkjOPQQDAjAeMQswCQYDVQQGEwJVUzEPMA0G
|
||||
A1UEChMGU1BJRkZFMB4XDTIwMDUyNTExNDEyMVoXDTIwMDYwMTExNDEzMVowHjEL
|
||||
MAkGA1UEBhMCVVMxDzANBgNVBAoTBlNQSUZGRTBZMBMGByqGSM49AgEGCCqGSM49
|
||||
AwEHA0IABAED6MJ6JluKjEVjKiOP8gPgcqxdJpQKI7iJLDTTd8Ums1/bXTvUxQXG
|
||||
PmMcqYAtEvTgs1ew/FDSh5L8XNvaghWjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNV
|
||||
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQtAHWFv+CwKHD7G/VNm6oke6CTtTAfBgNV
|
||||
HREEGDAWhhRzcGlmZmU6Ly9leGFtcGxlLm9yZzAKBggqhkjOPQQDAgNIADBFAiAn
|
||||
VJkxslbz+KJMvsenGo9id3FllKxK1edi2gdyQay62gIhANK6B1ExwDYzUOB5KQUH
|
||||
XZg4m88DL41Jn2b6k+fQggVh
|
||||
-----END CERTIFICATE-----
|
||||
Binary file not shown.
|
|
@ -1,5 +0,0 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg+p4+LW7wmMYquxWg
|
||||
Z75Bwl5dA+mIrfSRbD2+gQuZkuehRANCAASFtDAPsZg187ijRjpKPv78HfshnAVx
|
||||
rgdoCkxIs3OgoPPVULfvPslALF3sWQrLUxzIk33dQ/P46o9LsweBN2Hs
|
||||
-----END PRIVATE KEY-----
|
||||
Binary file not shown.
|
|
@ -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-----
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -5,7 +5,12 @@ import io.spiffe.helper.keystore.KeyStoreHelper;
|
|||
import io.spiffe.helper.keystore.KeyStoreType;
|
||||
import lombok.extern.java.Log;
|
||||
import lombok.val;
|
||||
import org.apache.commons.cli.*;
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.CommandLineParser;
|
||||
import org.apache.commons.cli.DefaultParser;
|
||||
import org.apache.commons.cli.Option;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@ import java.nio.file.Paths;
|
|||
|
||||
class FakeWorkloadApi extends SpiffeWorkloadAPIImplBase {
|
||||
|
||||
final String privateKey = "testdata/svid.key";
|
||||
final String svid = "testdata/svid.pem";
|
||||
final String x509Bundle = "testdata/bundle.pem";
|
||||
final String privateKey = "testdata/svid.key.der";
|
||||
final String svid = "testdata/svid.der";
|
||||
final String x509Bundle = "testdata/bundle.der";
|
||||
|
||||
|
||||
// Loads cert, bundle and key from files and generates a X509SVIDResponse.
|
||||
|
|
|
|||
|
|
@ -30,7 +30,9 @@ import java.security.UnrecoverableKeyException;
|
|||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
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 KeyStoreHelperTest {
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,10 @@ import java.security.UnrecoverableKeyException;
|
|||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
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 KeyStoreTest {
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue