Skip to content

Commit 9be1f4e

Browse files
authored
Merge pull request #178 from cconlon/pkixImprovements
PKIX Improvements and WolfSSLKeyStore engineProbe()
2 parents 4592dd2 + e0730f3 commit 9be1f4e

File tree

6 files changed

+468
-19
lines changed

6 files changed

+468
-19
lines changed

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

Lines changed: 80 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -733,28 +733,56 @@ public TrustAnchor findTrustAnchor(PKIXParameters params,
733733
return anchorFound;
734734
}
735735

736+
/**
737+
* Throw CertPathValidatorException for undetermined revocation status.
738+
*
739+
* Used when revocation checking is enabled but no CRLs are available
740+
* and no PKIXRevocationChecker is configured to handle OCSP.
741+
*
742+
* @param message error message describing the revocation failure
743+
* @param certPath the CertPath being validated (for exception reporting)
744+
* @param certs list of certificates from certPath
745+
*
746+
* @throws CertPathValidatorException always thrown with
747+
* UNDETERMINED_REVOCATION_STATUS reason
748+
*/
749+
private void throwUndeterminedRevocationStatus(String message,
750+
CertPath certPath, List<X509Certificate> certs)
751+
throws CertPathValidatorException {
752+
753+
/* Report index of last cert in path (closest to trust anchor)
754+
* to match SunJCE behavior. */
755+
int failIndex = 0;
756+
if (certs != null && certs.size() > 1) {
757+
failIndex = certs.size() - 1;
758+
}
759+
throw new CertPathValidatorException(message, null, certPath,
760+
failIndex, BasicReason.UNDETERMINED_REVOCATION_STATUS);
761+
}
762+
736763
/**
737764
* Check if revocation has been enabled in PKIXParameters, and if so
738765
* find and load any CRLs in params.getCertStores().
739766
*
740767
* When a PKIXRevocationChecker has been registered via
741768
* addCertPathChecker(), that checker handles revocation checking. CRL
742769
* checking in the native CertManager is only enabled if:
743-
* - No PKIXRevocationChecker is present (default CRL behavior), OR
770+
* - No PKIXRevocationChecker is present (default CRL behavior), or
744771
* - PKIXRevocationChecker has PREFER_CRLS option set
745772
*
746773
* @param params parameters used to check if revocation is enabled
747-
* and if so load any CRLs available
774+
* and, if so load any CRLs available
748775
* @param cm WolfSSLCertManager to load CRLs into
749-
* @param targetCert peer/leaf cert used to find matching CRL
776+
* @param certPath the CertPath being validated (for exception reporting)
777+
* @param certs list of certificates from certPath
750778
* @param pathCheckers list of registered CertPathCheckers
751779
*
752780
* @throws CertPathValidatorException if error is encountered during
753781
* revocation checking or CRL loading
754782
*/
755783
private void checkRevocationEnabledAndLoadCRLs(
756784
PKIXParameters params, WolfSSLCertManager cm,
757-
X509Certificate targetCert,
785+
CertPath certPath, List<X509Certificate> certs,
758786
List<PKIXCertPathChecker> pathCheckers)
759787
throws CertPathValidatorException {
760788

@@ -810,12 +838,22 @@ private void checkRevocationEnabledAndLoadCRLs(
810838

811839
/* Enable CRL in native WolfSSLCertManager */
812840
cm.CertManagerEnableCRL(WolfCrypt.WOLFSSL_CRL_CHECK);
813-
814841
log("CRL support enabled in native WolfSSLCertManager");
815842

816843
stores = params.getCertStores();
817844
if (stores == null || stores.isEmpty()) {
818845
log("no CertStores in PKIXParameters to load CRLs");
846+
847+
/* If revocation is enabled but no CRLs and no
848+
* PKIXRevocationChecker to handle OCSP, we cannot determine
849+
* revocation status. Per RFC 5280, this should fail. */
850+
if (!hasRevocationChecker) {
851+
throwUndeterminedRevocationStatus(
852+
"Revocation checking enabled but no CRLs available " +
853+
"and no PKIXRevocationChecker configured for OCSP",
854+
certPath, certs);
855+
}
856+
819857
return;
820858
}
821859

@@ -850,7 +888,7 @@ private void checkRevocationEnabledAndLoadCRLs(
850888

851889
/* Create CRL selector to help match target X509Certificate */
852890
X509CRLSelector selector = new X509CRLSelector();
853-
selector.setCertificateChecking(targetCert);
891+
selector.setCertificateChecking(certs.get(0));
854892

855893
try {
856894
/* Find and load any matching CRLs */
@@ -868,6 +906,16 @@ private void checkRevocationEnabledAndLoadCRLs(
868906
}
869907

870908
log("loaded " + loadedCount + " CRLs into WolfSSLCertManager");
909+
910+
/* If no CRLs were loaded and no PKIXRevocationChecker is handling
911+
* OCSP, we cannot determine revocation status. */
912+
if (loadedCount == 0 && !hasRevocationChecker) {
913+
throwUndeterminedRevocationStatus(
914+
"Revocation checking enabled but no CRLs found in " +
915+
"CertStores and no PKIXRevocationChecker configured " +
916+
"for OCSP",
917+
certPath, certs);
918+
}
871919
}
872920
else {
873921
log("revocation not enabled in PKIXParameters");
@@ -953,6 +1001,31 @@ public CertPathValidatorResult engineValidate(
9531001
}
9541002
}
9551003

1004+
/* Zero-length cert paths are valid per RFC 5280. This occurs when
1005+
* CertPathBuilder determines the trust anchor itself is the target
1006+
* (no intermediate certificates needed). Return success with the
1007+
* trust anchor's public key. No need to create CertManager. */
1008+
if (certPath.getCertificates().isEmpty()) {
1009+
Set<TrustAnchor> anchors = pkixParams.getTrustAnchors();
1010+
if (anchors == null || anchors.isEmpty()) {
1011+
throw new CertPathValidatorException(
1012+
"No TrustAnchors in PKIXParameters");
1013+
}
1014+
1015+
/* Return first trust anchor for zero-length path */
1016+
TrustAnchor anchor = anchors.iterator().next();
1017+
X509Certificate anchorCert = anchor.getTrustedCert();
1018+
if (anchorCert == null) {
1019+
throw new CertPathValidatorException(
1020+
"TrustAnchor has no certificate for zero-length path");
1021+
}
1022+
log("Zero-length cert path, returning trust anchor: " +
1023+
anchorCert.getSubjectX500Principal().getName());
1024+
1025+
return new PKIXCertPathValidatorResult(anchor, null,
1026+
anchorCert.getPublicKey());
1027+
}
1028+
9561029
/* Use wolfSSL CertManager to do chain verification */
9571030
try {
9581031
cm = new WolfSSLCertManager();
@@ -972,10 +1045,6 @@ public CertPathValidatorResult engineValidate(
9721045
certs.add((X509Certificate) cert);
9731046
}
9741047
}
975-
if (certs.size() == 0) {
976-
throw new CertPathValidatorException(
977-
"No Certificate objects in CertPath");
978-
}
9791048

9801049
/* Register verify callback to override date validation if
9811050
* PKIXParameters specifies an override date */
@@ -1019,7 +1088,7 @@ public CertPathValidatorResult engineValidate(
10191088
* will try to find/verify CRL against trusted roots on load.
10201089
* Pass pathCheckers so we can skip CRL setup when a
10211090
* PKIXRevocationChecker is handling revocation via OCSP. */
1022-
checkRevocationEnabledAndLoadCRLs(pkixParams, cm, certs.get(0),
1091+
checkRevocationEnabledAndLoadCRLs(pkixParams, cm, certPath, certs,
10231092
pathCheckers);
10241093

10251094
/* Verify cert chain */

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -545,11 +545,16 @@ public void setOcspResponses(Map<X509Certificate, byte[]> responses) {
545545
/**
546546
* Get pre-loaded OCSP responses.
547547
*
548+
* Returns the internal mutable map, not an unmodifiable copy.
549+
* JDK sun.security.validator.PKIXValidator.addResponses() expects
550+
* to be able to add OCSP responses to this map when using the internal
551+
* Validator API.
552+
*
548553
* @return Map of certificates to OCSP response bytes
549554
*/
550555
@Override
551556
public Map<X509Certificate, byte[]> getOcspResponses() {
552-
return Collections.unmodifiableMap(this.ocspResponses);
557+
return this.ocspResponses;
553558
}
554559

555560
/**

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

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1808,6 +1808,62 @@ public synchronized void engineLoad(InputStream stream, char[] password)
18081808
return;
18091809
}
18101810

1811+
/**
1812+
* Probes the specified input stream to determine if it contains a
1813+
* keystore that is supported by this implementation.
1814+
*
1815+
* This method is used by KeyStore.getInstance(File, char[]) and similar
1816+
* methods to auto-detect the keystore type without requiring explicit
1817+
* type specification.
1818+
*
1819+
* Note: engineProbe() was added in JDK 9. No @Override annotation to
1820+
* maintain Java 8 compatibility. On JDK 9+ this method is called by
1821+
* KeyStore for auto-detection; on Java 8 it is simply unused.
1822+
*
1823+
* @param stream the keystore data to be probed. The stream must support
1824+
* mark/reset so the caller can rewind after probing.
1825+
*
1826+
* @return true if the keystore data is supported (has WKS magic number),
1827+
* otherwise false
1828+
*
1829+
* @throws IOException if there is an I/O problem with the keystore data
1830+
* @throws NullPointerException if the stream is null
1831+
*/
1832+
public boolean engineProbe(InputStream stream) throws IOException {
1833+
1834+
int magic = 0;
1835+
DataInputStream dis = null;
1836+
1837+
if (stream == null) {
1838+
throw new NullPointerException("InputStream cannot be null");
1839+
}
1840+
1841+
/* Read first 4 bytes to check magic number */
1842+
stream.mark(4);
1843+
dis = new DataInputStream(stream);
1844+
1845+
try {
1846+
magic = dis.readInt();
1847+
if (magic == WKS_MAGIC_NUMBER) {
1848+
return true;
1849+
}
1850+
}
1851+
catch (IOException e) {
1852+
/* Could not read 4 bytes, not a valid WKS file, swallow
1853+
* exception and return false below. */
1854+
}
1855+
finally {
1856+
try {
1857+
stream.reset();
1858+
}
1859+
catch (IOException e) {
1860+
/* Reset failed, stream may not support mark/reset */
1861+
}
1862+
}
1863+
1864+
return false;
1865+
}
1866+
18111867
/**
18121868
* Internal method for logging output.
18131869
*

0 commit comments

Comments
 (0)