Adding tests to improve coverage for X509Svid, X509SvidValidator, X509BundleSet, SpiffeId, JwtSvid.

Signed-off-by: Max Lambrecht <maxlambrecht@gmail.com>
This commit is contained in:
Max Lambrecht 2020-05-18 12:11:47 -03:00
parent fa50d55dd0
commit 11d00e191c
20 changed files with 701 additions and 78 deletions

View File

@ -53,7 +53,7 @@ public class X509BundleSet implements X509BundleSource {
* @throws BundleNotFoundException if no bundle could be found for the given trust domain
*/
@Override
public X509Bundle getX509BundleForTrustDomain(final TrustDomain trustDomain) throws BundleNotFoundException {
public X509Bundle getX509BundleForTrustDomain(@NonNull final TrustDomain trustDomain) throws BundleNotFoundException {
val bundle = bundles.get(trustDomain);
if (bundle == null){
throw new BundleNotFoundException(String.format("No X509 bundle for trust domain %s", trustDomain));

View File

@ -101,11 +101,16 @@ public class CertificateUtils {
* @throws NoSuchAlgorithmException
* @throws CertPathValidatorException
*/
public static void validate(List<X509Certificate> chain, List<X509Certificate> trustedCerts) throws CertificateException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, CertPathValidatorException {
public static void validate(List<X509Certificate> chain, List<X509Certificate> trustedCerts) throws CertificateException, CertPathValidatorException {
val certificateFactory = getCertificateFactory();
val pkixParameters = toPkixParameters(trustedCerts);
val certPath = certificateFactory.generateCertPath(chain);
getCertPathValidator().validate(certPath, pkixParameters);
PKIXParameters pkixParameters = null;
try {
pkixParameters = toPkixParameters(trustedCerts);
val certPath = certificateFactory.generateCertPath(chain);
getCertPathValidator().validate(certPath, pkixParameters);
} catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException e) {
throw new CertificateException(e);
}
}
/**
@ -151,28 +156,32 @@ public class CertificateUtils {
*
* @throws InvalidKeyException if the keys don't match
*/
public static void validatePrivateKey(PrivateKey privateKey, X509Certificate x509Certificate) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException {
public static void validatePrivateKey(PrivateKey privateKey, X509Certificate x509Certificate) throws InvalidKeyException {
// create a challenge
byte[] challenge = new byte[1000];
ThreadLocalRandom.current().nextBytes(challenge);
Signature sig = null;
if ("RSA".equals(privateKey.getAlgorithm())) {
sig = Signature.getInstance("SHA256withRSA");
} else {
sig = Signature.getInstance("SHA1withECDSA");
}
try {
if ("RSA".equals(privateKey.getAlgorithm())) {
sig = Signature.getInstance("SHA256withRSA");
} else {
sig = Signature.getInstance("SHA1withECDSA");
}
sig.initSign(privateKey);
sig.update(challenge);
byte[] signature = sig.sign();
sig.initSign(privateKey);
sig.update(challenge);
byte[] signature = sig.sign();
sig.initVerify(x509Certificate.getPublicKey());
sig.update(challenge);
sig.initVerify(x509Certificate.getPublicKey());
sig.update(challenge);
if (!sig.verify(signature)) {
throw new InvalidKeyException("Private Key does not match Certificate Public Key");
if (!sig.verify(signature)) {
throw new InvalidKeyException("Private Key does not match Certificate Public Key");
}
} catch (SignatureException | NoSuchAlgorithmException e) {
throw new IllegalStateException("Could not validate private keys", e);
}
}

View File

@ -58,7 +58,7 @@ public class SpiffeId {
throw new IllegalArgumentException("SPIFFE ID cannot be empty");
}
val uri = URI.create(spiffeIdAsString);
val uri = URI.create(normalize(spiffeIdAsString));
if (!SPIFFE_SCHEME.equals(uri.getScheme())) {
throw new IllegalArgumentException("Invalid SPIFFE schema");

View File

@ -1,6 +1,7 @@
package spiffe.spiffeid;
import lombok.val;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.nio.file.Files;
@ -11,6 +12,7 @@ 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;
/**
@ -24,7 +26,8 @@ public class SpiffeIdUtils {
* Reads the Accepted SPIFFE IDs from a system property and parses them to {@link SpiffeId} instances.
*
* @param systemProperty name of the system property that contains a list of SPIFFE IDs separated by a commas.
* @return a list of {@link SpiffeId} parsed from the values read from the security property
* @return a list of {@link SpiffeId} parsed from the values read from the security property, in case there's no values
* in the System property, it returns an emtpy list
*
* @throws IllegalArgumentException if the given system property is empty or if any of the SPIFFE IDs
* cannot be parsed
@ -35,6 +38,9 @@ public class SpiffeIdUtils {
}
val spiffeIds = System.getProperty(systemProperty);
if (StringUtils.isBlank(spiffeIds)) {
return EMPTY_LIST;
}
return toListOfSpiffeIds(spiffeIds, DEFAULT_CHAR_SEPARATOR);
}
@ -52,7 +58,12 @@ public class SpiffeIdUtils {
if (isBlank(securityProperty)) {
throw new IllegalArgumentException("Argument securityProperty cannot be empty");
}
val spiffeIds = Security.getProperty(securityProperty);
if (StringUtils.isBlank(spiffeIds)) {
return EMPTY_LIST;
}
return toListOfSpiffeIds(spiffeIds, DEFAULT_CHAR_SEPARATOR);
}
@ -88,7 +99,7 @@ public class SpiffeIdUtils {
*/
public static List<SpiffeId> toListOfSpiffeIds(final String spiffeIds, final char separator) {
if (isBlank(spiffeIds)) {
throw new IllegalArgumentException("Argument spiffeIds cannot be emtpy");
throw new IllegalArgumentException("Argument spiffeIds cannot be empty");
}
val array = spiffeIds.split(String.valueOf(separator));

View File

@ -77,7 +77,7 @@ public class JwtSvid {
* when the algorithm is not supported, when the header 'kid' is missing, when the signature cannot be verified, or
* when the 'aud' claim has an audience that is not in the audience list provided as parameter
*
* @throws IllegalArgumentException when the token cannot be parsed
* @throws IllegalArgumentException when the token is blank or cannot be parsed
*
* @throws BundleNotFoundException if the bundle for the trust domain of the spiffe id from the 'sub' cannot be found
* in the JwtBundleSource
@ -93,6 +93,10 @@ public class JwtSvid {
// to find the Authority in the jwtBundleSource. Once the Authority
// is found, the token signature is verified
if (StringUtils.isBlank(token)) {
throw new IllegalArgumentException("Token cannot be blank");
}
Jwt<?, ?> jwt = decodeToken(token);
Claims claims = (Claims) jwt.getBody();
List<String> aud = claims.get("aud", List.class);
@ -130,7 +134,11 @@ public class JwtSvid {
* the 'aud' has an audience that is not in the audience provided as parameter
* @throws IllegalArgumentException when the token cannot be parsed
*/
public static JwtSvid parseInsecure(@NonNull final String token, List<String> audience) throws JwtSvidException {
public static JwtSvid parseInsecure(@NonNull final String token, @NonNull List<String> audience) throws JwtSvidException {
if (StringUtils.isBlank(token)) {
throw new IllegalArgumentException("Token cannot be blank");
}
Jwt<?, ?> jwt = decodeToken(token);
Claims claims = (Claims) jwt.getBody();
List<String> aud = claims.get("aud", List.class);
@ -160,7 +168,7 @@ public class JwtSvid {
return new Date(expiry.getTime());
}
private static void verifySignature(@NonNull String token, String keyId, PublicKey jwtAuthority) throws JwtSvidException {
private static void verifySignature(String token, String keyId, PublicKey jwtAuthority) throws JwtSvidException {
JwtParser jwtParser = Jwts.parserBuilder().setSigningKey(jwtAuthority).build();
try {
// parse token with signature verification using the jwt authority (public key)

View File

@ -12,7 +12,6 @@ import java.nio.file.Path;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
@ -41,9 +40,9 @@ public class X509Svid implements X509SvidSource {
PrivateKey privateKey;
private X509Svid(
@NonNull SpiffeId spiffeId,
@NonNull List<X509Certificate> chain,
@NonNull PrivateKey privateKey) {
SpiffeId spiffeId,
List<X509Certificate> chain,
PrivateKey privateKey) {
this.spiffeId = spiffeId;
this.chain = chain;
this.privateKey = privateKey;
@ -163,8 +162,6 @@ public class X509Svid implements X509SvidSource {
CertificateUtils.validatePrivateKey(privateKey, x509Certificates.get(0));
} catch (InvalidKeyException e) {
throw new X509SvidException("Private Key does not match Certificate Public Key", e);
} catch (NoSuchAlgorithmException | SignatureException e) {
throw new IllegalStateException("Could not validate private key", e);
}
}

View File

@ -7,8 +7,6 @@ import spiffe.exception.BundleNotFoundException;
import spiffe.internal.CertificateUtils;
import spiffe.spiffeid.SpiffeId;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
@ -42,8 +40,6 @@ public class X509SvidValidator {
CertificateUtils.validate(chain, new ArrayList<>(x509Bundle.getX509Authorities()));
} catch (CertPathValidatorException e) {
throw new CertificateException("Cert chain cannot be verified", e);
} catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException e) {
throw new CertificateException(e.getMessage(), e);
}
}

View File

@ -0,0 +1,135 @@
package spiffe.bundle.x509bundle;
import org.junit.jupiter.api.Test;
import spiffe.exception.BundleNotFoundException;
import spiffe.internal.DummyX509Certificate;
import spiffe.spiffeid.TrustDomain;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
class X509BundleSetTest {
@Test
void testOf_listOfBundles_Success() {
X509Bundle x509Bundle1 = new X509Bundle(TrustDomain.of("example.org"));
X509Bundle x509Bundle2 = new X509Bundle(TrustDomain.of("other.org"));
List<X509Bundle> bundleList = Arrays.asList(x509Bundle1, x509Bundle2);
X509BundleSet bundleSet = X509BundleSet.of(bundleList);
assertTrue(bundleSet.getBundles().contains(x509Bundle1));
assertTrue(bundleSet.getBundles().contains(x509Bundle2));
}
@Test
void testOf_null_throwsNullPointerException() {
try {
X509BundleSet.of(null);
fail("should have thrown exception");
} catch (NullPointerException e) {
assertEquals("bundles is marked non-null but is null", e.getMessage());
}
}
@Test
void testAdd() {
X509Bundle x509Bundle1 = new X509Bundle(TrustDomain.of("example.org"));
List<X509Bundle> bundleList = Collections.singletonList(x509Bundle1);
X509BundleSet bundleSet = X509BundleSet.of(bundleList);
X509Bundle x509Bundle2 = new X509Bundle(TrustDomain.of("other.org"));
bundleSet.add(x509Bundle2);
assertTrue(bundleSet.getBundles().contains(x509Bundle1));
assertTrue(bundleSet.getBundles().contains(x509Bundle2));
}
@Test
void testAdd_sameBundleAgain_noDuplicate() {
X509Bundle x509Bundle1 = new X509Bundle(TrustDomain.of("example.org"));
List<X509Bundle> bundleList = Collections.singletonList(x509Bundle1);
X509BundleSet bundleSet = X509BundleSet.of(bundleList);
bundleSet.add(x509Bundle1);
assertTrue(bundleSet.getBundles().contains(x509Bundle1));
assertEquals(1, bundleSet.getBundles().size());
}
@Test
void testAdd_aDifferentBundleForSameTrustDomain_replacesWithNewBundle() {
X509Bundle x509Bundle1 = new X509Bundle(TrustDomain.of("example.org"));
List<X509Bundle> bundleList = Collections.singletonList(x509Bundle1);
X509BundleSet bundleSet = X509BundleSet.of(bundleList);
X509Bundle x509Bundle2 = new X509Bundle(TrustDomain.of("example.org"));
x509Bundle2.addX509Authority(new DummyX509Certificate());
bundleSet.add(x509Bundle2);
assertTrue(bundleSet.getBundles().contains(x509Bundle2));
assertFalse(bundleSet.getBundles().contains(x509Bundle1));
assertEquals(1, bundleSet.getBundles().size());
}
@Test
void testAdd_nullBundle_throwsNullPointerException() {
X509Bundle x509Bundle1 = new X509Bundle(TrustDomain.of("example.org"));
List<X509Bundle> bundleList = Collections.singletonList(x509Bundle1);
X509BundleSet bundleSet = X509BundleSet.of(bundleList);
try {
bundleSet.add(null);
fail("should have thrown exception");
} catch (NullPointerException e) {
assertEquals("x509Bundle is marked non-null but is null", e.getMessage());
}
}
@Test
void testGetX509BundleForTrustDomain_Success() throws BundleNotFoundException {
X509Bundle x509Bundle1 = new X509Bundle(TrustDomain.of("example.org"));
X509Bundle x509Bundle2 = new X509Bundle(TrustDomain.of("other.org"));
List<X509Bundle> bundleList = Arrays.asList(x509Bundle1, x509Bundle2);
X509BundleSet bundleSet = X509BundleSet.of(bundleList);
assertEquals(x509Bundle1, bundleSet.getX509BundleForTrustDomain(TrustDomain.of("example.org")));
assertEquals(x509Bundle2, bundleSet.getX509BundleForTrustDomain(TrustDomain.of("other.org")));
}
@Test
void testGetX509BundleForTrustDomain_notFoundTrustDomain() {
X509Bundle x509Bundle1 = new X509Bundle(TrustDomain.of("example.org"));
X509Bundle x509Bundle2 = new X509Bundle(TrustDomain.of("other.org"));
List<X509Bundle> bundleList = Arrays.asList(x509Bundle1, x509Bundle2);
X509BundleSet bundleSet = X509BundleSet.of(bundleList);
try {
bundleSet.getX509BundleForTrustDomain(TrustDomain.of("unknown.org"));
fail("expected BundleNotFoundException");
} catch (BundleNotFoundException e) {
assertEquals("No X509 bundle for trust domain unknown.org", e.getMessage());
}
}
@Test
void testGetX509BundleForTrustDomain_nullTrustDomain_throwsException() throws BundleNotFoundException {
X509Bundle x509Bundle1 = new X509Bundle(TrustDomain.of("example.org"));
X509Bundle x509Bundle2 = new X509Bundle(TrustDomain.of("other.org"));
List<X509Bundle> bundleList = Arrays.asList(x509Bundle1, x509Bundle2);
X509BundleSet bundleSet = X509BundleSet.of(bundleList);
try {
bundleSet.getX509BundleForTrustDomain(null);
fail("expected exception");
} catch (NullPointerException e) {
assertEquals("trustDomain is marked non-null but is null", e.getMessage());
}
}
@Test
void getBundles() {
}
}

View File

@ -7,6 +7,7 @@ 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 spiffe.exception.BundleNotFoundException;
import spiffe.internal.DummyX509Certificate;
import spiffe.spiffeid.TrustDomain;
@ -26,12 +27,42 @@ import static org.junit.jupiter.api.Assertions.*;
public class X509BundleTest {
@Test
void TestNewBunlde() {
void TestNewBundle() {
X509Bundle x509Bundle = new X509Bundle(TrustDomain.of("example.org"));
assertEquals(0, x509Bundle.getX509Authorities().size());
assertEquals(TrustDomain.of("example.org"), x509Bundle.getTrustDomain());
}
@Test
void testNewBundle_nullTrustDomain_throwsNullPointerException() {
try {
new X509Bundle(null );
fail("should have thrown exception");
} catch (NullPointerException e) {
assertEquals("trustDomain is marked non-null but is null", e.getMessage());
}
}
@Test
void testNewBundleWithAuthorities_nullTrustDomain_throwsNullPointerException() {
try {
new X509Bundle(null, new HashSet<>());
fail("should have thrown exception");
} catch (NullPointerException e) {
assertEquals("trustDomain is marked non-null but is null", e.getMessage());
}
}
@Test
void testNewBundleAuthorities_nullAuthorities_throwsNullPointerException() {
try {
new X509Bundle(TrustDomain.of("example.org"), null);
fail("should have thrown exception");
} catch (NullPointerException e) {
assertEquals("x509Authorities is marked non-null but is null", e.getMessage());
}
}
@Test
void TestFromAuthorities() {
X509Certificate x509Cert1 = new DummyX509Certificate();
@ -46,10 +77,26 @@ public class X509BundleTest {
assertEquals(authorities, x509Bundle.getX509Authorities());
}
@Test
void testGetX509BundleForTrustDomain() throws BundleNotFoundException {
X509Bundle x509Bundle = new X509Bundle(TrustDomain.of("example.org"));
assertEquals(x509Bundle, x509Bundle.getX509BundleForTrustDomain(TrustDomain.of("example.org")));
}
@Test
void testGetX509BundleForTrustDomain_notBundleFound_throwsBundleNotFoundException() {
X509Bundle x509Bundle = new X509Bundle(TrustDomain.of("example.org"));
try {
x509Bundle.getX509BundleForTrustDomain(TrustDomain.of("other.org"));
} catch (BundleNotFoundException e) {
assertEquals("No X509 bundle found for trust domain other.org", e.getMessage());
}
}
@Test
void TestLoad_Succeeds() {
try {
X509Bundle x509Bundle = X509Bundle.load(TrustDomain.of("example.org"), Paths.get(loadResource("testdata/x509bundle/certs.pem")));
X509Bundle x509Bundle = X509Bundle.load(TrustDomain.of("example.org"), Paths.get(toUri("testdata/x509bundle/certs.pem")));
assertEquals(2, x509Bundle.getX509Authorities().size());
} catch (IOException | CertificateException | URISyntaxException e) {
fail(e);
@ -59,24 +106,64 @@ public class X509BundleTest {
@Test
void TestLoad_Fails() {
try {
X509Bundle x509Bundle = X509Bundle.load(TrustDomain.of("example.org"), Paths.get("testdata/x509bundle/non-existent.pem"));
X509Bundle.load(TrustDomain.of("example.org"), Paths.get("testdata/x509bundle/non-existent.pem"));
fail("should have thrown exception");
} catch (IOException | CertificateException e) {
assertEquals("Unable to load X.509 bundle file", e.getMessage());
}
}
@Test
void testLoad_nullTrustDomain_throwsNullPointerException() throws IOException, CertificateException {
try {
X509Bundle.load(null,Paths.get("testdata/x509bundle/non-existent.pem"));
fail("should have thrown exception");
} catch (NullPointerException e) {
assertEquals("trustDomain is marked non-null but is null", e.getMessage());
}
}
@Test
void testLoad_nullBundlePath_throwsNullPointerException() throws IOException, CertificateException {
try {
X509Bundle.load(TrustDomain.of("example.org"), null);
fail("should have thrown exception");
} catch (NullPointerException e) {
assertEquals("bundlePath is marked non-null but is null", e.getMessage());
}
}
@Test
void testParse_nullTrustDomain_throwsNullPointerException() throws IOException, CertificateException {
try {
X509Bundle.parse(null, "bytes".getBytes());
fail("should have thrown exception");
} catch (NullPointerException e) {
assertEquals("trustDomain is marked non-null but is null", e.getMessage());
}
}
@Test
void testParse_nullBundlePath_throwsNullPointerException() throws IOException, CertificateException {
try {
X509Bundle.parse(TrustDomain.of("example.org"), null);
fail("should have thrown exception");
} catch (NullPointerException e) {
assertEquals("bundleBytes is marked non-null but is null", e.getMessage());
}
}
@Test
void TestX509AuthoritiesCRUD() {
X509Bundle bundle1 = null;
X509Bundle bundle2 = null;
try {
// Load bundle1, which contains a single certificate
bundle1 = X509Bundle.load(TrustDomain.of("example.org"), Paths.get(loadResource("testdata/x509bundle/cert.pem")));
bundle1 = X509Bundle.load(TrustDomain.of("example.org"), Paths.get(toUri("testdata/x509bundle/cert.pem")));
// Load bundle2, which contains 2 certificates
// The first certificate is the same than the one used in bundle1
bundle2 = X509Bundle.load(TrustDomain.of("example.org"), Paths.get(loadResource("testdata/x509bundle/certs.pem")));
bundle2 = X509Bundle.load(TrustDomain.of("example.org"), Paths.get(toUri("testdata/x509bundle/certs.pem")));
} catch (IOException | CertificateException | URISyntaxException e) {
fail(e);
}
@ -115,7 +202,7 @@ public class X509BundleTest {
@MethodSource("provideX509BundleScenarios")
void parseX509Bundle(TestCase testCase) {
try {
Path path = Paths.get(loadResource(testCase.path));
Path path = Paths.get(toUri(testCase.path));
byte[] bytes = Files.readAllBytes(path);
X509Bundle x509Bundle = X509Bundle.parse(testCase.trustDomain, bytes);
@ -205,7 +292,7 @@ public class X509BundleTest {
}
}
private URI loadResource(String path) throws URISyntaxException {
private URI toUri(String path) throws URISyntaxException {
return getClass().getClassLoader().getResource(path).toURI();
}
}

View File

@ -9,8 +9,6 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
@ -23,7 +21,7 @@ public class CertificateUtilsTest {
@Test
void generateCertificates_ofPEMByteArray_returnsListWithOneX509Certificate() throws IOException, URISyntaxException {
val path = Paths.get(loadResource("testdata/internal/cert.pem"));
val path = Paths.get(toUri("testdata/internal/cert.pem"));
val certBytes = Files.readAllBytes(path);
List<X509Certificate> x509CertificateList = null;
@ -40,8 +38,8 @@ public class CertificateUtilsTest {
@Test
void validate_certificateThatIsExpired_throwsCertificateException() throws IOException, CertificateException, URISyntaxException {
val certPath = Paths.get(loadResource("testdata/internal/cert2.pem"));
val certBundle = Paths.get(loadResource("testdata/internal/bundle.pem"));
val certPath = Paths.get(toUri("testdata/internal/cert2.pem"));
val certBundle = Paths.get(toUri("testdata/internal/bundle.pem"));
val certBytes = Files.readAllBytes(certPath);
val bundleBytes = Files.readAllBytes(certBundle);
@ -52,12 +50,12 @@ public class CertificateUtilsTest {
try {
CertificateUtils.validate(chain, trustedCert);
fail("Expected exception");
} catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException | CertPathValidatorException e) {
} catch (CertPathValidatorException e) {
assertEquals("validity check failed", e.getMessage());
}
}
private URI loadResource(String path) throws URISyntaxException {
private URI toUri(String path) throws URISyntaxException {
return getClass().getClassLoader().getResource(path).toURI();
}
}

View File

@ -95,9 +95,21 @@ public class SpiffeIdTest {
() -> assertEquals("trust-domain.org", spiffeId.getTrustDomain().toString()),
() -> assertEquals("/path1/path2", spiffeId.getPath())
);
}
@Test
void parse_aStringContainingLeadingAndTrailingBlanks_ReturnsASpiffeIdThatHasTrustDomainAndPathSegments() {
val spiffeIdAsString = " spiffe://trust-domain.org/path1/path2 ";
val spiffeId = SpiffeId.parse(spiffeIdAsString);
assertAll("SpiffeId",
() -> assertEquals("trust-domain.org", spiffeId.getTrustDomain().toString()),
() -> assertEquals("/path1/path2", spiffeId.getPath())
);
}
@Test
void parse_aStringContainingInvalidSchema_throwsIllegalArgumentException() {
val invalidadSpiffeId = "siffe://trust-domain.org/path1/path2";
@ -121,7 +133,27 @@ public class SpiffeIdTest {
}
@Test
void of_nullTrustDomain_throwsIllegalArgumentException() {
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");

View File

@ -0,0 +1,154 @@
package spiffe.spiffeid;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.Security;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
class piffeIdUtilsTest {
@Test
void getSpiffeIdsFromSystemProperty() {
System.setProperty("spiffe.property", " spiffe://example.org/workload1, spiffe://example.org/workload2 ");
List<SpiffeId> spiffeIdList = SpiffeIdUtils.getSpiffeIdsFromSystemProperty("spiffe.property");
assertNotNull(spiffeIdList);
assertEquals(2, spiffeIdList.size());
assertEquals(SpiffeId.parse("spiffe://example.org/workload1"), spiffeIdList.get(0));
assertEquals(SpiffeId.parse("spiffe://example.org/workload2"), spiffeIdList.get(1));
}
@Test
void getSpiffeIdsFromSystemPropertyThatHasNoValue_returnsEmptyList() {
System.setProperty("spiffe.property", "");
List<SpiffeId> spiffeIdList = SpiffeIdUtils.getSpiffeIdsFromSystemProperty("spiffe.property");
assertNotNull(spiffeIdList);
assertEquals(0, spiffeIdList.size());
}
@Test
void getSpiffeIdsFromBlankSystemProperty_throwsIllegalArgumentException() {
try {
SpiffeIdUtils.getSpiffeIdsFromSystemProperty("");
fail("should have thrown exception");
} catch (IllegalArgumentException e) {
//expected
}
}
@Test
void getSpiffeIdsFromNullSystemProperty_throwsIllegalArgumentException() {
try {
SpiffeIdUtils.getSpiffeIdsFromSystemProperty(null);
fail("should have thrown exception");
} catch (IllegalArgumentException e) {
//expected
}
}
@Test
void getSpiffeIdsFromSecurityProperty() {
Security.setProperty("spiffe.property", " spiffe://example.org/workload1, spiffe://example.org/workload2 ");
List<SpiffeId> spiffeIdList = SpiffeIdUtils.getSpiffeIdsFromSecurityProperty("spiffe.property");
assertNotNull(spiffeIdList);
assertEquals(2, spiffeIdList.size());
assertEquals(SpiffeId.parse("spiffe://example.org/workload1"), spiffeIdList.get(0));
assertEquals(SpiffeId.parse("spiffe://example.org/workload2"), spiffeIdList.get(1));
}
@Test
void getSpiffeIdsFromSecurityPropertyThatHasNoValue_returnsEmptyList() {
Security.setProperty("spiffe.property", "");
List<SpiffeId> spiffeIdList = SpiffeIdUtils.getSpiffeIdsFromSecurityProperty("spiffe.property");
assertNotNull(spiffeIdList);
assertEquals(0, spiffeIdList.size());
}
@Test
void getSpiffeIdsFromBlankSecurityProperty_throwsIllegalArgumentException() {
try {
SpiffeIdUtils.getSpiffeIdsFromSecurityProperty("");
fail("should have thrown exception");
} catch (IllegalArgumentException e) {
//expected
}
}
@Test
void getSpiffeIdsFromNullSecurityProperty_throwsIllegalArgumentException() {
try {
SpiffeIdUtils.getSpiffeIdsFromSecurityProperty(null);
fail("should have thrown exception");
} catch (IllegalArgumentException e) {
//expected
}
}
@Test
void getSpiffeIdListFromFile() throws URISyntaxException {
Path path = Paths.get(toUri("testdata/spiffeid/spiffeIds.txt"));
try {
List<SpiffeId> spiffeIdList = SpiffeIdUtils.getSpiffeIdListFromFile(path);
assertNotNull(spiffeIdList);
assertEquals(3, spiffeIdList.size());
assertEquals(SpiffeId.parse("spiffe://example.org/workload1"), spiffeIdList.get(0));
assertEquals(SpiffeId.parse("spiffe://example.org/workload2"), spiffeIdList.get(1));
assertEquals(SpiffeId.parse("spiffe://example2.org/workload1"), spiffeIdList.get(2));
} catch (IOException e) {
fail(e);
}
}
@Test
void getSpiffeIdListFromNonExistenFile_throwsException() throws IOException {
Path path = Paths.get("testdata/spiffeid/non-existent-file");
try {
SpiffeIdUtils.getSpiffeIdListFromFile(path);
fail("should have thrown exception");
} catch (NoSuchFileException e) {
assertEquals("testdata/spiffeid/non-existent-file", e.getMessage());
}
}
@Test
void toListOfSpiffeIds() {
String spiffeIdsAsString = " spiffe://example.org/workload1, spiffe://example.org/workload2 ";
List<SpiffeId> spiffeIdList = SpiffeIdUtils.toListOfSpiffeIds(spiffeIdsAsString, ',');
assertNotNull(spiffeIdList);
assertEquals(2, spiffeIdList.size());
assertEquals(SpiffeId.parse("spiffe://example.org/workload1"), spiffeIdList.get(0));
assertEquals(SpiffeId.parse("spiffe://example.org/workload2"), spiffeIdList.get(1));
}
@Test
void toListOfSPiffeIds_blankStringParameter() {
try {
SpiffeIdUtils.toListOfSpiffeIds("", ',');
} catch (IllegalArgumentException e) {
assertEquals("Argument spiffeIds cannot be empty", e.getMessage());
}
}
private URI toUri(String path) throws URISyntaxException {
return getClass().getClassLoader().getResource(path).toURI();
}
}

View File

@ -7,6 +7,7 @@ import io.jsonwebtoken.impl.DefaultClaims;
import io.jsonwebtoken.security.Keys;
import lombok.Builder;
import lombok.Value;
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;
@ -34,7 +35,7 @@ class JwtSvidParseAndValidateTest {
@ParameterizedTest
@MethodSource("provideJwtScenarios")
void parseJwt(TestCase testCase) {
void parseAndValidateJwt(TestCase testCase) {
try {
String token = testCase.generateToken.get();
@ -44,13 +45,61 @@ class JwtSvidParseAndValidateTest {
assertEquals(testCase.expectedJwtSvid.getAudience(), jwtSvid.getAudience());
assertEquals(testCase.expectedJwtSvid.getExpiry().toInstant().getEpochSecond(), jwtSvid.getExpiry().toInstant().getEpochSecond());
assertEquals(token, jwtSvid.getToken());
assertEquals(token, jwtSvid.marshall());
} catch (Exception e) {
assertEquals(testCase.expectedException.getClass(), e.getClass());
assertEquals(testCase.expectedException.getMessage(), e.getMessage());
}
}
@Test
void testParseAndValidate_nullToken_throwsNullPointerException() throws JwtSvidException, AuthorityNotFoundException, BundleNotFoundException {
TrustDomain trustDomain = TrustDomain.of("test.domain");
JwtBundle jwtBundle = new JwtBundle(trustDomain);
List<String> audience = Collections.singletonList("audience");
try {
JwtSvid.parseAndValidate(null, jwtBundle, audience);
} catch (NullPointerException e) {
assertEquals("token is marked non-null but is null", e.getMessage());
}
}
@Test
void testParseAndValidate_emptyToken_throwsIllegalArgumentException() throws JwtSvidException, AuthorityNotFoundException, BundleNotFoundException {
TrustDomain trustDomain = TrustDomain.of("test.domain");
JwtBundle jwtBundle = new JwtBundle(trustDomain);
List<String> audience = Collections.singletonList("audience");
try {
JwtSvid.parseAndValidate("", jwtBundle, audience);
} catch (IllegalArgumentException e) {
assertEquals("Token cannot be blank", e.getMessage());
}
}
@Test
void testParseAndValidate_nullBundle_throwsNullPointerException() throws JwtSvidException, AuthorityNotFoundException, BundleNotFoundException {
List<String> audience = Collections.singletonList("audience");
try {
JwtSvid.parseAndValidate("token", null, audience);
} catch (NullPointerException e) {
assertEquals("jwtBundleSource is marked non-null but is null", e.getMessage());
}
}
@Test
void testParseAndValidate_nullAudience_throwsNullPointerException() throws JwtSvidException, AuthorityNotFoundException, BundleNotFoundException {
TrustDomain trustDomain = TrustDomain.of("test.domain");
JwtBundle jwtBundle = new JwtBundle(trustDomain);
List<String> audience = Collections.singletonList("audience");
try {
JwtSvid.parseAndValidate("token", jwtBundle, null);
} catch (NullPointerException e) {
assertEquals("audience is marked non-null but is null", e.getMessage());
}
}
static Stream<Arguments> provideJwtScenarios() {
KeyPair key1 = Keys.keyPairFor(SignatureAlgorithm.ES384);

View File

@ -7,6 +7,7 @@ import io.jsonwebtoken.impl.DefaultClaims;
import io.jsonwebtoken.security.Keys;
import lombok.Builder;
import lombok.Value;
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;
@ -49,6 +50,42 @@ class JwtSvidParseInsecureTest {
}
@Test
void testParseInsecure_nullToken_throwsNullPointerException() throws JwtSvidException {
List<String> audience = Collections.singletonList("audience");
try {
JwtSvid.parseInsecure(null, audience);
} catch (NullPointerException e) {
assertEquals("token is marked non-null but is null", e.getMessage());
}
}
@Test
void testParseAndValidate_emptyToken_throwsIllegalArgumentException() throws JwtSvidException {
List<String> audience = Collections.singletonList("audience");
try {
JwtSvid.parseInsecure("", audience);
} catch (IllegalArgumentException e) {
assertEquals("Token cannot be blank", e.getMessage());
}
}
@Test
void testParseInsecure_nullAudience_throwsNullPointerException() throws JwtSvidException {
try {
KeyPair key1 = Keys.keyPairFor(SignatureAlgorithm.ES384);
TrustDomain trustDomain = TrustDomain.of("test.domain");
SpiffeId spiffeId = trustDomain.newSpiffeId("host");
List<String> audience = Collections.singletonList("audience");
Date expiration = new Date(System.currentTimeMillis() + 3600000);
Claims claims = buildClaims(audience, spiffeId.toString(), expiration);
JwtSvid.parseInsecure(generateToken(claims, key1, "authority1"), null);
} catch (NullPointerException e) {
assertEquals("audience is marked non-null but is null", e.getMessage());
}
}
static Stream<Arguments> provideJwtScenarios() {
KeyPair key1 = Keys.keyPairFor(SignatureAlgorithm.ES384);

View File

@ -16,6 +16,7 @@ import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.cert.X509Certificate;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.*;
@ -38,8 +39,8 @@ public class X509SvidTest {
@Test
void testLoad_Success() throws URISyntaxException {
Path certPath = Paths.get(loadResource(certSingle));
Path keyPath = Paths.get(loadResource(keyRSA));
Path certPath = Paths.get(toUri(certSingle));
Path keyPath = Paths.get(toUri(keyRSA));
try {
X509Svid x509Svid = X509Svid.load(certPath, keyPath);
assertEquals("spiffe://example.org/workload-1", x509Svid.getSpiffeId().toString());
@ -50,7 +51,7 @@ public class X509SvidTest {
@Test
void testLoad_FailsCannotReadCertFile() throws URISyntaxException {
Path keyPath = Paths.get(loadResource(keyRSA));
Path keyPath = Paths.get(toUri(keyRSA));
try {
X509Svid.load(Paths.get("not-existent-cert"), keyPath);
fail("should have thrown IOException");
@ -61,7 +62,7 @@ public class X509SvidTest {
@Test
void testLoad_FailsCannotReadKeyFile() throws URISyntaxException {
Path certPath = Paths.get(loadResource(certSingle));
Path certPath = Paths.get(toUri(certSingle));
try {
X509Svid.load(certPath, Paths.get("not-existent-key"));
fail("should have thrown IOException");
@ -70,13 +71,72 @@ public class X509SvidTest {
}
}
@Test
void testLoad_nullCertFilePath_throwsNullPointerException() throws URISyntaxException {
try {
X509Svid.load(null, Paths.get(toUri(keyRSA)));
fail("should have thrown exception");
} catch (NullPointerException | X509SvidException e) {
assertEquals("certsFilePath is marked non-null but is null", e.getMessage());
}
}
@Test
void testLoad_nullKeyFilePath_throwsNullPointerException() throws URISyntaxException, X509SvidException {
try {
X509Svid.load(Paths.get(toUri(certSingle)), null);
fail("should have thrown exception");
} catch (NullPointerException e) {
assertEquals("privateKeyFilePath is marked non-null but is null", e.getMessage());
}
}
@Test
void testParse_nullByteArray_throwsNullPointerException() throws X509SvidException {
try {
X509Svid.parse(null, "key".getBytes());
fail("should have thrown exception");
} catch (NullPointerException e) {
assertEquals("certsBytes is marked non-null but is null", e.getMessage());
}
}
@Test
void testParse_nullKeyByteArray_throwsNullPointerException() throws X509SvidException {
try {
X509Svid.parse("cert".getBytes(), null);
fail("should have thrown exception");
} catch (NullPointerException e) {
assertEquals("privateKeyBytes is marked non-null but is null", e.getMessage());
}
}
@Test
void testGetX509Svid() throws URISyntaxException, X509SvidException {
Path certPath = Paths.get(toUri(certSingle));
Path keyPath = Paths.get(toUri(keyRSA));
X509Svid x509Svid = X509Svid.load(certPath, keyPath);
assertEquals(x509Svid, x509Svid.getX509Svid());
}
@Test
void testGetChainArray() throws URISyntaxException, X509SvidException {
Path certPath = Paths.get(toUri(certMultiple));
Path keyPath = Paths.get(toUri(keyECDSA));
X509Svid x509Svid = X509Svid.load(certPath, keyPath);
X509Certificate[] x509CertificatesArray = x509Svid.getChainArray();
assertEquals(x509Svid.getChain().get(0), x509CertificatesArray[0]);
assertEquals(x509Svid.getChain().get(1), x509CertificatesArray[1]);
}
@ParameterizedTest
@MethodSource("provideX509SvidScenarios")
void parseX509Svid(TestCase testCase) {
try {
Path certPath = Paths.get(loadResource(testCase.certsPath));
Path keyPath = Paths.get(loadResource(testCase.keyPath));
Path certPath = Paths.get(toUri(testCase.certsPath));
Path keyPath = Paths.get(toUri(testCase.keyPath));
byte[] certBytes = Files.readAllBytes(certPath);
byte[] keyBytes = Files.readAllBytes(keyPath);
@ -246,7 +306,7 @@ public class X509SvidTest {
}
}
private URI loadResource(String path) throws URISyntaxException {
private URI toUri(String path) throws URISyntaxException {
return getClass().getClassLoader().getResource(path).toURI();
}
}

View File

@ -21,6 +21,7 @@ import java.security.cert.CertificateException;
import java.util.Arrays;
import java.util.List;
import static java.util.Collections.EMPTY_LIST;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.when;
@ -37,11 +38,11 @@ public class X509SvidValidatorTest {
@Test
void verifyChain_certificateExpired_throwsCertificateException() throws IOException, CertificateException, BundleNotFoundException, URISyntaxException {
val certPath = Paths.get(loadResource("testdata/x509svid/cert.pem"));
val certPath = Paths.get(toUri("testdata/x509svid/cert.pem"));
val certBytes = Files.readAllBytes(certPath);
val chain = CertificateUtils.generateCertificates(certBytes);
val bundlePath = Paths.get(loadResource("testdata/x509svid/bundle.pem"));
val bundlePath = Paths.get(toUri("testdata/x509svid/bundle.pem"));
X509Bundle x509Bundle=
X509Bundle.load(
TrustDomain.of("example.org"),
@ -63,11 +64,11 @@ public class X509SvidValidatorTest {
@Test
void verifyChain_noBundleForTrustDomain_throwsBundleNotFoundException() throws IOException, CertificateException, BundleNotFoundException, URISyntaxException {
val certPath = Paths.get(loadResource("testdata/x509svid/cert.pem"));
val certPath = Paths.get(toUri("testdata/x509svid/cert.pem"));
val certBytes = Files.readAllBytes(certPath);
val chain = CertificateUtils.generateCertificates(certBytes);
val bundlePath = Paths.get(loadResource("testdata/x509svid/bundle.pem"));
val bundlePath = Paths.get(toUri("testdata/x509svid/bundle.pem"));
X509Bundle x509Bundle=
X509Bundle.load(
TrustDomain.of("example.org"),
@ -92,7 +93,7 @@ public class X509SvidValidatorTest {
val spiffeId1 = SpiffeId.parse("spiffe://example.org/test");
val spiffeId2 = SpiffeId.parse("spiffe://example.org/test2");
val certPath = Paths.get(loadResource("testdata/x509svid/cert.pem"));
val certPath = Paths.get(toUri("testdata/x509svid/cert.pem"));
val certBytes = Files.readAllBytes(certPath);
val x509Certificate = CertificateUtils.generateCertificates(certBytes);
@ -107,7 +108,7 @@ public class X509SvidValidatorTest {
val spiffeId2 = SpiffeId.parse("spiffe://example.org/other2");
List<SpiffeId> spiffeIdList = Arrays.asList(spiffeId1, spiffeId2);
val certPath = Paths.get(loadResource("testdata/x509svid/cert.pem"));
val certPath = Paths.get(toUri("testdata/x509svid/cert.pem"));
val certBytes = Files.readAllBytes(certPath);
val x509Certificate = CertificateUtils.generateCertificates(certBytes);
@ -119,7 +120,53 @@ public class X509SvidValidatorTest {
}
}
private URI loadResource(String path) throws URISyntaxException {
@Test
void checkSpiffeId_nullX509Certificate_throwsNullPointerException() throws CertificateException {
try {
X509SvidValidator.verifySpiffeId(null, () -> EMPTY_LIST);
fail("should have thrown an exception");
} catch (NullPointerException e) {
assertEquals("x509Certificate is marked non-null but is null", e.getMessage());
}
}
@Test
void checkSpiffeId_nullAcceptedSpiffeIdsSuppplier_throwsNullPointerException() throws CertificateException, URISyntaxException, IOException {
try {
val certPath = Paths.get(toUri("testdata/x509svid/cert.pem"));
val certBytes = Files.readAllBytes(certPath);
val x509Certificate = CertificateUtils.generateCertificates(certBytes);
X509SvidValidator.verifySpiffeId(x509Certificate.get(0), null);
fail("should have thrown an exception");
} catch (NullPointerException e) {
assertEquals("acceptedSpiffedIdsSupplier is marked non-null but is null", e.getMessage());
}
}
@Test
void verifyChain_nullChain_throwsNullPointerException() throws CertificateException, BundleNotFoundException {
try {
X509SvidValidator.verifyChain(null, bundleSourceMock);
fail("should have thrown an exception");
} catch (NullPointerException e) {
assertEquals("chain is marked non-null but is null", e.getMessage());
}
}
@Test
void verifyChain_nullBundleSource_throwsNullPointerException() throws CertificateException, BundleNotFoundException, URISyntaxException, IOException {
try {
val certPath = Paths.get(toUri("testdata/x509svid/cert.pem"));
val certBytes = Files.readAllBytes(certPath);
val chain = CertificateUtils.generateCertificates(certBytes);
X509SvidValidator.verifyChain(chain, null);
fail("should have thrown an exception");
} catch (NullPointerException e) {
assertEquals("x509BundleSource is marked non-null but is null", e.getMessage());
}
}
private URI toUri(String path) throws URISyntaxException {
return getClass().getClassLoader().getResource(path).toURI();
}
}

View File

@ -0,0 +1,3 @@
spiffe://example.org/workload1
spiffe://example.org/workload2
spiffe://example2.org/workload1

View File

@ -38,8 +38,8 @@ public class KeyStoreTest {
void setup() throws X509SvidException, URISyntaxException {
x509Svid = X509Svid
.load(
Paths.get(loadResource("testdata/x509cert.pem")),
Paths.get(loadResource("testdata/pkcs8key.pem"))
Paths.get(toUri("testdata/x509cert.pem")),
Paths.get(toUri("testdata/pkcs8key.pem"))
);
}
@ -97,7 +97,7 @@ public class KeyStoreTest {
}
}
private URI loadResource(String path) throws URISyntaxException {
private URI toUri(String path) throws URISyntaxException {
return getClass().getClassLoader().getResource(path).toURI();
}
}

View File

@ -34,8 +34,8 @@ public class SpiffeKeyManagerTest {
keyManager = (X509KeyManager) new SpiffeKeyManagerFactory().engineGetKeyManagers(x509SvidSource)[0];
x509Svid = X509Svid
.load(
Paths.get(loadResource("testdata/cert.pem")),
Paths.get(loadResource("testdata/key.pem")));
Paths.get(toUri("testdata/cert.pem")),
Paths.get(toUri("testdata/key.pem")));
}
@Test
@ -60,7 +60,7 @@ public class SpiffeKeyManagerTest {
assertNotNull(privateKey);
}
private URI loadResource(String path) throws URISyntaxException {
private URI toUri(String path) throws URISyntaxException {
return getClass().getClassLoader().getResource(path).toURI();
}
}

View File

@ -43,16 +43,16 @@ public class SpiffeTrustManagerTest {
static void setupClass() throws IOException, CertificateException, X509SvidException, URISyntaxException {
x509Svid = X509Svid
.load(
Paths.get(loadResource("testdata/cert.pem")),
Paths.get(loadResource("testdata/key.pem")));
Paths.get(toUri("testdata/cert.pem")),
Paths.get(toUri("testdata/key.pem")));
otherX509Svid = X509Svid
.load(
Paths.get(loadResource("testdata/cert2.pem")),
Paths.get(loadResource("testdata/key2.pem")));
Paths.get(toUri("testdata/cert2.pem")),
Paths.get(toUri("testdata/key2.pem")));
x509Bundle = X509Bundle
.load(
TrustDomain.of("example.org"),
Paths.get(loadResource("testdata/bundle.pem")));
Paths.get(toUri("testdata/bundle.pem")));
}
@BeforeEach
@ -206,7 +206,7 @@ public class SpiffeTrustManagerTest {
}
}
private static URI loadResource(String path) throws URISyntaxException {
private static URI toUri(String path) throws URISyntaxException {
return SpiffeTrustManagerTest.class.getClassLoader().getResource(path).toURI();
}
}