Merge pull request #200 from cconlon/certPathBuilderFixes
CertPathBuilder fixes for disabledAlgorithms, date handling, and edge casespull/202/head
commit
ae366dd9ec
|
|
@ -7,6 +7,14 @@
|
|||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/*
|
||||
* Class: com_wolfssl_wolfcrypt_WolfSSLCertManager
|
||||
* Method: getWOLFSSL_LOAD_FLAG_DATE_ERR_OKAY
|
||||
* Signature: ()I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_getWOLFSSL_1LOAD_1FLAG_1DATE_1ERR_1OKAY
|
||||
(JNIEnv *, jclass);
|
||||
|
||||
/*
|
||||
* Class: com_wolfssl_wolfcrypt_WolfSSLCertManager
|
||||
* Method: CertManagerNew
|
||||
|
|
@ -39,6 +47,14 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManager
|
|||
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerLoadCABuffer
|
||||
(JNIEnv *, jclass, jlong, jbyteArray, jlong, jint);
|
||||
|
||||
/*
|
||||
* Class: com_wolfssl_wolfcrypt_WolfSSLCertManager
|
||||
* Method: CertManagerLoadCABufferEx
|
||||
* Signature: (J[BJII)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerLoadCABufferEx
|
||||
(JNIEnv *, jclass, jlong, jbyteArray, jlong, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: com_wolfssl_wolfcrypt_WolfSSLCertManager
|
||||
* Method: CertManagerUnloadCAs
|
||||
|
|
@ -119,6 +135,14 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManager
|
|||
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerCheckOCSPResponse
|
||||
(JNIEnv *, jclass, jlong, jbyteArray, jint, jbyteArray, jint);
|
||||
|
||||
/*
|
||||
* Class: com_wolfssl_wolfcrypt_WolfSSLCertManager
|
||||
* Method: OcspResponseStatus
|
||||
* Signature: ([BI)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_OcspResponseStatus
|
||||
(JNIEnv *, jclass, jbyteArray, jint);
|
||||
|
||||
/*
|
||||
* Class: com_wolfssl_wolfcrypt_WolfSSLCertManager
|
||||
* Method: CertManagerSetVerify
|
||||
|
|
|
|||
|
|
@ -170,6 +170,32 @@ static void removeCallbackCtx(WOLFSSL_CERT_MANAGER* cm)
|
|||
}
|
||||
}
|
||||
|
||||
/* Extract cert DER bytes at given depth from WOLFSSL_X509_STORE_CTX into
|
||||
* a new jbyteArray. Returns NULL if cert not available at depth. */
|
||||
static jbyteArray getCertDerAtDepth(JNIEnv* jenv,
|
||||
WOLFSSL_X509_STORE_CTX* store, int depth)
|
||||
{
|
||||
jbyteArray der = NULL;
|
||||
WOLFSSL_BUFFER_INFO* certInfo = NULL;
|
||||
|
||||
if (store->certs == NULL || depth < 0 || depth >= store->totalCerts) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
certInfo = &store->certs[depth];
|
||||
if (certInfo->buffer == NULL || certInfo->length == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
der = (*jenv)->NewByteArray(jenv, (jsize)certInfo->length);
|
||||
if (der != NULL) {
|
||||
(*jenv)->SetByteArrayRegion(jenv, der, 0, (jsize)certInfo->length,
|
||||
(const jbyte*)certInfo->buffer);
|
||||
}
|
||||
|
||||
return der;
|
||||
}
|
||||
|
||||
/* Native verify callback that calls into Java verify callback.
|
||||
*
|
||||
* This is registered with wolfSSL_CertManagerSetVerify() and called
|
||||
|
|
@ -185,6 +211,7 @@ static int nativeVerifyCallback(int preverify, WOLFSSL_X509_STORE_CTX* store)
|
|||
jint result = 0;
|
||||
JNIEnv* jenv = NULL;
|
||||
VerifyCallbackCtx* ctx = NULL;
|
||||
jbyteArray certDer = NULL;
|
||||
jclass callbackClass = NULL;
|
||||
jmethodID verifyMethod = NULL;
|
||||
|
||||
|
|
@ -236,9 +263,15 @@ static int nativeVerifyCallback(int preverify, WOLFSSL_X509_STORE_CTX* store)
|
|||
error = store->error;
|
||||
errorDepth = store->error_depth;
|
||||
|
||||
/* If available, extract cert DER bytes from store context at errorDepth */
|
||||
certDer = getCertDerAtDepth(jenv, store, errorDepth);
|
||||
|
||||
/* Find verify() method on callback object */
|
||||
callbackClass = (*jenv)->GetObjectClass(jenv, ctx->callback);
|
||||
if (callbackClass == NULL) {
|
||||
if (certDer != NULL) {
|
||||
(*jenv)->DeleteLocalRef(jenv, certDer);
|
||||
}
|
||||
if (needsDetach) {
|
||||
(*ctx->jvm)->DetachCurrentThread(ctx->jvm);
|
||||
}
|
||||
|
|
@ -246,8 +279,11 @@ static int nativeVerifyCallback(int preverify, WOLFSSL_X509_STORE_CTX* store)
|
|||
}
|
||||
|
||||
verifyMethod = (*jenv)->GetMethodID(jenv, callbackClass,
|
||||
"verify", "(III)I");
|
||||
"verify", "(III[B)I");
|
||||
if (verifyMethod == NULL) {
|
||||
if (certDer != NULL) {
|
||||
(*jenv)->DeleteLocalRef(jenv, certDer);
|
||||
}
|
||||
(*jenv)->DeleteLocalRef(jenv, callbackClass);
|
||||
if (needsDetach) {
|
||||
(*ctx->jvm)->DetachCurrentThread(ctx->jvm);
|
||||
|
|
@ -255,9 +291,9 @@ static int nativeVerifyCallback(int preverify, WOLFSSL_X509_STORE_CTX* store)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Call Java callback.verify(preverify, error, errorDepth) */
|
||||
/* Call Java callback.verify(preverify, error, errorDepth, certDer) */
|
||||
result = (*jenv)->CallIntMethod(jenv, ctx->callback, verifyMethod,
|
||||
(jint)preverify, (jint)error, (jint)errorDepth);
|
||||
(jint)preverify, (jint)error, (jint)errorDepth, certDer);
|
||||
|
||||
/* Check for Java exceptions, reject on exception */
|
||||
if ((*jenv)->ExceptionCheck(jenv)) {
|
||||
|
|
@ -273,6 +309,9 @@ static int nativeVerifyCallback(int preverify, WOLFSSL_X509_STORE_CTX* store)
|
|||
store->error = 0;
|
||||
}
|
||||
|
||||
if (certDer != NULL) {
|
||||
(*jenv)->DeleteLocalRef(jenv, certDer);
|
||||
}
|
||||
(*jenv)->DeleteLocalRef(jenv, callbackClass);
|
||||
if (needsDetach) {
|
||||
(*ctx->jvm)->DetachCurrentThread(ctx->jvm);
|
||||
|
|
@ -281,6 +320,15 @@ static int nativeVerifyCallback(int preverify, WOLFSSL_X509_STORE_CTX* store)
|
|||
return (int)result;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_getWOLFSSL_1LOAD_1FLAG_1DATE_1ERR_1OKAY
|
||||
(JNIEnv* env, jclass jcl)
|
||||
{
|
||||
(void)env;
|
||||
(void)jcl;
|
||||
|
||||
return WOLFSSL_LOAD_FLAG_DATE_ERR_OKAY;
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerNew
|
||||
(JNIEnv* env, jclass jcl)
|
||||
{
|
||||
|
|
@ -363,6 +411,31 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManager
|
|||
return (jint)ret;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerLoadCABufferEx
|
||||
(JNIEnv* env, jclass jcl, jlong cmPtr, jbyteArray in, jlong sz, jint format, jint flags)
|
||||
{
|
||||
int ret = 0;
|
||||
word32 buffSz = 0;
|
||||
byte* buff = NULL;
|
||||
WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)(uintptr_t)cmPtr;
|
||||
(void)jcl;
|
||||
(void)sz;
|
||||
|
||||
if (env == NULL || in == NULL) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
buff = (byte*)(*env)->GetByteArrayElements(env, in, NULL);
|
||||
buffSz = (*env)->GetArrayLength(env, in);
|
||||
|
||||
ret = wolfSSL_CertManagerLoadCABuffer_ex(cm, buff, buffSz, format, 0,
|
||||
(word32)flags);
|
||||
|
||||
(*env)->ReleaseByteArrayElements(env, in, (jbyte*)buff, JNI_ABORT);
|
||||
|
||||
return (jint)ret;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerUnloadCAs
|
||||
(JNIEnv* env, jclass jcl, jlong cmPtr)
|
||||
{
|
||||
|
|
@ -715,6 +788,69 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManager
|
|||
#endif
|
||||
}
|
||||
|
||||
/* Get OCSPResponseStatus from raw OCSP response bytes.
|
||||
*
|
||||
* Uses wolfSSL_d2i_OCSP_RESPONSE() to parse response and
|
||||
* wolfSSL_OCSP_response_status() to extract the status.
|
||||
*
|
||||
* RFC 6960:
|
||||
* OCSPResponse ::= SEQUENCE {
|
||||
* responseStatus OCSPResponseStatus,
|
||||
* responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
|
||||
* OCSPResponseStatus ::= ENUMERATED {
|
||||
* successful (0), malformedRequest (1), internalError (2),
|
||||
* tryLater (3), sigRequired (5), unauthorized (6) }
|
||||
*
|
||||
* Returns OCSPResponseStatus ENUMERATED value (0-6) on success, or negative
|
||||
* error code on failure (BAD_FUNC_ARG, MEMORY_E, NOT_COMPILED_IN, or -1 on
|
||||
* parse failure).
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_OcspResponseStatus
|
||||
(JNIEnv* env, jclass jcl, jbyteArray response, jint responseSz)
|
||||
{
|
||||
#if defined(HAVE_OCSP) && defined(OPENSSL_EXTRA)
|
||||
int ret = -1;
|
||||
jint arrLen = 0;
|
||||
byte* respBuf = NULL;
|
||||
const unsigned char* p = NULL;
|
||||
OcspResponse* ocspResp = NULL;
|
||||
(void)jcl;
|
||||
(void)responseSz;
|
||||
|
||||
if (env == NULL || response == NULL) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
arrLen = (*env)->GetArrayLength(env, response);
|
||||
if (arrLen <= 0) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
respBuf = (byte*)(*env)->GetByteArrayElements(env, response, NULL);
|
||||
if (respBuf == NULL) {
|
||||
return MEMORY_E;
|
||||
}
|
||||
|
||||
p = (const unsigned char*)respBuf;
|
||||
ocspResp = wolfSSL_d2i_OCSP_RESPONSE(NULL, &p, (int)arrLen);
|
||||
if (ocspResp != NULL) {
|
||||
ret = wolfSSL_OCSP_response_status(ocspResp);
|
||||
wolfSSL_OCSP_RESPONSE_free(ocspResp);
|
||||
}
|
||||
|
||||
(*env)->ReleaseByteArrayElements(env, response, (jbyte*)respBuf, JNI_ABORT);
|
||||
|
||||
return (jint)ret;
|
||||
|
||||
#else
|
||||
(void)env;
|
||||
(void)jcl;
|
||||
(void)response;
|
||||
(void)responseSz;
|
||||
return NOT_COMPILED_IN;
|
||||
#endif /* HAVE_OCSP && OPENSSL_EXTRA */
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerSetVerify
|
||||
(JNIEnv* env, jclass jcl, jlong cmPtr, jobject callback)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -113,13 +113,188 @@ public class WolfCryptPKIXCertPathBuilder extends CertPathBuilderSpi {
|
|||
throw new InvalidAlgorithmParameterException(
|
||||
"params not of type PKIXBuilderParameters");
|
||||
}
|
||||
|
||||
/* Target constraints must be X509CertSelector if set */
|
||||
PKIXBuilderParameters pkixParams = (PKIXBuilderParameters)params;
|
||||
CertSelector selector = pkixParams.getTargetCertConstraints();
|
||||
if ((selector != null) && !(selector instanceof X509CertSelector)) {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"Target certificate constraints must be X509CertSelector");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check certificate against disabled algorithms constraints from security
|
||||
* property jdk.certpath.disabledAlgorithms.
|
||||
*
|
||||
* Validates both the signature algorithm and public key algorithm/size
|
||||
* against the disabled algorithms list.
|
||||
*
|
||||
* @param cert certificate to check
|
||||
*
|
||||
* @throws CertPathBuilderException if algorithm is disabled or key size is
|
||||
* too small
|
||||
*/
|
||||
private void checkAlgorithmConstraints(X509Certificate cert)
|
||||
throws CertPathBuilderException {
|
||||
|
||||
String sigAlg = null;
|
||||
PublicKey pubKey = null;
|
||||
String propertyName = "jdk.certpath.disabledAlgorithms";
|
||||
|
||||
if (cert == null) {
|
||||
throw new CertPathBuilderException(
|
||||
"X509Certificate is null when checking algorithm constraints");
|
||||
}
|
||||
|
||||
/* Check signature algorithm against disabled list */
|
||||
sigAlg = cert.getSigAlgName();
|
||||
if (WolfCryptUtil.isAlgorithmDisabled(sigAlg, propertyName)) {
|
||||
log("Algorithm constraints check failed on signature algorithm: " +
|
||||
sigAlg);
|
||||
throw new CertPathBuilderException(
|
||||
"Algorithm constraints check failed on signature algorithm: " +
|
||||
sigAlg);
|
||||
}
|
||||
|
||||
/* Check public key algorithm and size against constraints */
|
||||
pubKey = cert.getPublicKey();
|
||||
if (!WolfCryptUtil.isKeyAllowed(pubKey, propertyName)) {
|
||||
log("Algorithm constraints check failed on public key: " +
|
||||
pubKey.getAlgorithm());
|
||||
throw new CertPathBuilderException(
|
||||
"Algorithm constraints check failed on public key: " +
|
||||
pubKey.getAlgorithm());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check trust anchor against disabled algorithms constraints from
|
||||
* security property jdk.certpath.disabledAlgorithms.
|
||||
*
|
||||
* Handles both trust anchors with certificates and those with only a
|
||||
* public key (no cert). Only checks the public key algorithm and size,
|
||||
* not the signature algorithm, since trust anchors are self-signed and
|
||||
* their signatures are inherently trusted (match SunJCE behavior).
|
||||
*
|
||||
* @param anchor trust anchor to check
|
||||
*
|
||||
* @throws CertPathBuilderException if key algorithm is disabled or key
|
||||
* size is too small
|
||||
*/
|
||||
private void checkTrustAnchorConstraints(TrustAnchor anchor)
|
||||
throws CertPathBuilderException {
|
||||
|
||||
PublicKey pubKey = null;
|
||||
String propertyName = "jdk.certpath.disabledAlgorithms";
|
||||
|
||||
if (anchor == null) {
|
||||
throw new CertPathBuilderException(
|
||||
"TrustAnchor is null when checking trust anchor constraints");
|
||||
}
|
||||
|
||||
X509Certificate cert = anchor.getTrustedCert();
|
||||
if (cert != null) {
|
||||
pubKey = cert.getPublicKey();
|
||||
}
|
||||
else {
|
||||
pubKey = anchor.getCAPublicKey();
|
||||
}
|
||||
|
||||
if (pubKey == null) {
|
||||
throw new CertPathBuilderException(
|
||||
"Trust anchor has no public key to check algo constraints");
|
||||
}
|
||||
|
||||
if (!WolfCryptUtil.isKeyAllowed(pubKey, propertyName)) {
|
||||
log("Algorithm constraints check failed on trust " +
|
||||
"anchor public key: " + pubKey.getAlgorithm());
|
||||
throw new CertPathBuilderException(
|
||||
"Algorithm constraints check failed on trust " +
|
||||
"anchor public key: " + pubKey.getAlgorithm());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if certificate uses a disabled algorithm.
|
||||
*
|
||||
* Same checks as checkAlgorithmConstraints() but returns boolean
|
||||
* instead of throwing, for use in filtering intermediate certificates.
|
||||
*
|
||||
* @param cert certificate to check
|
||||
*
|
||||
* @return true if certificate uses a disabled algorithm, false if allowed
|
||||
*/
|
||||
private boolean hasDisabledAlgorithm(X509Certificate cert) {
|
||||
|
||||
try {
|
||||
checkAlgorithmConstraints(cert);
|
||||
return false;
|
||||
}
|
||||
catch (CertPathBuilderException e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that each certificate in the path was signed by a key that meets
|
||||
* algorithm constraints. Walks the chain from target toward trust anchor,
|
||||
* checking each signer's public key against
|
||||
* jdk.certpath.disabledAlgorithms.
|
||||
*
|
||||
* @param path list of certificates (target at index 0)
|
||||
* @param anchor trust anchor that signed the last cert in path
|
||||
*
|
||||
* @throws CertPathBuilderException if a signer key is disabled
|
||||
*/
|
||||
private void checkSignerKeyConstraints(
|
||||
List<X509Certificate> path, TrustAnchor anchor)
|
||||
throws CertPathBuilderException {
|
||||
|
||||
String propertyName = "jdk.certpath.disabledAlgorithms";
|
||||
|
||||
for (int i = 0; i < path.size(); i++) {
|
||||
|
||||
PublicKey signerKey = null;
|
||||
|
||||
if (i < path.size() - 1) {
|
||||
/* Signer is the next cert in the path */
|
||||
signerKey = path.get(i + 1).getPublicKey();
|
||||
}
|
||||
else {
|
||||
/* Last cert in path is signed by trust anchor */
|
||||
X509Certificate anchorCert = anchor.getTrustedCert();
|
||||
if (anchorCert != null) {
|
||||
signerKey = anchorCert.getPublicKey();
|
||||
}
|
||||
else {
|
||||
signerKey = anchor.getCAPublicKey();
|
||||
}
|
||||
}
|
||||
|
||||
if (signerKey != null &&
|
||||
!WolfCryptUtil.isKeyAllowed(signerKey, propertyName)) {
|
||||
|
||||
String certName =
|
||||
path.get(i).getSubjectX500Principal().getName();
|
||||
log("Algorithm constraints check failed on signer key for: " +
|
||||
certName + ", signer key algorithm: " +
|
||||
signerKey.getAlgorithm());
|
||||
throw new CertPathBuilderException(
|
||||
"Algorithm constraints check failed on signer key for: " +
|
||||
certName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the target certificate based on the X509CertSelector in params.
|
||||
*
|
||||
* Searches through all CertStores for a certificate matching the
|
||||
* target constraints.
|
||||
* Searches for a certificate matching the target constraints in the
|
||||
* following order:
|
||||
* 1. Directly set in the selector via setCertificate()
|
||||
* 2. In CertStores attached to the parameters
|
||||
* 3. Among trust anchor certificates
|
||||
*
|
||||
* @param params PKIXBuilderParameters containing target selector
|
||||
*
|
||||
|
|
@ -136,8 +311,7 @@ public class WolfCryptPKIXCertPathBuilder extends CertPathBuilderSpi {
|
|||
List<CertStore> certStores = null;
|
||||
|
||||
if (params == null) {
|
||||
throw new CertPathBuilderException(
|
||||
"PKIXBuilderParameters is null");
|
||||
throw new CertPathBuilderException("PKIXBuilderParameters is null");
|
||||
}
|
||||
|
||||
selector = params.getTargetCertConstraints();
|
||||
|
|
@ -149,11 +323,13 @@ public class WolfCryptPKIXCertPathBuilder extends CertPathBuilderSpi {
|
|||
|
||||
if (!(selector instanceof X509CertSelector)) {
|
||||
throw new CertPathBuilderException(
|
||||
"Target certificate constraints must be X509CertSelector");
|
||||
"Target certificate constraints must be " +
|
||||
"X509CertSelector");
|
||||
}
|
||||
|
||||
x509Selector = (X509CertSelector)selector;
|
||||
log("searching for target certificate with selector: " + x509Selector);
|
||||
log("searching for target certificate with selector: " +
|
||||
x509Selector);
|
||||
|
||||
/* Check if target cert is directly set in selector */
|
||||
targetCert = x509Selector.getCertificate();
|
||||
|
|
@ -164,32 +340,42 @@ public class WolfCryptPKIXCertPathBuilder extends CertPathBuilderSpi {
|
|||
|
||||
/* Search through CertStores for matching certificate */
|
||||
certStores = params.getCertStores();
|
||||
if (certStores == null || certStores.isEmpty()) {
|
||||
throw new CertPathBuilderException(
|
||||
"No CertStores provided in PKIXBuilderParameters");
|
||||
if (certStores != null) {
|
||||
for (CertStore store : certStores) {
|
||||
try {
|
||||
Collection<? extends Certificate> certs =
|
||||
store.getCertificates(x509Selector);
|
||||
|
||||
if (certs != null && !certs.isEmpty()) {
|
||||
targetCert = (X509Certificate)certs.iterator().next();
|
||||
log("found target certificate in CertStore: " +
|
||||
targetCert.getSubjectX500Principal().getName());
|
||||
return targetCert;
|
||||
}
|
||||
|
||||
} catch (CertStoreException e) {
|
||||
log("error searching CertStore: " + e.getMessage());
|
||||
/* Continue to next store */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (CertStore store : certStores) {
|
||||
try {
|
||||
Collection<? extends Certificate> certs =
|
||||
store.getCertificates(x509Selector);
|
||||
/* Search trust anchor certificates as fallback */
|
||||
Set<TrustAnchor> anchors = params.getTrustAnchors();
|
||||
if (anchors != null) {
|
||||
for (TrustAnchor anchor : anchors) {
|
||||
X509Certificate anchorCert = anchor.getTrustedCert();
|
||||
if ((anchorCert != null) && x509Selector.match(anchorCert)) {
|
||||
|
||||
if (certs != null && !certs.isEmpty()) {
|
||||
/* Return first matching certificate */
|
||||
targetCert = (X509Certificate) certs.iterator().next();
|
||||
log("found target certificate: " +
|
||||
targetCert.getSubjectX500Principal().getName());
|
||||
return targetCert;
|
||||
log("found target certificate in trust anchors: " +
|
||||
anchorCert.getSubjectX500Principal().getName());
|
||||
return anchorCert;
|
||||
}
|
||||
|
||||
} catch (CertStoreException e) {
|
||||
log("error searching CertStore: " + e.getMessage());
|
||||
/* Continue to next store */
|
||||
}
|
||||
}
|
||||
|
||||
throw new CertPathBuilderException(
|
||||
"Target certificate not found in CertStores");
|
||||
"Target certificate not found matching selector");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -545,7 +731,8 @@ public class WolfCryptPKIXCertPathBuilder extends CertPathBuilderSpi {
|
|||
*
|
||||
* Searches through provided CertStores for CA certificates
|
||||
* (basicConstraints >= 0) and returns them as DER-encoded byte arrays.
|
||||
* Trust anchor certificates are excluded from the results.
|
||||
* Certificates are excluded if they are trust anchors or use disabled
|
||||
* algorithms per jdk.certpath.disabledAlgorithms.
|
||||
*
|
||||
* @param certStores list of CertStores to search
|
||||
* @param anchors set of trust anchors to exclude
|
||||
|
|
@ -572,7 +759,8 @@ public class WolfCryptPKIXCertPathBuilder extends CertPathBuilderSpi {
|
|||
for (Certificate c : certs) {
|
||||
if (c instanceof X509Certificate) {
|
||||
X509Certificate x509 = (X509Certificate) c;
|
||||
/* Skip if it's a trust anchor */
|
||||
|
||||
/* Skip if trust anchor */
|
||||
boolean isTrustAnchor = false;
|
||||
for (TrustAnchor anchor : anchors) {
|
||||
X509Certificate ac = anchor.getTrustedCert();
|
||||
|
|
@ -581,11 +769,16 @@ public class WolfCryptPKIXCertPathBuilder extends CertPathBuilderSpi {
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (!isTrustAnchor) {
|
||||
intermediatesDer.add(x509.getEncoded());
|
||||
log("collected intermediate: " +
|
||||
x509.getSubjectX500Principal().getName());
|
||||
|
||||
/* Skip adding if cert uses disabled algorithm */
|
||||
if (isTrustAnchor || hasDisabledAlgorithm(x509)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Convert to DER and add to list */
|
||||
intermediatesDer.add(x509.getEncoded());
|
||||
log("collected intermediate: " +
|
||||
x509.getSubjectX500Principal().getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -635,34 +828,19 @@ public class WolfCryptPKIXCertPathBuilder extends CertPathBuilderSpi {
|
|||
try {
|
||||
storeCtx = new WolfSSLX509StoreCtx();
|
||||
|
||||
/* If a custom validation date is specified, we skip date
|
||||
* validation when adding certificates, then set the custom
|
||||
* verification time for chain verification.
|
||||
*
|
||||
* This allows for testing/use of expired certs if desired,
|
||||
* or validating against a specific date.
|
||||
*
|
||||
* SunJCE only validates expiration dates on chain verification,
|
||||
* not cert loading, so our default behavior already does some
|
||||
* extra validation here in the case when a custom date is not
|
||||
* set. */
|
||||
/* If a custom validation date is specified, set NO_CHECK_TIME so
|
||||
* expired certs can be loaded into the store, then set the custom
|
||||
* verify time. Native code clears NO_CHECK_TIME on the ctx when
|
||||
* USE_CHECK_TIME is set, so chain verification still validates
|
||||
* dates against the custom time. */
|
||||
if (validationDate != null) {
|
||||
if (!WolfSSLX509StoreCtx.isStoreCheckTimeSupported()) {
|
||||
throw new CertPathBuilderException(
|
||||
"PKIXBuilderParameters.setDate() requires " +
|
||||
"a wolfSSL version that supports X509_STORE " +
|
||||
"check_time propagation (> 5.8.4)");
|
||||
}
|
||||
log("using custom validation date: " + validationDate);
|
||||
storeCtx.setFlags(
|
||||
WolfSSLX509StoreCtx.WOLFSSL_NO_CHECK_TIME);
|
||||
storeCtx.setVerificationTime(
|
||||
validationDate.getTime() / 1000);
|
||||
storeCtx.setFlags(WolfSSLX509StoreCtx.WOLFSSL_NO_CHECK_TIME);
|
||||
storeCtx.setVerificationTime(validationDate.getTime() / 1000);
|
||||
}
|
||||
|
||||
/* Add trust anchors to the store */
|
||||
for (TrustAnchor anchor : anchors) {
|
||||
|
||||
X509Certificate anchorCert = anchor.getTrustedCert();
|
||||
if (anchorCert != null) {
|
||||
byte[] anchorDer;
|
||||
|
|
@ -680,9 +858,10 @@ public class WolfCryptPKIXCertPathBuilder extends CertPathBuilderSpi {
|
|||
}
|
||||
}
|
||||
|
||||
/* Collect all intermediate certificates from CertStores */
|
||||
List<byte[]> intermediatesDer =
|
||||
collectIntermediateCertificates(certStores, anchors);
|
||||
/* Collect all intermediate certificates from CertStores,
|
||||
* filtering disabled algorithms */
|
||||
List<byte[]> intermediatesDer = collectIntermediateCertificates(
|
||||
certStores, anchors);
|
||||
|
||||
/* Convert target cert to DER */
|
||||
byte[] targetDer;
|
||||
|
|
@ -698,8 +877,7 @@ public class WolfCryptPKIXCertPathBuilder extends CertPathBuilderSpi {
|
|||
byte[][] intermediatesArray = null;
|
||||
if (!intermediatesDer.isEmpty()) {
|
||||
int size = intermediatesDer.size();
|
||||
intermediatesArray =
|
||||
intermediatesDer.toArray(new byte[size][]);
|
||||
intermediatesArray = intermediatesDer.toArray(new byte[size][]);
|
||||
}
|
||||
|
||||
/* Build and verify the chain */
|
||||
|
|
@ -734,9 +912,9 @@ public class WolfCryptPKIXCertPathBuilder extends CertPathBuilderSpi {
|
|||
log("native chain building returned " + fullChain.size() +
|
||||
" certificate(s)");
|
||||
|
||||
/* Chain includes target->intermediates->trust anchor.
|
||||
* We need to separate the trust anchor from the path.
|
||||
* The last cert in the chain should match a trust anchor. */
|
||||
/* Chain includes target->intermediates->trust anchor. Separate
|
||||
* trust anchor from the path. Last cert in chain should match a
|
||||
* trust anchor */
|
||||
if (fullChain.isEmpty()) {
|
||||
throw new CertPathBuilderException(
|
||||
"Native chain building returned empty chain");
|
||||
|
|
@ -752,7 +930,7 @@ public class WolfCryptPKIXCertPathBuilder extends CertPathBuilderSpi {
|
|||
anchorCert.getSubjectX500Principal().equals(
|
||||
lastCert.getSubjectX500Principal())) {
|
||||
|
||||
/* Verify it's the same cert (compare encoded) */
|
||||
/* Verify same cert (compare encoded) */
|
||||
try {
|
||||
if (Arrays.equals(anchorCert.getEncoded(),
|
||||
lastCert.getEncoded())) {
|
||||
|
|
@ -931,6 +1109,9 @@ public class WolfCryptPKIXCertPathBuilder extends CertPathBuilderSpi {
|
|||
log("target certificate is a trust anchor, " +
|
||||
"returning empty path");
|
||||
|
||||
/* Check trust anchor public key constraints */
|
||||
checkTrustAnchorConstraints(anchor);
|
||||
|
||||
try {
|
||||
CertificateFactory cf =
|
||||
CertificateFactory.getInstance("X.509");
|
||||
|
|
@ -946,11 +1127,34 @@ public class WolfCryptPKIXCertPathBuilder extends CertPathBuilderSpi {
|
|||
}
|
||||
}
|
||||
|
||||
/* Build and verify path using wolfSSL X509_STORE */
|
||||
NativeChainResult result = buildAndVerifyPathNative(
|
||||
targetCert, pkixParams);
|
||||
path = result.path;
|
||||
trustAnchor = result.trustAnchor;
|
||||
/* Check target cert algorithm constraints before building */
|
||||
checkAlgorithmConstraints(targetCert);
|
||||
|
||||
if (pkixParams.getDate() != null &&
|
||||
!WolfSSLX509StoreCtx.isStoreCheckTimeSupported()) {
|
||||
/* Native builder can't handle custom dates on this wolfSSL version
|
||||
* (check_time not supported in X509_STORE). Fall back to Java
|
||||
* chain building. Date validation deferred to CertPathValidator. */
|
||||
log("using Java chain building, native check_time not supported");
|
||||
path = buildPath(targetCert, pkixParams);
|
||||
trustAnchor = findPathTrustAnchor(path,
|
||||
pkixParams.getTrustAnchors());
|
||||
|
||||
/* Check algorithm constraints for all certs in path (native path
|
||||
* filters during collectIntermediateCerts) */
|
||||
for (X509Certificate cert : path) {
|
||||
checkAlgorithmConstraints(cert);
|
||||
}
|
||||
} else {
|
||||
/* Build and verify path using wolfSSL X509_STORE */
|
||||
NativeChainResult result =
|
||||
buildAndVerifyPathNative(targetCert, pkixParams);
|
||||
path = result.path;
|
||||
trustAnchor = result.trustAnchor;
|
||||
}
|
||||
|
||||
/* Check that each signer key meets constraints. */
|
||||
checkSignerKeyConstraints(path, trustAnchor);
|
||||
|
||||
try {
|
||||
/* Convert path to CertPath object */
|
||||
|
|
@ -964,7 +1168,7 @@ public class WolfCryptPKIXCertPathBuilder extends CertPathBuilderSpi {
|
|||
|
||||
log("successfully built path with " + path.size() + " certificate(s)");
|
||||
|
||||
/* PolicyNode not returned, certificate policies are not supported */
|
||||
/* PolicyNode not returned, cert policies not supported */
|
||||
return new PKIXCertPathBuilderResult(certPath, trustAnchor,
|
||||
null, targetCert.getPublicKey());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,8 +26,6 @@ import java.util.List;
|
|||
import java.util.Set;
|
||||
import java.util.Collection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Date;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.PublicKey;
|
||||
|
|
@ -60,6 +58,9 @@ import com.wolfssl.wolfcrypt.WolfCryptError;
|
|||
import com.wolfssl.wolfcrypt.WolfSSLCertManager;
|
||||
import com.wolfssl.wolfcrypt.WolfSSLCertManagerVerifyCallback;
|
||||
import com.wolfssl.wolfcrypt.WolfCryptException;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.CertificateException;
|
||||
|
||||
/**
|
||||
* wolfJCE implementation of CertPathValidator for PKIX (X.509)
|
||||
|
|
@ -83,50 +84,42 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
|
|||
* Inner class implementing verification callback for date override.
|
||||
*
|
||||
* This callback is registered with WolfSSLCertManager when
|
||||
* PKIXParameters.getDate() returns a non-null override date. It
|
||||
* intercepts certificate date validation errors and re-validates
|
||||
* the certificate dates against the override date instead of the
|
||||
* current system time.
|
||||
* PKIXParameters.getDate() returns a non-null override date. It intercepts
|
||||
* certificate date validation errors and re-validates the certificate dates
|
||||
* against the override date instead of the current system time.
|
||||
*
|
||||
* For thread safety, each CertPathValidator creates its own callback
|
||||
* instance with its own certificate map and override date.
|
||||
* instance with its own override date.
|
||||
*/
|
||||
private class DateOverrideVerifyCallback
|
||||
implements WolfSSLCertManagerVerifyCallback {
|
||||
|
||||
private final Date overrideDate;
|
||||
private final Map<Integer, X509Certificate> certsByDepth;
|
||||
private final CertificateFactory certFactory;
|
||||
|
||||
/* X509 error codes wolfSSL maps date errors to when
|
||||
* OPENSSL_COMPATIBLE_DEFAULTS is defined. Values from wolfssl/ssl.h,
|
||||
* WOLFSSL_X509_V_ERR enum. */
|
||||
private static final int X509_V_ERR_CERT_NOT_YET_VALID = 9;
|
||||
private static final int X509_V_ERR_CERT_HAS_EXPIRED = 10;
|
||||
|
||||
/**
|
||||
* Create new DateOverrideVerifyCallback.
|
||||
*
|
||||
* @param date Override date to use for validation
|
||||
* @param certs List of certificates in chain, ordered from
|
||||
* end-entity (index 0) to root
|
||||
*
|
||||
* @throws CertificateException if X.509 CertificateFactory
|
||||
* is not available
|
||||
*/
|
||||
public DateOverrideVerifyCallback(
|
||||
Date date, List<X509Certificate> certs) {
|
||||
public DateOverrideVerifyCallback(Date date)
|
||||
throws CertificateException {
|
||||
|
||||
this.overrideDate = date;
|
||||
this.certsByDepth = new HashMap<>();
|
||||
|
||||
/* Map certificates by depth for lookup in callback.
|
||||
* Depth 0 = end entity cert, increasing depth toward root. */
|
||||
for (int i = 0; i < certs.size(); i++) {
|
||||
certsByDepth.put(i, certs.get(i));
|
||||
}
|
||||
this.certFactory = CertificateFactory.getInstance("X.509");
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify callback implementation that overrides date validation.
|
||||
*
|
||||
* When a date validation error occurs (ASN_BEFORE_DATE_E or
|
||||
* ASN_AFTER_DATE_E), this checks if the override date falls
|
||||
* within the certificate's validity period. If so, the error is
|
||||
* overridden and verification continues. For all other errors,
|
||||
* the original preverify result is used.
|
||||
*
|
||||
* This callback is called from native JNI.
|
||||
* Delegates to 4-arg verify with null certDer.
|
||||
*
|
||||
* @param preverify 1 if pre-verification passed, 0 if failed
|
||||
* @param error Error code from verification (0 = no error)
|
||||
|
|
@ -136,68 +129,99 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
|
|||
*/
|
||||
@Override
|
||||
public int verify(int preverify, int error, int errorDepth) {
|
||||
return verify(preverify, error, errorDepth, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify callback that enforces override date validation.
|
||||
*
|
||||
* This callback is called from native JNI with the DER-encoded
|
||||
* certificate bytes from the WOLFSSL_X509_STORE_CTX.
|
||||
*
|
||||
* Validates the certificate against the override date. For date errors
|
||||
* where the override date is valid, the error is overridden. Non date
|
||||
* errors defer to preverify.
|
||||
*
|
||||
* Parses the DER-encoded certificate bytes provided by the native
|
||||
* callback to obtain the certificate validity dates.
|
||||
*
|
||||
* Date errors are checked as both raw ASN error codes
|
||||
* (ASN_BEFORE_DATE_E, ASN_AFTER_DATE_E) and X509 codes
|
||||
* (X509_V_ERR_CERT_NOT_YET_VALID, X509_V_ERR_CERT_HAS_EXPIRED)
|
||||
* since wolfSSL maps ASN codes to X509 codes when
|
||||
* OPENSSL_COMPATIBLE_DEFAULTS is defined.
|
||||
*
|
||||
* @param preverify 1 if pre-verification passed, 0 if failed
|
||||
* @param error Error code from verification (0 = no error)
|
||||
* @param errorDepth Certificate depth in chain (0 = end entity)
|
||||
* @param certDer DER-encoded certificate at errorDepth, or
|
||||
* null if not available
|
||||
*
|
||||
* @return 1 to accept certificate, 0 to reject
|
||||
*/
|
||||
@Override
|
||||
public int verify(int preverify, int error, int errorDepth,
|
||||
byte[] certDer) {
|
||||
|
||||
Date notBefore;
|
||||
Date notAfter;
|
||||
X509Certificate cert;
|
||||
X509Certificate cert = null;
|
||||
|
||||
/* Get the certificate at this depth */
|
||||
cert = certsByDepth.get(errorDepth);
|
||||
if (cert == null) {
|
||||
/* Reject if cert not found */
|
||||
log("Date override: cert not found at depth " + errorDepth +
|
||||
", rejecting");
|
||||
return 0;
|
||||
/* Parse cert from DER bytes provided by native callback */
|
||||
if (certDer != null) {
|
||||
try {
|
||||
cert = (X509Certificate)certFactory.generateCertificate(
|
||||
new ByteArrayInputStream(certDer));
|
||||
} catch (CertificateException e) {
|
||||
log("Date override: failed to parse cert DER at depth " +
|
||||
errorDepth + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/* Get certificate validity dates */
|
||||
/* If no cert available, defer to native result */
|
||||
if (cert == null) {
|
||||
log("Date override: no cert available at depth " + errorDepth +
|
||||
", returning preverify: " + preverify);
|
||||
return preverify;
|
||||
}
|
||||
|
||||
/* Get cert validity dates */
|
||||
notBefore = cert.getNotBefore();
|
||||
notAfter = cert.getNotAfter();
|
||||
|
||||
/* When date override is active, always validate against the
|
||||
* override date, not system time. */
|
||||
|
||||
/* If date error exists but override date is valid, accept */
|
||||
if (error == WolfCryptError.ASN_BEFORE_DATE_E.getCode() ||
|
||||
error == WolfCryptError.ASN_AFTER_DATE_E.getCode()) {
|
||||
|
||||
/* Override date must be within cert validity */
|
||||
if (overrideDate.before(notBefore) ||
|
||||
overrideDate.after(notAfter)) {
|
||||
log("Date override: override date " + overrideDate +
|
||||
" outside validity window (notBefore: " + notBefore +
|
||||
", notAfter: " + notAfter + "), rejecting");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Always check override date against cert validity. Override date
|
||||
* may be outside cert validity window. */
|
||||
if (overrideDate.before(notBefore) ||
|
||||
overrideDate.after(notAfter)) {
|
||||
log("Date override: override date " + overrideDate +
|
||||
" within validity, accepting despite date error");
|
||||
" outside validity window (notBefore: " + notBefore +
|
||||
", notAfter: " + notAfter + "), rejecting");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Override date is within cert validity window. Override native
|
||||
* date error if present. For non-date errors, defer to preverify.
|
||||
*
|
||||
* Check both raw ASN codes and X509 mapped codes since wolfSSL
|
||||
* maps ASN to X509 error codes with OPENSSL_COMPATIBLE_DEFAULTS. */
|
||||
if (error == 0 || preverify == 1) {
|
||||
return preverify;
|
||||
}
|
||||
|
||||
if (error == WolfCryptError.ASN_BEFORE_DATE_E.getCode() ||
|
||||
error == WolfCryptError.ASN_AFTER_DATE_E.getCode() ||
|
||||
error == X509_V_ERR_CERT_NOT_YET_VALID ||
|
||||
error == X509_V_ERR_CERT_HAS_EXPIRED) {
|
||||
log("Date override: override date " + overrideDate +
|
||||
" within validity, accepting despite date error " + error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* No date error, but still need to validate override date.
|
||||
* If override date is outside cert validity, reject even though
|
||||
* current system time might be valid. */
|
||||
if (overrideDate.before(notBefore)) {
|
||||
log("Date override: override date " + overrideDate +
|
||||
" before cert validity (notBefore: " + notBefore +
|
||||
"), rejecting");
|
||||
return 0;
|
||||
}
|
||||
/* Non-date error, defer to preverify */
|
||||
log("Date override: non-date error " + error + " at depth " +
|
||||
errorDepth + ", returning preverify: " + preverify);
|
||||
|
||||
if (overrideDate.after(notAfter)) {
|
||||
log("Date override: override date " + overrideDate +
|
||||
" after cert validity (notAfter: " + notAfter +
|
||||
"), rejecting");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Override date is valid, accept */
|
||||
log("Date override: override date " + overrideDate +
|
||||
" within validity, accepting");
|
||||
|
||||
return 1;
|
||||
return preverify;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -211,7 +235,7 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
|
|||
/**
|
||||
* Check CertPathParameters matches our requirements.
|
||||
* 1. Not null
|
||||
* 2. Is an instance of PKIXParameters
|
||||
* 2. Instance of PKIXParameters
|
||||
*
|
||||
* @throws InvalidAlgorithmParameterException if null or not an instance
|
||||
* of PKIXParameters
|
||||
|
|
@ -350,8 +374,8 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
|
|||
}
|
||||
|
||||
/**
|
||||
* Check certificate against disabled algorithms constraints from
|
||||
* security property jdk.certpath.disabledAlgorithms.
|
||||
* Check certificate against disabled algorithms constraints from security
|
||||
* property jdk.certpath.disabledAlgorithms.
|
||||
*
|
||||
* Validates both the signature algorithm and public key algorithm/size
|
||||
* against the disabled algorithms list.
|
||||
|
|
@ -378,12 +402,10 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
|
|||
/* Check signature algorithm against disabled list */
|
||||
sigAlg = cert.getSigAlgName();
|
||||
if (WolfCryptUtil.isAlgorithmDisabled(sigAlg, propertyName)) {
|
||||
log("Algorithm constraints check failed on signature " +
|
||||
"algorithm: " + sigAlg);
|
||||
log("Algorithm constraints check failed on sig algo: " + sigAlg);
|
||||
throw new CertPathValidatorException(
|
||||
"Algorithm constraints check failed on signature " +
|
||||
"algorithm: " + sigAlg, null, path, certIdx,
|
||||
BasicReason.ALGORITHM_CONSTRAINED);
|
||||
"Algorithm constraints check failed on sig algo: " + sigAlg,
|
||||
null, path, certIdx, BasicReason.ALGORITHM_CONSTRAINED);
|
||||
}
|
||||
|
||||
/* Check public key algorithm and size against constraints */
|
||||
|
|
@ -397,6 +419,53 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check trust anchor against disabled algorithms constraints from
|
||||
* security property jdk.certpath.disabledAlgorithms.
|
||||
*
|
||||
* Handle both trust anchors with certificates and those with only a
|
||||
* public key (no cert).
|
||||
*
|
||||
* @param anchor trust anchor to check
|
||||
*
|
||||
* @throws CertPathValidatorException if key algorithm is disabled or key
|
||||
* size is too small
|
||||
*/
|
||||
private void checkTrustAnchorConstraints(TrustAnchor anchor)
|
||||
throws CertPathValidatorException {
|
||||
|
||||
PublicKey pubKey = null;
|
||||
String propertyName = "jdk.certpath.disabledAlgorithms";
|
||||
|
||||
if (anchor == null) {
|
||||
throw new CertPathValidatorException(
|
||||
"TrustAnchor is null when checking trust anchor constraints");
|
||||
}
|
||||
|
||||
X509Certificate cert = anchor.getTrustedCert();
|
||||
if (cert != null) {
|
||||
pubKey = cert.getPublicKey();
|
||||
}
|
||||
else {
|
||||
pubKey = anchor.getCAPublicKey();
|
||||
}
|
||||
|
||||
if (pubKey == null) {
|
||||
throw new CertPathValidatorException(
|
||||
"Trust anchor has no public key to check against " +
|
||||
"algo constraints");
|
||||
}
|
||||
|
||||
if (!WolfCryptUtil.isKeyAllowed(pubKey, propertyName)) {
|
||||
log("Algo constraints check failed on trust anchor public key: " +
|
||||
pubKey.getAlgorithm());
|
||||
throw new CertPathValidatorException(
|
||||
"Algo constraints check failed on trust anchor public key: " +
|
||||
pubKey.getAlgorithm(), null, null, -1,
|
||||
BasicReason.ALGORITHM_CONSTRAINED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check X509Certificate against constraints or settings inside
|
||||
* PKIXParameters.
|
||||
|
|
@ -423,9 +492,8 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
|
|||
/* Check target cert constraints, if set in parameters */
|
||||
checkTargetCertConstraints(cert, certIdx, path, params);
|
||||
|
||||
/* Certificate policies are not currently supported by this
|
||||
* CertPathValidator implementation, throw exceptions when
|
||||
* user tries to use them. */
|
||||
/* Certificate policies are not currently supported by this validator,
|
||||
* throw exceptions when user tries to use them. */
|
||||
disallowCertPolicyUse(params);
|
||||
}
|
||||
|
||||
|
|
@ -520,13 +588,16 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
|
|||
* Load TrustAnchors from PKIXParameters into WolfSSLCertManager as
|
||||
* trusted CA certificates.
|
||||
*
|
||||
* Trust anchors are always loaded with WOLFSSL_LOAD_FLAG_DATE_ERR_OKAY
|
||||
* since per RFC 5280 trust anchors should not be date-validated.
|
||||
*
|
||||
* @param params PKIXParameters from which to get TrustAnchor Set
|
||||
* @param cm WolfSSLCertManager to load TrustAnchors into as trusted roots
|
||||
*
|
||||
* @throws CertPathValidatorException on failure to load trust anchors
|
||||
*/
|
||||
private void loadTrustAnchorsIntoCertManager(
|
||||
PKIXParameters params, WolfSSLCertManager cm)
|
||||
private void loadTrustAnchorsIntoCertManager(PKIXParameters params,
|
||||
WolfSSLCertManager cm)
|
||||
throws CertPathValidatorException {
|
||||
|
||||
Set<TrustAnchor> trustAnchors = null;
|
||||
|
|
@ -554,13 +625,21 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
|
|||
X509Certificate anchorCert = anchor.getTrustedCert();
|
||||
if (anchorCert != null) {
|
||||
try {
|
||||
cm.CertManagerLoadCA(anchorCert);
|
||||
cm.CertManagerLoadCA(anchorCert,
|
||||
WolfSSLCertManager.WOLFSSL_LOAD_FLAG_DATE_ERR_OKAY);
|
||||
|
||||
log("loaded TrustAnchor: " +
|
||||
anchorCert.getSubjectX500Principal().getName());
|
||||
|
||||
} catch (WolfCryptException e) {
|
||||
throw new CertPathValidatorException(e);
|
||||
/* In wolfSSL <= 5.8.4 with WOLFSSL_TRUST_PEER_CERT and
|
||||
* OPENSSL_COMPATIBLE_DEFAULTS, a date error from trusted
|
||||
* peer loading may overwrite the successful CA load return
|
||||
* code. The CA is still loaded into the CertManager despite
|
||||
* the error return, so we just log and continue here. */
|
||||
log("TrustAnchor load returned error for " +
|
||||
anchorCert.getSubjectX500Principal().getName() +
|
||||
", CA may still be loaded: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -604,18 +683,21 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
|
|||
"Failed verification on certificate", e, path, i);
|
||||
}
|
||||
|
||||
/* Verified successfully. If this is a CA and we have more
|
||||
* certs, load this as trusted (intermediate) */
|
||||
/* Verified successfully. If this is a CA and we have more certs,
|
||||
* load this as trusted (intermediate). Use DATE_ERR_OKAY since
|
||||
* this cert has already been date-validated above. */
|
||||
if (i > 0 && cert.getBasicConstraints() >= 0) {
|
||||
try {
|
||||
cm.CertManagerLoadCA(cert);
|
||||
cm.CertManagerLoadCA(cert,
|
||||
WolfSSLCertManager.WOLFSSL_LOAD_FLAG_DATE_ERR_OKAY);
|
||||
|
||||
log("chain [" + i + "] is intermediate, loading as root");
|
||||
|
||||
} catch (WolfCryptException e) {
|
||||
|
||||
log("chain [" + i + "] is CA, but failed to load as " +
|
||||
"trusted root, not loading");
|
||||
/* CA may still be loaded despite error from trusted peer
|
||||
* loading on older wolfSSL */
|
||||
log("chain [" + i + "] intermediate load returned error, " +
|
||||
"CA may still be loaded: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -672,13 +754,10 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
|
|||
overrideDate = params.getDate();
|
||||
if (overrideDate != null) {
|
||||
try {
|
||||
/* Create simple single-cert callback for this verification */
|
||||
List<X509Certificate> singleCert = new ArrayList<>();
|
||||
singleCert.add(cert);
|
||||
DateOverrideVerifyCallback callback =
|
||||
new DateOverrideVerifyCallback(overrideDate, singleCert);
|
||||
new DateOverrideVerifyCallback(overrideDate);
|
||||
cm.setVerifyCallback(callback);
|
||||
} catch (WolfCryptException e) {
|
||||
} catch (WolfCryptException | CertificateException e) {
|
||||
cm.free();
|
||||
throw new CertPathValidatorException(
|
||||
"Failed to set date override callback in findTrustAnchor");
|
||||
|
|
@ -710,11 +789,18 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
|
|||
}
|
||||
|
||||
try {
|
||||
/* Load anchor as CA */
|
||||
cm.CertManagerLoadCA(anchorCert);
|
||||
/* Load anchor as CA, skip date validation per RFC 5280 (trust
|
||||
* anchors not date-validated) */
|
||||
cm.CertManagerLoadCA(anchorCert,
|
||||
WolfSSLCertManager.WOLFSSL_LOAD_FLAG_DATE_ERR_OKAY);
|
||||
} catch (WolfCryptException e) {
|
||||
/* error loading CA, skip to next */
|
||||
continue;
|
||||
/* In wolfSSL <= 5.8.4 with WOLFSSL_TRUST_PEER_CERT and
|
||||
* OPENSSL_COMPATIBLE_DEFAULTS, a date error from trusted
|
||||
* peer loading may overwrite the successful CA load return
|
||||
* code. The CA is still loaded into the CertManager despite
|
||||
* the error return, so we just log and continue here. */
|
||||
log("findTrustAnchor load error, CA may still be loaded: " +
|
||||
e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
@ -764,14 +850,14 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
|
|||
* Check if revocation has been enabled in PKIXParameters, and if so
|
||||
* find and load any CRLs in params.getCertStores().
|
||||
*
|
||||
* When a PKIXRevocationChecker has been registered via
|
||||
* addCertPathChecker(), that checker handles revocation checking. CRL
|
||||
* checking in the native CertManager is only enabled if:
|
||||
* When a PKIXRevocationChecker is registered via addCertPathChecker(),
|
||||
* that checker handles revocation checking. CRL checking in the native
|
||||
* CertManager is only enabled if:
|
||||
* - No PKIXRevocationChecker is present (default CRL behavior), or
|
||||
* - PKIXRevocationChecker has PREFER_CRLS option set
|
||||
*
|
||||
* @param params parameters used to check if revocation is enabled
|
||||
* and, if so load any CRLs available
|
||||
* @param params parameters used to check if revocation is enabled and,
|
||||
* if so load any CRLs available
|
||||
* @param cm WolfSSLCertManager to load CRLs into
|
||||
* @param certPath the CertPath being validated (for exception reporting)
|
||||
* @param certs list of certificates from certPath
|
||||
|
|
@ -780,9 +866,8 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
|
|||
* @throws CertPathValidatorException if error is encountered during
|
||||
* revocation checking or CRL loading
|
||||
*/
|
||||
private void checkRevocationEnabledAndLoadCRLs(
|
||||
PKIXParameters params, WolfSSLCertManager cm,
|
||||
CertPath certPath, List<X509Certificate> certs,
|
||||
private void checkRevocationEnabledAndLoadCRLs(PKIXParameters params,
|
||||
WolfSSLCertManager cm, CertPath certPath, List<X509Certificate> certs,
|
||||
List<PKIXCertPathChecker> pathCheckers)
|
||||
throws CertPathValidatorException {
|
||||
|
||||
|
|
@ -810,9 +895,8 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
|
|||
(WolfCryptPKIXRevocationChecker)checker;
|
||||
Set<PKIXRevocationChecker.Option> options =
|
||||
revChecker.getOptions();
|
||||
if (options != null &&
|
||||
options.contains(
|
||||
PKIXRevocationChecker.Option.PREFER_CRLS)) {
|
||||
if (options != null && options.contains(
|
||||
PKIXRevocationChecker.Option.PREFER_CRLS)) {
|
||||
preferCrls = true;
|
||||
}
|
||||
break;
|
||||
|
|
@ -827,8 +911,8 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
|
|||
}
|
||||
|
||||
if (params.isRevocationEnabled()) {
|
||||
log("revocation enabled in PKIXParameters, checking " +
|
||||
"for CRLs to load");
|
||||
log("revocation enabled in PKIXParameters, checking for CRLs " +
|
||||
"to load");
|
||||
|
||||
if (!WolfCrypt.CrlEnabled()) {
|
||||
throw new CertPathValidatorException(
|
||||
|
|
@ -872,8 +956,8 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
|
|||
certCount++;
|
||||
} catch (WolfCryptException e) {
|
||||
/* Log but not hard fail */
|
||||
log("Warning: failed to load cert from " +
|
||||
"CertStore: " + e.getMessage());
|
||||
log("Failed to load cert from CertStore: " +
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1022,6 +1106,9 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
|
|||
log("Zero-length cert path, returning trust anchor: " +
|
||||
anchorCert.getSubjectX500Principal().getName());
|
||||
|
||||
/* Check trust anchor public key constraints */
|
||||
checkTrustAnchorConstraints(anchor);
|
||||
|
||||
return new PKIXCertPathValidatorResult(anchor, null,
|
||||
anchorCert.getPublicKey());
|
||||
}
|
||||
|
|
@ -1035,10 +1122,9 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
|
|||
}
|
||||
|
||||
try {
|
||||
/* Get List of Certificate objects in CertPath, sanity check
|
||||
* that they are X509Certificate instances. This needs to be
|
||||
* done before date override callback registration since
|
||||
* callback needs access to certificate list. */
|
||||
/* Get List of Certificate objects in CertPath, sanity check that
|
||||
* they are X509Certificate instances. Done before date override
|
||||
* callback registration since callback needs access to cert list */
|
||||
certs = new ArrayList<>();
|
||||
for (Certificate cert : certPath.getCertificates()) {
|
||||
if (cert instanceof X509Certificate) {
|
||||
|
|
@ -1047,33 +1133,32 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
|
|||
}
|
||||
|
||||
/* Register verify callback to override date validation if
|
||||
* PKIXParameters specifies an override date */
|
||||
* PKIXParameters specifies an override date. Callback checks certs
|
||||
* against the override date. */
|
||||
if (pkixParams.getDate() != null) {
|
||||
try {
|
||||
DateOverrideVerifyCallback callback =
|
||||
new DateOverrideVerifyCallback(
|
||||
pkixParams.getDate(), certs);
|
||||
cm.setVerifyCallback(callback);
|
||||
cm.setVerifyCallback(new DateOverrideVerifyCallback(
|
||||
pkixParams.getDate()));
|
||||
|
||||
log("Registered date override callback for " +
|
||||
"validation date: " + pkixParams.getDate());
|
||||
log("Registered date override callback for validation " +
|
||||
"date: " + pkixParams.getDate());
|
||||
|
||||
} catch (WolfCryptException e) {
|
||||
} catch (WolfCryptException | CertificateException e) {
|
||||
throw new CertPathValidatorException(
|
||||
"Failed to register date override callback: " +
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/* Load trust anchors into CertManager from PKIXParameters.
|
||||
* This must happen before initializing cert path checkers since
|
||||
* OCSP validation requires trust anchors to verify responses. */
|
||||
/* Load trust anchors into CertManager from PKIXParameters. Done
|
||||
* before initializing cert path checkers since OCSP validation
|
||||
* requires trust anchors to verify responses. */
|
||||
loadTrustAnchorsIntoCertManager(pkixParams, cm);
|
||||
|
||||
/* Initialize all PKIXCertPathCheckers before calling check().
|
||||
* Store the returned list so we use the same checker instances
|
||||
* for both init() and check() calls. Pass certs so revocation
|
||||
* checker can find issuers for OCSP response verification. */
|
||||
* Store returned list to use same checker instances for init() and
|
||||
* check(). Pass certs so revocation checker can find issuers
|
||||
* for OCSP response verification. */
|
||||
pathCheckers = initCertPathCheckers(pkixParams, cm, certs);
|
||||
|
||||
/* Sanity checks on certs from PKIXParameters constraints */
|
||||
|
|
@ -1082,12 +1167,12 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
|
|||
callCertPathCheckers(certs.get(i), pathCheckers);
|
||||
}
|
||||
|
||||
/* Enable CRL if PKIXParameters.isRevocationEnabled(), load
|
||||
* any CRLs found in PKIXParameters.getCertStores(). Needs to
|
||||
* happen after trust anchors are loaded, since native wolfSSL
|
||||
* will try to find/verify CRL against trusted roots on load.
|
||||
* Pass pathCheckers so we can skip CRL setup when a
|
||||
* PKIXRevocationChecker is handling revocation via OCSP. */
|
||||
/* Enable CRL if PKIXParameters.isRevocationEnabled(), load any
|
||||
* CRLs found in PKIXParameters.getCertStores(). Done after trust
|
||||
* anchors loaded, since native wolfSSL will try to find/verify CRL
|
||||
* against trusted roots on load. Pass pathCheckers so we can skip
|
||||
* CRL setup when a PKIXRevocationChecker is handling revocation
|
||||
* via OCSP. */
|
||||
checkRevocationEnabledAndLoadCRLs(pkixParams, cm, certPath, certs,
|
||||
pathCheckers);
|
||||
|
||||
|
|
@ -1096,16 +1181,20 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
|
|||
|
||||
/* Cert chain has been verified, find TrustAnchor to return
|
||||
* in PKIXCertPathValidatorResult */
|
||||
trustAnchor = findTrustAnchor(
|
||||
pkixParams, certs.get(certs.size() - 1));
|
||||
trustAnchor = findTrustAnchor(pkixParams,
|
||||
certs.get(certs.size() - 1));
|
||||
|
||||
/* Check trust anchor public key constraints */
|
||||
if (trustAnchor != null) {
|
||||
checkTrustAnchorConstraints(trustAnchor);
|
||||
}
|
||||
|
||||
} finally {
|
||||
/* Free native WolfSSLCertManager resources */
|
||||
cm.free();
|
||||
}
|
||||
|
||||
/* PolicyNode not returned, since certificate policies are not
|
||||
* yet supported */
|
||||
/* PolicyNode not returned, since certificate policies not supported */
|
||||
return new PKIXCertPathValidatorResult(trustAnchor, null,
|
||||
certs.get(0).getPublicKey());
|
||||
}
|
||||
|
|
@ -1120,9 +1209,9 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
|
|||
* various checking options before being passed to the validate() method
|
||||
* via PKIXParameters.addCertPathChecker().
|
||||
*
|
||||
* Note: The CertManager will be provided to the checker during
|
||||
* certificate path validation when the checker's init() method is
|
||||
* called with the appropriate parameters.
|
||||
* Note: The CertManager will be provided to the checker during certificate
|
||||
* path validation when the checker's init() method is called with the
|
||||
* appropriate parameters.
|
||||
*
|
||||
* @return a CertPathChecker object that this implementation uses to
|
||||
* check the revocation status of certificates.
|
||||
|
|
|
|||
|
|
@ -406,6 +406,36 @@ public class WolfCryptPKIXRevocationChecker extends PKIXRevocationChecker {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get human readable name for OCSPResponseStatus value.
|
||||
*
|
||||
* Per RFC 6960, OCSPResponseStatus ::= ENUMERATED {
|
||||
* successful (0), malformedRequest (1), internalError (2),
|
||||
* tryLater (3), sigRequired (5), unauthorized (6) }
|
||||
*
|
||||
* @param status OCSPResponseStatus value
|
||||
* @return status name string
|
||||
*/
|
||||
private String getOcspResponseStatusName(int status) {
|
||||
|
||||
switch (status) {
|
||||
case 0:
|
||||
return "SUCCESSFUL";
|
||||
case 1:
|
||||
return "MALFORMED_REQUEST";
|
||||
case 2:
|
||||
return "INTERNAL_ERROR";
|
||||
case 3:
|
||||
return "TRY_LATER";
|
||||
case 5:
|
||||
return "SIG_REQUIRED";
|
||||
case 6:
|
||||
return "UNAUTHORIZED";
|
||||
default:
|
||||
return "UNKNOWN(" + status + ")";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check pre-loaded OCSP response.
|
||||
*
|
||||
|
|
@ -420,6 +450,7 @@ public class WolfCryptPKIXRevocationChecker extends PKIXRevocationChecker {
|
|||
private void checkPreloadedOcspResponse(X509Certificate cert)
|
||||
throws CertPathValidatorException {
|
||||
|
||||
int ocspStatus;
|
||||
byte[] response;
|
||||
byte[] certDer;
|
||||
|
||||
|
|
@ -435,14 +466,29 @@ public class WolfCryptPKIXRevocationChecker extends PKIXRevocationChecker {
|
|||
"CertManager not available for OCSP response checking");
|
||||
}
|
||||
|
||||
/* Check OCSP response status before native verification.
|
||||
* Non-successful OCSP responses (e.g. UNAUTHORIZED, TRY_LATER) should
|
||||
* be reported as errors per RFC 6960. Use native wolfSSL to parse the
|
||||
* response status from raw DER bytes. */
|
||||
ocspStatus = WolfSSLCertManager.getOcspResponseStatus(response,
|
||||
response.length);
|
||||
if (ocspStatus > 0) {
|
||||
throw new CertPathValidatorException("OCSP response error: " +
|
||||
getOcspResponseStatusName(ocspStatus));
|
||||
}
|
||||
else if (ocspStatus < 0) {
|
||||
throw new CertPathValidatorException(
|
||||
"Failed to parse OCSP response status: " + ocspStatus);
|
||||
}
|
||||
|
||||
/* Load issuer cert so OCSP response signature can be verified */
|
||||
loadIssuerForOcspVerification(cert);
|
||||
|
||||
try {
|
||||
certDer = cert.getEncoded();
|
||||
|
||||
certManager.CertManagerCheckOCSPResponse(
|
||||
response, response.length, certDer, certDer.length);
|
||||
certManager.CertManagerCheckOCSPResponse(response, response.length,
|
||||
certDer, certDer.length);
|
||||
|
||||
} catch (CertificateEncodingException e) {
|
||||
throw new CertPathValidatorException(
|
||||
|
|
|
|||
|
|
@ -55,14 +55,22 @@ public class WolfSSLCertManager {
|
|||
/* lock around native WOLFSSL_CERT_MANAGER pointer use */
|
||||
private final Object cmLock = new Object();
|
||||
|
||||
/** Flag to allow loading certs with date errors */
|
||||
public static final int WOLFSSL_LOAD_FLAG_DATE_ERR_OKAY =
|
||||
getWOLFSSL_LOAD_FLAG_DATE_ERR_OKAY();
|
||||
|
||||
/* Verification callback, null if not set */
|
||||
private WolfSSLCertManagerVerifyCallback verifyCallback = null;
|
||||
|
||||
private static native int getWOLFSSL_LOAD_FLAG_DATE_ERR_OKAY();
|
||||
|
||||
static native long CertManagerNew();
|
||||
static native void CertManagerFree(long cm);
|
||||
static native int CertManagerLoadCA(long cm, String f, String d);
|
||||
static native int CertManagerLoadCABuffer(
|
||||
long cm, byte[] in, long sz, int format);
|
||||
static native int CertManagerLoadCABufferEx(
|
||||
long cm, byte[] in, long sz, int format, int flags);
|
||||
static native int CertManagerUnloadCAs(long cm);
|
||||
static native int CertManagerVerifyBuffer(
|
||||
long cm, byte[] in, long sz, int format);
|
||||
|
|
@ -76,6 +84,7 @@ public class WolfSSLCertManager {
|
|||
static native int CertManagerCheckOCSP(long cm, byte[] cert, int sz);
|
||||
static native int CertManagerCheckOCSPResponse(
|
||||
long cm, byte[] response, int responseSz, byte[] cert, int certSz);
|
||||
static native int OcspResponseStatus(byte[] response, int responseSz);
|
||||
static native int CertManagerSetVerify(long cm, Object callback);
|
||||
static native int CertManagerClearVerify(long cm);
|
||||
|
||||
|
|
@ -180,8 +189,69 @@ public class WolfSSLCertManager {
|
|||
synchronized (cmLock) {
|
||||
try {
|
||||
/* Throws WolfCryptException on native error */
|
||||
CertManagerLoadCABuffer(cert.getEncoded(),
|
||||
cert.getEncoded().length, WolfCrypt.SSL_FILETYPE_ASN1);
|
||||
byte[] encoded = cert.getEncoded();
|
||||
CertManagerLoadCABuffer(encoded, encoded.length,
|
||||
WolfCrypt.SSL_FILETYPE_ASN1);
|
||||
} catch (CertificateEncodingException e) {
|
||||
throw new WolfCryptException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load CA into CertManager from byte array with extended flags.
|
||||
*
|
||||
* @param in byte array holding X.509 certificate to load
|
||||
* @param sz size of input byte array, bytes
|
||||
* @param format format of input certificate, either
|
||||
* WolfCrypt.SSL_FILETYPE_PEM (PEM formatted) or
|
||||
* WolfCrypt.SSL_FILETYPE_ASN1 (ASN.1/DER).
|
||||
* @param flags load flags, e.g.
|
||||
* WOLFSSL_LOAD_FLAG_DATE_ERR_OKAY
|
||||
*
|
||||
* @throws IllegalStateException WolfSSLCertManager has been freed
|
||||
* @throws WolfCryptException on native wolfSSL error
|
||||
*/
|
||||
public synchronized void CertManagerLoadCABufferEx(byte[] in, long sz,
|
||||
int format, int flags)
|
||||
throws IllegalStateException, WolfCryptException {
|
||||
|
||||
int ret = 0;
|
||||
|
||||
confirmObjectIsActive();
|
||||
|
||||
synchronized (cmLock) {
|
||||
ret = CertManagerLoadCABufferEx(this.cmPtr, in, sz, format, flags);
|
||||
if (ret != WolfCrypt.WOLFSSL_SUCCESS) {
|
||||
throw new WolfCryptException(ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load CA into CertManager from X509Certificate object with extended flags.
|
||||
*
|
||||
* @param cert X509Certificate containing CA cert
|
||||
* @param flags load flags, e.g.
|
||||
* WOLFSSL_LOAD_FLAG_DATE_ERR_OKAY
|
||||
*
|
||||
* @throws IllegalStateException WolfSSLCertManager has been freed
|
||||
* @throws WolfCryptException on native wolfSSL error
|
||||
*/
|
||||
public synchronized void CertManagerLoadCA(X509Certificate cert, int flags)
|
||||
throws IllegalStateException, WolfCryptException {
|
||||
|
||||
confirmObjectIsActive();
|
||||
|
||||
if (cert == null) {
|
||||
throw new WolfCryptException("Input X509Certificate is null");
|
||||
}
|
||||
|
||||
synchronized (cmLock) {
|
||||
try {
|
||||
byte[] encoded = cert.getEncoded();
|
||||
CertManagerLoadCABufferEx(encoded, encoded.length,
|
||||
WolfCrypt.SSL_FILETYPE_ASN1, flags);
|
||||
} catch (CertificateEncodingException e) {
|
||||
throw new WolfCryptException(e);
|
||||
}
|
||||
|
|
@ -224,8 +294,8 @@ public class WolfSSLCertManager {
|
|||
|
||||
if (cert != null && cert.getBasicConstraints() >= 0) {
|
||||
/* Will throw WolfCryptException on error */
|
||||
CertManagerLoadCABuffer(cert.getEncoded(),
|
||||
cert.getEncoded().length,
|
||||
byte[] encoded = cert.getEncoded();
|
||||
CertManagerLoadCABuffer(encoded, encoded.length,
|
||||
WolfCrypt.SSL_FILETYPE_ASN1);
|
||||
loadedCerts++;
|
||||
}
|
||||
|
|
@ -315,8 +385,9 @@ public class WolfSSLCertManager {
|
|||
synchronized (cmLock) {
|
||||
try {
|
||||
/* Throws WolfCryptException on native error */
|
||||
CertManagerVerifyBuffer(cert.getEncoded(),
|
||||
cert.getEncoded().length, WolfCrypt.SSL_FILETYPE_ASN1);
|
||||
byte[] encoded = cert.getEncoded();
|
||||
CertManagerVerifyBuffer(encoded, encoded.length,
|
||||
WolfCrypt.SSL_FILETYPE_ASN1);
|
||||
} catch (CertificateEncodingException e) {
|
||||
throw new WolfCryptException(e);
|
||||
}
|
||||
|
|
@ -623,6 +694,38 @@ public class WolfSSLCertManager {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OCSPResponseStatus from raw OCSP response bytes.
|
||||
*
|
||||
* Uses native wolfSSL_d2i_OCSP_RESPONSE() to parse the response and
|
||||
* wolfSSL_OCSP_response_status() to extract the status value per RFC 6960.
|
||||
*
|
||||
* This is a static method that does not require a WolfSSLCertManager
|
||||
* instance.
|
||||
*
|
||||
* @param response raw OCSP response bytes (DER encoded)
|
||||
* @param responseSz size of OCSP response
|
||||
*
|
||||
* @return OCSPResponseStatus value:
|
||||
* 0 = successful,
|
||||
* 1 = malformedRequest,
|
||||
* 2 = internalError,
|
||||
* 3 = tryLater,
|
||||
* 5 = sigRequired,
|
||||
* 6 = unauthorized,
|
||||
* negative on error (NOT_COMPILED_IN, BAD_FUNC_ARG,
|
||||
* MEMORY_E, or -1 if response parsing failed)
|
||||
*/
|
||||
public static int getOcspResponseStatus(byte[] response, int responseSz) {
|
||||
|
||||
if (response == null || responseSz < 0 ||
|
||||
responseSz > response.length) {
|
||||
return WolfCryptError.BAD_FUNC_ARG.getCode();
|
||||
}
|
||||
|
||||
return OcspResponseStatus(response, responseSz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set verification callback for this CertManager.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -59,5 +59,29 @@ public interface WolfSSLCertManagerVerifyCallback {
|
|||
* @return 1 to accept the certificate, 0 to reject
|
||||
*/
|
||||
int verify(int preverify, int error, int errorDepth);
|
||||
|
||||
/**
|
||||
* Verify callback with certificate DER bytes from store context.
|
||||
*
|
||||
* This overload passes the DER-encoded certificate being verified,
|
||||
* extracted from the native WOLFSSL_X509_STORE_CTX. Implementations
|
||||
* can override this method to inspect the actual certificate when it
|
||||
* is not available through other means (e.g. trust anchors not in a
|
||||
* pre-built map).
|
||||
*
|
||||
* The default implementation delegates to the 3-arg verify() method,
|
||||
* so existing implementations are not broken.
|
||||
*
|
||||
* @param preverify 1 if pre-verification passed, 0 if failed
|
||||
* @param error Error code from verification (0 indicates no error)
|
||||
* @param errorDepth Certificate depth in chain (0 = end entity cert)
|
||||
* @param certDer DER-encoded certificate at errorDepth, or null
|
||||
* if not available from the store context
|
||||
* @return 1 to accept the certificate, 0 to reject
|
||||
*/
|
||||
default int verify(int preverify, int error, int errorDepth,
|
||||
byte[] certDer) {
|
||||
return verify(preverify, error, errorDepth);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1928,5 +1928,179 @@ public class WolfCryptPKIXCertPathValidatorTest {
|
|||
e.getMessage().contains("no certificate"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that trust anchor public key constraints are enforced when
|
||||
* validating a normal (non-empty) cert path.
|
||||
*/
|
||||
@Test
|
||||
public void testTrustAnchorKeyConstraintsOnNormalPath()
|
||||
throws Exception {
|
||||
|
||||
String origProperty = null;
|
||||
CertificateFactory cf = null;
|
||||
X509Certificate caCert = null;
|
||||
X509Certificate clientCert = null;
|
||||
TrustAnchor anchor = null;
|
||||
Set<TrustAnchor> anchors = null;
|
||||
PKIXParameters params = null;
|
||||
CertPath path = null;
|
||||
CertPathValidator validator = null;
|
||||
FileInputStream fis = null;
|
||||
|
||||
/* Save original security property value */
|
||||
origProperty = Security.getProperty("jdk.certpath.disabledAlgorithms");
|
||||
|
||||
try {
|
||||
/* Load wolfSSL CA cert (2048-bit RSA key) */
|
||||
cf = CertificateFactory.getInstance("X.509");
|
||||
fis = new FileInputStream(caCertDer);
|
||||
try {
|
||||
caCert = (X509Certificate)cf.generateCertificate(fis);
|
||||
} finally {
|
||||
fis.close();
|
||||
}
|
||||
|
||||
/* Load wolfSSL client cert (2048-bit RSA key) */
|
||||
fis = new FileInputStream(clientCertDer);
|
||||
try {
|
||||
clientCert = (X509Certificate)cf.generateCertificate(fis);
|
||||
} finally {
|
||||
fis.close();
|
||||
}
|
||||
|
||||
/* Set minimum RSA key size to 4096 bits. The client
|
||||
* cert (in the path) will fail first since it is
|
||||
* checked before the trust anchor. Use a constraint
|
||||
* that only affects key size to verify trust anchor
|
||||
* checking also works in normal path flow. */
|
||||
Security.setProperty("jdk.certpath.disabledAlgorithms",
|
||||
"RSA keySize < 4096");
|
||||
|
||||
/* Setup trust anchor with CA cert */
|
||||
anchor = new TrustAnchor(caCert, null);
|
||||
anchors = new HashSet<TrustAnchor>();
|
||||
anchors.add(anchor);
|
||||
|
||||
params = new PKIXParameters(anchors);
|
||||
params.setRevocationEnabled(false);
|
||||
|
||||
/* Create CertPath with client cert */
|
||||
List<Certificate> certList = new ArrayList<Certificate>();
|
||||
certList.add(clientCert);
|
||||
path = cf.generateCertPath(certList);
|
||||
|
||||
/* Validate - should fail because key size is too
|
||||
* small (either path cert or trust anchor) */
|
||||
validator = CertPathValidator.getInstance("PKIX", "wolfJCE");
|
||||
|
||||
try {
|
||||
validator.validate(path, params);
|
||||
fail("Expected CertPathValidatorException " +
|
||||
"for RSA key size constraint");
|
||||
} catch (CertPathValidatorException cpve) {
|
||||
assertEquals(
|
||||
"Expected ALGORITHM_CONSTRAINED",
|
||||
BasicReason.ALGORITHM_CONSTRAINED, cpve.getReason());
|
||||
}
|
||||
|
||||
} finally {
|
||||
/* Close any open file streams */
|
||||
if (fis != null) {
|
||||
try {
|
||||
fis.close();
|
||||
} catch (IOException e) {
|
||||
/* Ignore close errors */
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore original security property */
|
||||
if (origProperty != null) {
|
||||
Security.setProperty("jdk.certpath.disabledAlgorithms",
|
||||
origProperty);
|
||||
}
|
||||
else {
|
||||
Security.setProperty("jdk.certpath.disabledAlgorithms", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that trust anchor public key constraints are enforced when
|
||||
* validating a zero-length cert path (trust anchor is the target
|
||||
* certificate).
|
||||
*/
|
||||
@Test
|
||||
public void testTrustAnchorKeyConstraintsOnZeroLengthPath()
|
||||
throws Exception {
|
||||
|
||||
String origProperty = null;
|
||||
CertificateFactory cf = null;
|
||||
X509Certificate caCert = null;
|
||||
TrustAnchor anchor = null;
|
||||
Set<TrustAnchor> anchors = null;
|
||||
PKIXParameters params = null;
|
||||
CertPath emptyPath = null;
|
||||
CertPathValidator validator = null;
|
||||
FileInputStream fis = null;
|
||||
|
||||
/* Save original security property value */
|
||||
origProperty = Security.getProperty("jdk.certpath.disabledAlgorithms");
|
||||
|
||||
try {
|
||||
/* Load CA cert (2048-bit RSA key) */
|
||||
cf = CertificateFactory.getInstance("X.509");
|
||||
fis = new FileInputStream(caCertDer);
|
||||
caCert = (X509Certificate)cf.generateCertificate(fis);
|
||||
fis.close();
|
||||
|
||||
/* Set minimum RSA key size to 4096 bits */
|
||||
Security.setProperty("jdk.certpath.disabledAlgorithms",
|
||||
"RSA keySize < 4096");
|
||||
|
||||
/* Setup trust anchor with CA cert */
|
||||
anchor = new TrustAnchor(caCert, null);
|
||||
anchors = new HashSet<TrustAnchor>();
|
||||
anchors.add(anchor);
|
||||
|
||||
params = new PKIXParameters(anchors);
|
||||
params.setRevocationEnabled(false);
|
||||
|
||||
/* Create zero-length (empty) CertPath */
|
||||
emptyPath = cf.generateCertPath(new ArrayList<Certificate>());
|
||||
|
||||
/* Validate - should fail because trust anchor
|
||||
* key size is too small */
|
||||
validator = CertPathValidator.getInstance("PKIX", "wolfJCE");
|
||||
|
||||
try {
|
||||
validator.validate(emptyPath, params);
|
||||
fail("Expected CertPathValidatorException for trust anchor " +
|
||||
"RSA key size constraint on zero-length path");
|
||||
} catch (CertPathValidatorException cpve) {
|
||||
assertEquals("Expected ALGORITHM_CONSTRAINED",
|
||||
BasicReason.ALGORITHM_CONSTRAINED, cpve.getReason());
|
||||
}
|
||||
|
||||
} finally {
|
||||
/* Close any open file streams */
|
||||
if (fis != null) {
|
||||
try {
|
||||
fis.close();
|
||||
} catch (IOException e) {
|
||||
/* Ignore close errors */
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore original security property */
|
||||
if (origProperty != null) {
|
||||
Security.setProperty("jdk.certpath.disabledAlgorithms",
|
||||
origProperty);
|
||||
}
|
||||
else {
|
||||
Security.setProperty("jdk.certpath.disabledAlgorithms", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue