private static void AddElementStatus( Interop.libcrypto.X509VerifyStatusCode errorCode, List <X509ChainStatus> elementStatus, List <X509ChainStatus> overallStatus) { X509ChainStatusFlags statusFlag = MapVerifyErrorToChainStatus(errorCode); Debug.Assert( (statusFlag & (statusFlag - 1)) == 0, "Status flag has more than one bit set", "More than one bit is set in status '{0}' for error code '{1}'", statusFlag, errorCode); foreach (X509ChainStatus currentStatus in elementStatus) { if ((currentStatus.Status & statusFlag) != 0) { return; } } X509ChainStatus chainStatus = new X509ChainStatus { Status = MapVerifyErrorToChainStatus(errorCode), StatusInformation = Interop.libcrypto.X509_verify_cert_error_string(errorCode), }; elementStatus.Add(chainStatus); AddUniqueStatus(overallStatus, ref chainStatus); }
public static IChainPal BuildChain( X509Certificate2 leaf, List <X509Certificate2> candidates, List <X509Certificate2> downloaded, OidCollection applicationPolicy, OidCollection certificatePolicy, DateTime verificationTime) { X509ChainElement[] elements; List <X509ChainStatus> overallStatus = new List <X509ChainStatus>(); // An X509_STORE is more comparable to Cryptography.X509Certificate2Collection than to // Cryptography.X509Store. So read this with OpenSSL eyes, not CAPI/CNG eyes. // // (If you need to think of it as an X509Store, it's a volatile memory store) using (SafeX509StoreHandle store = Interop.libcrypto.X509_STORE_new()) using (SafeX509StoreCtxHandle storeCtx = Interop.libcrypto.X509_STORE_CTX_new()) { Interop.libcrypto.CheckValidOpenSslHandle(store); Interop.libcrypto.CheckValidOpenSslHandle(storeCtx); foreach (X509Certificate2 cert in candidates) { OpenSslX509CertificateReader pal = (OpenSslX509CertificateReader)cert.Pal; if (!Interop.libcrypto.X509_STORE_add_cert(store, pal.SafeHandle)) { throw Interop.libcrypto.CreateOpenSslCryptographicException(); } } // When CRL checking support is added, it should be done before the call to // X509_STORE_CTX_init (aka here) by calling X509_STORE_set_flags(store, flags); SafeX509Handle leafHandle = ((OpenSslX509CertificateReader)leaf.Pal).SafeHandle; if (!Interop.libcrypto.X509_STORE_CTX_init(storeCtx, store, leafHandle, IntPtr.Zero)) { throw Interop.libcrypto.CreateOpenSslCryptographicException(); } Interop.Crypto.SetX509ChainVerifyTime(storeCtx, verificationTime); int verify = Interop.libcrypto.X509_verify_cert(storeCtx); if (verify < 0) { throw Interop.libcrypto.CreateOpenSslCryptographicException(); } using (SafeX509StackHandle chainStack = Interop.libcrypto.X509_STORE_CTX_get1_chain(storeCtx)) { int chainSize = Interop.Crypto.GetX509StackFieldCount(chainStack); int errorDepth = -1; Interop.libcrypto.X509VerifyStatusCode errorCode = 0; if (verify == 0) { errorCode = Interop.libcrypto.X509_STORE_CTX_get_error(storeCtx); errorDepth = Interop.libcrypto.X509_STORE_CTX_get_error_depth(storeCtx); } elements = new X509ChainElement[chainSize]; int maybeRootDepth = chainSize - 1; // The leaf cert is 0, up to (maybe) the root at chainSize - 1 for (int i = 0; i < chainSize; i++) { List <X509ChainStatus> status = new List <X509ChainStatus>(); if (i == errorDepth) { AddElementStatus(errorCode, status, overallStatus); } IntPtr elementCertPtr = Interop.Crypto.GetX509StackField(chainStack, i); if (elementCertPtr == IntPtr.Zero) { throw Interop.libcrypto.CreateOpenSslCryptographicException(); } // Duplicate the certificate handle X509Certificate2 elementCert = new X509Certificate2(elementCertPtr); // If the last cert is self signed then it's the root cert, do any extra checks. if (i == maybeRootDepth && IsSelfSigned(elementCert)) { // If the root certificate was downloaded, it's untrusted. if (downloaded.Contains(elementCert)) { AddElementStatus( Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_CERT_UNTRUSTED, status, overallStatus); } } elements[i] = new X509ChainElement(elementCert, status.ToArray(), ""); } } } if ((certificatePolicy != null && certificatePolicy.Count > 0) || (applicationPolicy != null && applicationPolicy.Count > 0)) { List <X509Certificate2> certsToRead = new List <X509Certificate2>(); foreach (X509ChainElement element in elements) { certsToRead.Add(element.Certificate); } CertificatePolicyChain policyChain = new CertificatePolicyChain(certsToRead); bool failsPolicyChecks = false; if (certificatePolicy != null) { if (!policyChain.MatchesCertificatePolicies(certificatePolicy)) { failsPolicyChecks = true; } } if (applicationPolicy != null) { if (!policyChain.MatchesApplicationPolicies(applicationPolicy)) { failsPolicyChecks = true; } } if (failsPolicyChecks) { X509ChainElement leafElement = elements[0]; X509ChainStatus chainStatus = new X509ChainStatus { Status = X509ChainStatusFlags.InvalidPolicyConstraints, StatusInformation = SR.Chain_NoPolicyMatch, }; var elementStatus = new List <X509ChainStatus>(leafElement.ChainElementStatus.Length + 1); elementStatus.AddRange(leafElement.ChainElementStatus); AddUniqueStatus(elementStatus, ref chainStatus); AddUniqueStatus(overallStatus, ref chainStatus); elements[0] = new X509ChainElement( leafElement.Certificate, elementStatus.ToArray(), leafElement.Information); } } return(new OpenSslX509ChainProcessor { ChainStatus = overallStatus.ToArray(), ChainElements = elements, }); }
private static X509ChainStatusFlags MapVerifyErrorToChainStatus(Interop.libcrypto.X509VerifyStatusCode code) { switch (code) { case Interop.libcrypto.X509VerifyStatusCode.X509_V_OK: return(X509ChainStatusFlags.NoError); case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_CERT_NOT_YET_VALID: case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_CERT_HAS_EXPIRED: case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: return(X509ChainStatusFlags.NotTimeValid); case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_CERT_REVOKED: return(X509ChainStatusFlags.Revoked); case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_CERT_SIGNATURE_FAILURE: return(X509ChainStatusFlags.NotSignatureValid); case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_CERT_UNTRUSTED: case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: return(X509ChainStatusFlags.UntrustedRoot); case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_CRL_HAS_EXPIRED: return(X509ChainStatusFlags.OfflineRevocation); case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_CRL_NOT_YET_VALID: case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_CRL_SIGNATURE_FAILURE: case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_KEYUSAGE_NO_CRL_SIGN: case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_CRL: case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER: case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION: return(X509ChainStatusFlags.RevocationStatusUnknown); case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_INVALID_EXTENSION: return(X509ChainStatusFlags.InvalidExtension); case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: return(X509ChainStatusFlags.PartialChain); case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_INVALID_PURPOSE: return(X509ChainStatusFlags.NotValidForUsage); case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_INVALID_CA: case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_INVALID_NON_CA: case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_PATH_LENGTH_EXCEEDED: case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_KEYUSAGE_NO_CERTSIGN: case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE: return(X509ChainStatusFlags.InvalidBasicConstraints); case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_INVALID_POLICY_EXTENSION: case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_NO_EXPLICIT_POLICY: return(X509ChainStatusFlags.InvalidPolicyConstraints); case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_CERT_REJECTED: return(X509ChainStatusFlags.ExplicitDistrust); case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION: return(X509ChainStatusFlags.HasNotSupportedCriticalExtension); case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_CERT_CHAIN_TOO_LONG: throw new CryptographicException(); case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_OUT_OF_MEM: throw new OutOfMemoryException(); default: Debug.Fail("Unrecognized X509VerifyStatusCode:" + code); throw new CryptographicException(); } }