Skip to content

Commit 99415db

Browse files
committed
JCE: support zero-length cert paths in PKIXCertPathValidator
1 parent 4592dd2 commit 99415db

File tree

2 files changed

+128
-4
lines changed

2 files changed

+128
-4
lines changed

src/main/java/com/wolfssl/provider/jce/WolfCryptPKIXCertPathValidator.java

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -953,6 +953,31 @@ public CertPathValidatorResult engineValidate(
953953
}
954954
}
955955

956+
/* Zero-length cert paths are valid per RFC 5280. This occurs when
957+
* CertPathBuilder determines the trust anchor itself is the target
958+
* (no intermediate certificates needed). Return success with the
959+
* trust anchor's public key. No need to create CertManager. */
960+
if (certPath.getCertificates().isEmpty()) {
961+
Set<TrustAnchor> anchors = pkixParams.getTrustAnchors();
962+
if (anchors == null || anchors.isEmpty()) {
963+
throw new CertPathValidatorException(
964+
"No TrustAnchors in PKIXParameters");
965+
}
966+
967+
/* Return first trust anchor for zero-length path */
968+
TrustAnchor anchor = anchors.iterator().next();
969+
X509Certificate anchorCert = anchor.getTrustedCert();
970+
if (anchorCert == null) {
971+
throw new CertPathValidatorException(
972+
"TrustAnchor has no certificate for zero-length path");
973+
}
974+
log("Zero-length cert path, returning trust anchor: " +
975+
anchorCert.getSubjectX500Principal().getName());
976+
977+
return new PKIXCertPathValidatorResult(anchor, null,
978+
anchorCert.getPublicKey());
979+
}
980+
956981
/* Use wolfSSL CertManager to do chain verification */
957982
try {
958983
cm = new WolfSSLCertManager();
@@ -972,10 +997,6 @@ public CertPathValidatorResult engineValidate(
972997
certs.add((X509Certificate) cert);
973998
}
974999
}
975-
if (certs.size() == 0) {
976-
throw new CertPathValidatorException(
977-
"No Certificate objects in CertPath");
978-
}
9791000

9801001
/* Register verify callback to override date validation if
9811002
* PKIXParameters specifies an override date */

src/test/java/com/wolfssl/provider/jce/test/WolfCryptPKIXCertPathValidatorTest.java

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1674,5 +1674,108 @@ public void testAlgorithmConstraintsWithSHAVariant() throws Exception {
16741674
}
16751675
}
16761676
}
1677+
1678+
/**
1679+
* Test that zero-length cert paths are valid per RFC 5280. This occurs
1680+
* when CertPathBuilder determines the trust anchor itself is the target.
1681+
*/
1682+
@Test
1683+
public void testZeroLengthCertPath()
1684+
throws CertificateException, NoSuchAlgorithmException,
1685+
InvalidAlgorithmParameterException, CertPathValidatorException,
1686+
NoSuchProviderException {
1687+
1688+
FileInputStream fis = null;
1689+
X509Certificate caCert = null;
1690+
CertificateFactory cf = CertificateFactory.getInstance("X.509");
1691+
1692+
/* Use CA cert from examples as trust anchor */
1693+
try {
1694+
fis = new FileInputStream(caCertDer);
1695+
caCert = (X509Certificate)cf.generateCertificate(fis);
1696+
fis.close();
1697+
} catch (Exception e) {
1698+
fail("Failed to load CA cert: " + e.getMessage());
1699+
}
1700+
1701+
/* Create trust anchor from CA cert */
1702+
TrustAnchor anchor = new TrustAnchor(caCert, null);
1703+
Set<TrustAnchor> anchors = new HashSet<>();
1704+
anchors.add(anchor);
1705+
1706+
/* Create empty cert path (zero-length) */
1707+
List<Certificate> emptyCertList = new ArrayList<>();
1708+
CertPath emptyPath = cf.generateCertPath(emptyCertList);
1709+
assertEquals(0, emptyPath.getCertificates().size());
1710+
1711+
/* Create PKIXParameters with the trust anchor */
1712+
PKIXParameters params = new PKIXParameters(anchors);
1713+
params.setRevocationEnabled(false);
1714+
1715+
/* Validate zero-length path - should succeed */
1716+
CertPathValidator cpv =
1717+
CertPathValidator.getInstance("PKIX", provider);
1718+
PKIXCertPathValidatorResult result =
1719+
(PKIXCertPathValidatorResult)cpv.validate(emptyPath, params);
1720+
1721+
/* Verify result contains trust anchor and its public key */
1722+
assertNotNull(result);
1723+
assertNotNull(result.getTrustAnchor());
1724+
assertEquals(anchor.getTrustedCert(),
1725+
result.getTrustAnchor().getTrustedCert());
1726+
assertNotNull(result.getPublicKey());
1727+
assertEquals(caCert.getPublicKey(), result.getPublicKey());
1728+
}
1729+
1730+
/**
1731+
* Test that zero-length cert path with TrustAnchor that has no
1732+
* certificate throws CertPathValidatorException.
1733+
*/
1734+
@Test
1735+
public void testZeroLengthCertPathAnchorNoCertificate()
1736+
throws CertificateException, NoSuchAlgorithmException,
1737+
InvalidAlgorithmParameterException, NoSuchProviderException {
1738+
1739+
CertificateFactory cf = CertificateFactory.getInstance("X.509");
1740+
1741+
/* Create empty cert path (zero-length) */
1742+
List<Certificate> emptyCertList = new ArrayList<>();
1743+
CertPath emptyPath = cf.generateCertPath(emptyCertList);
1744+
1745+
/* Load CA cert to get its name and public key */
1746+
FileInputStream fis = null;
1747+
X509Certificate caCert = null;
1748+
try {
1749+
fis = new FileInputStream(caCertDer);
1750+
caCert = (X509Certificate)cf.generateCertificate(fis);
1751+
fis.close();
1752+
} catch (Exception e) {
1753+
fail("Failed to load CA cert: " + e.getMessage());
1754+
}
1755+
1756+
/* Create TrustAnchor without certificate (using CA name and key).
1757+
* This is valid for normal cert paths but not for zero-length
1758+
* paths where we need to return the anchor's certificate. */
1759+
TrustAnchor anchorWithoutCert = new TrustAnchor(
1760+
caCert.getSubjectX500Principal(), caCert.getPublicKey(), null);
1761+
Set<TrustAnchor> anchors = new HashSet<>();
1762+
anchors.add(anchorWithoutCert);
1763+
1764+
PKIXParameters params = new PKIXParameters(anchors);
1765+
params.setRevocationEnabled(false);
1766+
1767+
/* Validate zero-length path with anchor that has no cert */
1768+
CertPathValidator cpv =
1769+
CertPathValidator.getInstance("PKIX", provider);
1770+
1771+
try {
1772+
cpv.validate(emptyPath, params);
1773+
fail("Expected CertPathValidatorException for zero-length " +
1774+
"path with TrustAnchor that has no certificate");
1775+
} catch (CertPathValidatorException e) {
1776+
assertTrue("Exception message should mention no certificate",
1777+
e.getMessage().contains("no certificate"));
1778+
}
1779+
}
16771780
}
16781781

0 commit comments

Comments
 (0)