diff --git a/java-spiffe-core/src/main/java/io/spiffe/svid/x509svid/X509Svid.java b/java-spiffe-core/src/main/java/io/spiffe/svid/x509svid/X509Svid.java index e380d3c9..47d5fd2d 100644 --- a/java-spiffe-core/src/main/java/io/spiffe/svid/x509svid/X509Svid.java +++ b/java-spiffe-core/src/main/java/io/spiffe/svid/x509svid/X509Svid.java @@ -217,6 +217,7 @@ private static X509Svid createX509Svid(final byte[] certsBytes, final PrivateKey privateKey = generatePrivateKey(privateKeyBytes, keyFileFormat, x509Certificates); final SpiffeId spiffeId = getSpiffeId(x509Certificates); + validateLeafSpiffeId(spiffeId); validateLeafCertificate(x509Certificates.get(0)); // there are intermediate CA certificates @@ -227,6 +228,13 @@ private static X509Svid createX509Svid(final byte[] certsBytes, return new X509Svid(spiffeId, x509Certificates, privateKey, hint); } + private static void validateLeafSpiffeId(final SpiffeId spiffeId) throws X509SvidException { + final String path = spiffeId.getPath(); + if (path == null || path.isEmpty()) { + throw new X509SvidException("Leaf certificate SPIFFE ID must have a non-root path"); + } + } + private static SpiffeId getSpiffeId(final List x509Certificates) throws X509SvidException { final SpiffeId spiffeId; try { diff --git a/java-spiffe-core/src/test/java/io/spiffe/svid/x509svid/X509SvidTest.java b/java-spiffe-core/src/test/java/io/spiffe/svid/x509svid/X509SvidTest.java index da852e80..5689c25d 100644 --- a/java-spiffe-core/src/test/java/io/spiffe/svid/x509svid/X509SvidTest.java +++ b/java-spiffe-core/src/test/java/io/spiffe/svid/x509svid/X509SvidTest.java @@ -1,8 +1,10 @@ package io.spiffe.svid.x509svid; +import io.spiffe.exception.InvalidSpiffeIdException; import io.spiffe.exception.X509SvidException; import io.spiffe.spiffeid.SpiffeId; import io.spiffe.spiffeid.TrustDomain; +import io.spiffe.utils.CertAndKeyPair; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -18,8 +20,11 @@ import java.util.stream.Stream; import static io.spiffe.utils.TestUtils.toUri; +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.assertThrows; import static org.junit.jupiter.api.Assertions.fail; class X509SvidTest { @@ -315,6 +320,38 @@ void testGetChainArray() throws URISyntaxException, X509SvidException { assertEquals(x509Svid.getChain().get(1), x509CertificatesArray[1]); } + @Test + void parseRaw_leafSpiffeIdWithoutPath_isRejected() throws Exception { + CertAndKeyPair rootCa = createRootCA("C = US, O = SPIFFE", "spiffe://example.org"); + CertAndKeyPair leaf = createCertificate("C = US, O = SPIRE", "C = US, O = SPIFFE", "spiffe://example.org", rootCa, false); + + byte[] certBytes = leaf.getCertificate().getEncoded(); + byte[] keyBytes = leaf.getKeyPair().getPrivate().getEncoded(); + + X509SvidException exception = assertThrows( + X509SvidException.class, + () -> X509Svid.parseRaw(certBytes, keyBytes) + ); + + assertEquals("Leaf certificate SPIFFE ID must have a non-root path", exception.getMessage()); + } + + @Test + void parseRaw_leafSpiffeIdWithRootOnlyPath_isRejected() throws Exception { + CertAndKeyPair rootCa = createRootCA("C = US, O = SPIFFE", "spiffe://example.org"); + CertAndKeyPair leaf = createCertificate("C = US, O = SPIRE", "C = US, O = SPIFFE", "spiffe://example.org/", rootCa, false); + + byte[] certBytes = leaf.getCertificate().getEncoded(); + byte[] keyBytes = leaf.getKeyPair().getPrivate().getEncoded(); + + InvalidSpiffeIdException exception = assertThrows( + InvalidSpiffeIdException.class, + () -> X509Svid.parseRaw(certBytes, keyBytes) + ); + + assertEquals("Path cannot have a trailing slash", exception.getMessage()); + } + @ParameterizedTest @MethodSource("provideX509SvidScenarios") void parseX509Svid(TestCase testCase) {