/// <summary> /// Returns the CRLs for the issuer. /// </summary> public List <X509CRL> EnumerateCRLs(X509Certificate2 issuer, bool validateUpdateTime = true) { if (issuer == null) { throw new ArgumentNullException(nameof(issuer)); } List <X509CRL> crls = new List <X509CRL>(); foreach (X509CRL crl in EnumerateCRLs()) { if (!Utils.CompareDistinguishedName(crl.Issuer, issuer.Subject)) { continue; } if (!crl.VerifySignature(issuer, false)) { continue; } if (!validateUpdateTime || crl.UpdateTime <= DateTime.UtcNow && (crl.NextUpdateTime == DateTime.MinValue || crl.NextUpdateTime >= DateTime.UtcNow)) { crls.Add(crl); } } return(crls); }
/// <summary> /// Finds a certificate in a collection. /// </summary> /// <param name="collection">The collection of the <see cref="X509Certificate2"/> certificates objects.</param> /// <param name="needPrivateKey">If set to <c>true</c> the certificate must have private key.</param> /// <returns>An <see cref="X509Certificate2"/> certificate with matching subject name.</returns> public static X509Certificate2 Find(X509Certificate2Collection collection, string thumbprint, string subjectName, bool needPrivateKey) { #if !SILVERLIGHT // find by thumbprint. if (!String.IsNullOrEmpty(thumbprint)) { collection = collection.Find(X509FindType.FindByThumbprint, thumbprint, false); foreach (X509Certificate2 certificate in collection) { if (!needPrivateKey || certificate.HasPrivateKey) { if (String.IsNullOrEmpty(subjectName)) { return(certificate); } List <string> subjectName2 = Utils.ParseDistinguishedName(subjectName); if (Utils.CompareDistinguishedName(certificate, subjectName2)) { return(certificate); } } } return(null); } // find by subject name. if (!String.IsNullOrEmpty(subjectName)) { List <string> subjectName2 = Utils.ParseDistinguishedName(subjectName); foreach (X509Certificate2 certificate in collection) { if (Utils.CompareDistinguishedName(certificate, subjectName2)) { if (!needPrivateKey || certificate.HasPrivateKey) { return(certificate); } } } collection = collection.Find(X509FindType.FindBySubjectName, subjectName, false); foreach (X509Certificate2 certificate in collection) { if (!needPrivateKey || certificate.HasPrivateKey) { return(certificate); } } } #endif // certificate not found. return(null); }
/// <summary> /// Returns the issuers for the certificate. /// </summary> /// <param name="certificate">The certificate.</param> /// <param name="issuers">The issuers.</param> /// <returns></returns> public async Task <bool> GetIssuers(X509Certificate2 certificate, List <CertificateIdentifier> issuers) { bool isTrusted = false; CertificateIdentifier issuer = null; do { issuer = await GetIssuer(certificate, m_trustedCertificateList, m_trustedCertificateStore, true); if (issuer == null) { issuer = await GetIssuer(certificate, m_issuerCertificateList, m_issuerCertificateStore, true); } else { isTrusted = true; } if (issuer != null) { issuers.Add(issuer); certificate = await issuer.Find(false); // check for root. if (Utils.CompareDistinguishedName(certificate.Subject, certificate.Issuer)) { break; } } }while (issuer != null); return(isTrusted); }
/// <summary> /// Gets the CRL file paths. /// </summary> /// <param name="thumbprint">The certificate thumbprint.</param> /// <returns></returns> public string[] GetCrlFilePaths(string thumbprint) { List <string> filePaths = new List <string>(); Entry entry = Find(thumbprint); DirectoryInfo info = new DirectoryInfo(this.Directory.FullName + "\\crl"); foreach (FileInfo file in info.GetFiles("*.crl")) { X509CRL crl = null; try { crl = new X509CRL(file.FullName); } catch (Exception e) { Utils.Trace(e, "Could not parse CRL file."); continue; } if (!Utils.CompareDistinguishedName(crl.Issuer, entry.Certificate.Subject)) { continue; } filePaths.Add(file.FullName); } return(filePaths.ToArray()); }
/// <summary> /// Returns true the certificate is in the CRL. /// </summary> public bool IsRevoked(X509Certificate2 certificate) { // check that the issuer matches. if (m_issuer == null || !Utils.CompareDistinguishedName(certificate.Issuer, m_issuer.Subject)) { throw new ServiceResultException(StatusCodes.BadCertificateInvalid, "Certificate was not created by the CRL issuer."); } Org.BouncyCastle.X509.X509Certificate bccert = new X509CertificateParser().ReadCertificate(certificate.RawData); return(m_crl.IsRevoked(bccert)); }
/// <summary> /// Returns true the certificate is in the CRL. /// </summary> public bool IsRevoked(X509Certificate2 certificate) { // check that the issuer matches. if (m_issuer == null || !Utils.CompareDistinguishedName(certificate.Issuer, m_issuer.Subject)) { throw new ServiceResultException(StatusCodes.BadCertificateInvalid, "Certificate was not created by the CRL issuer."); } // TODO: get the cert info for the target certificate and check revocation. // not revoked. return(false); }
/// <summary> /// Returns the issuers for the certificates. /// </summary> public async Task <bool> GetIssuers(X509Certificate2Collection certificates, List <CertificateIdentifier> issuers) { bool isTrusted = false; CertificateIdentifier issuer = null; X509Certificate2 certificate = certificates[0]; CertificateIdentifierCollection collection = new CertificateIdentifierCollection(); for (int ii = 1; ii < certificates.Count; ii++) { collection.Add(new CertificateIdentifier(certificates[ii])); } do { issuer = await GetIssuer(certificate, m_trustedCertificateList, m_trustedCertificateStore, true); if (issuer == null) { issuer = await GetIssuer(certificate, m_issuerCertificateList, m_issuerCertificateStore, true); if (issuer == null) { issuer = await GetIssuer(certificate, collection, null, true); } } if (issuer != null) { isTrusted = true; issuers.Add(issuer); certificate = await issuer.Find(false); // check for root. if (Utils.CompareDistinguishedName(certificate.Subject, certificate.Issuer)) { break; } } else { isTrusted = false; } }while (issuer != null); return(isTrusted); }
/// <summary> /// Adds a CRL to the store. /// </summary> public void AddCRL(X509CRL crl) { if (crl == null) { throw new ArgumentNullException("crl"); } X509Certificate2 issuer = null; X509Certificate2Collection certificates = null; certificates = Enumerate().Result; foreach (X509Certificate2 certificate in certificates) { if (Utils.CompareDistinguishedName(certificate.Subject, crl.Issuer)) { if (crl.VerifySignature(certificate, false)) { issuer = certificate; break; } } } if (issuer == null) { throw new ServiceResultException(StatusCodes.BadCertificateInvalid, "Could not find issuer of the CRL."); } StringBuilder builder = new StringBuilder(); builder.Append(m_directory.FullName); builder.Append(Path.DirectorySeparatorChar + "crl" + Path.DirectorySeparatorChar); builder.Append(GetFileName(issuer)); builder.Append(".crl"); FileInfo fileInfo = new FileInfo(builder.ToString()); if (!fileInfo.Directory.Exists) { fileInfo.Directory.Create(); } File.WriteAllBytes(fileInfo.FullName, crl.RawData); }
/// <summary> /// Returns true if the certificate matches the criteria. /// </summary> private bool Match( X509Certificate2 certificate, string subjectName, string serialNumber, string authorityKeyId) { // check for null. if (certificate == null) { return(false); } // check for subject name match. if (!Utils.CompareDistinguishedName(certificate.SubjectName.Name, subjectName)) { return(false); } // check for serial number match. if (!String.IsNullOrEmpty(serialNumber)) { if (certificate.SerialNumber != serialNumber) { return(false); } } // check for authority key id match. if (!String.IsNullOrEmpty(authorityKeyId)) { X509SubjectKeyIdentifierExtension subjectKeyId = FindSubjectKeyIdentifierExtension(certificate); if (subjectKeyId != null) { if (subjectKeyId.SubjectKeyIdentifier != authorityKeyId) { return(false); } } } // found match. return(true); }
/// <summary> /// Returns the CRLs for the issuer. /// </summary> public List <X509CRL> EnumerateCRLs(X509Certificate2 issuer) { if (issuer == null) { throw new ArgumentNullException("issuer"); } List <X509CRL> crls = new List <X509CRL>(); // check for CRL. DirectoryInfo info = new DirectoryInfo(this.Directory.FullName + "\\crl"); if (info.Exists) { foreach (FileInfo file in info.GetFiles("*.crl")) { X509CRL crl = new X509CRL(file.FullName); if (!Utils.CompareDistinguishedName(crl.Issuer, issuer.Subject)) { continue; } if (!crl.VerifySignature(issuer, false)) { continue; } if (crl.UpdateTime <= DateTime.UtcNow && (crl.NextUpdateTime == DateTime.MinValue || crl.NextUpdateTime >= DateTime.UtcNow)) { crls.Add(crl); } } } return(crls); }
/// <summary> /// Checks if issuer has revoked the certificate. /// </summary> public virtual StatusCode IsRevoked(X509Certificate2 issuer, X509Certificate2 certificate) { if (issuer == null) { throw new ArgumentNullException("issuer"); } if (certificate == null) { throw new ArgumentNullException("certificate"); } // check for CRL. DirectoryInfo info = new DirectoryInfo(this.Directory.FullName + Path.DirectorySeparatorChar + "crl"); if (info.Exists) { bool crlExpired = true; foreach (FileInfo file in info.GetFiles("*.crl")) { X509CRL crl = null; try { crl = new X509CRL(file.FullName); } catch (Exception e) { Utils.Trace(e, "Could not parse CRL file."); continue; } if (!Utils.CompareDistinguishedName(crl.Issuer, issuer.Subject)) { continue; } if (!crl.VerifySignature(issuer, false)) { continue; } if (crl.IsRevoked(certificate)) { return(StatusCodes.BadCertificateRevoked); } if (crl.UpdateTime <= DateTime.UtcNow && (crl.NextUpdateTime == DateTime.MinValue || crl.NextUpdateTime >= DateTime.UtcNow)) { crlExpired = false; } } // certificate is fine. if (!crlExpired) { return(StatusCodes.Good); } } // can't find a valid CRL. return(StatusCodes.BadCertificateRevocationUnknown); }
/// <summary> /// Loads the private key from a PFX file in the certificate store. /// </summary> public virtual X509Certificate2 LoadApplicationCertificate(string thumbprint, string subjectName, string applicationURI, string password) { if (m_certificateSubdir == null || !m_certificateSubdir.Exists) { return(null); } if (string.IsNullOrEmpty(thumbprint) && string.IsNullOrEmpty(subjectName)) { return(null); } foreach (FileInfo file in m_certificateSubdir.GetFiles("*.der")) { try { X509Certificate2 certificate = new X509Certificate2(file.FullName); if (!String.IsNullOrEmpty(thumbprint)) { if (!string.Equals(certificate.Thumbprint, thumbprint, StringComparison.CurrentCultureIgnoreCase)) { continue; } } if (!String.IsNullOrEmpty(subjectName)) { if (!Utils.CompareDistinguishedName(subjectName, certificate.Subject)) { if (subjectName.Contains("=") || !certificate.Subject.Contains("CN=" + subjectName)) { continue; } } } string fileRoot = file.Name.Substring(0, file.Name.Length - file.Extension.Length); StringBuilder filePath = new StringBuilder(); filePath.Append(m_privateKeySubdir.FullName); filePath.Append(Path.DirectorySeparatorChar); filePath.Append(fileRoot); FileInfo privateKeyFile = new FileInfo(filePath.ToString() + ".pfx"); RSA rsa = null; try { certificate = new X509Certificate2( privateKeyFile.FullName, (password == null) ? String.Empty : password, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet); rsa = certificate.GetRSAPrivateKey(); } catch (Exception) { certificate = new X509Certificate2( privateKeyFile.FullName, (password == null) ? String.Empty : password, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.DefaultKeySet); rsa = certificate.GetRSAPrivateKey(); } if (rsa != null) { int inputBlockSize = rsa.KeySize / 8 - 42; byte[] bytes1 = rsa.Encrypt(new byte[inputBlockSize], RSAEncryptionPadding.OaepSHA1); byte[] bytes2 = rsa.Decrypt(bytes1, RSAEncryptionPadding.OaepSHA1); if (bytes2 != null) { // Utils.Trace(1, "RSA: {0}", certificate.Thumbprint); return(certificate); } } } catch (Exception e) { Utils.Trace(e, "Could not load private key for certificate " + subjectName); } } return(null); }
private static ServiceResult CheckChainStatus(X509ChainStatus status, CertificateIdentifier id, CertificateIdentifier issuer, bool isIssuer) { switch (status.Status) { case X509ChainStatusFlags.NotValidForUsage: { return(ServiceResult.Create( (isIssuer) ? StatusCodes.BadCertificateUseNotAllowed : StatusCodes.BadCertificateIssuerUseNotAllowed, "Certificate may not be used as an application instance certificate. {0}: {1}", status.Status, status.StatusInformation)); } case X509ChainStatusFlags.NoError: case X509ChainStatusFlags.OfflineRevocation: case X509ChainStatusFlags.InvalidBasicConstraints: case X509ChainStatusFlags.PartialChain: { break; } case X509ChainStatusFlags.UntrustedRoot: { // ignore this error because the root check is done // by looking the certificate up in the trusted issuer stores passed to the validator. // the ChainStatus uses the Windows trusted issuer stores. break; } case X509ChainStatusFlags.RevocationStatusUnknown: { if (issuer != null) { if ((issuer.ValidationOptions & CertificateValidationOptions.SuppressRevocationStatusUnknown) != 0) { break; } } // check for meaning less errors for self-signed certificates. if (id.Certificate != null && Utils.CompareDistinguishedName(id.Certificate.Subject, id.Certificate.Subject)) { break; } return(ServiceResult.Create( (isIssuer) ? StatusCodes.BadCertificateIssuerRevocationUnknown : StatusCodes.BadCertificateRevocationUnknown, "Certificate revocation status cannot be verified. {0}: {1}", status.Status, status.StatusInformation)); } case X509ChainStatusFlags.Revoked: { return(ServiceResult.Create( (isIssuer) ? StatusCodes.BadCertificateIssuerRevoked : StatusCodes.BadCertificateRevoked, "Certificate has been revoked. {0}: {1}", status.Status, status.StatusInformation)); } case X509ChainStatusFlags.NotTimeNested: { if (id != null && ((id.ValidationOptions & CertificateValidationOptions.SuppressCertificateExpired) != 0)) { break; } return(ServiceResult.Create( StatusCodes.BadCertificateIssuerTimeInvalid, "Certificate issuer validatity time does not overhas is expired or not yet valid. {0}: {1}", status.Status, status.StatusInformation)); } case X509ChainStatusFlags.NotTimeValid: { if (id != null && ((id.ValidationOptions & CertificateValidationOptions.SuppressCertificateExpired) != 0)) { break; } return(ServiceResult.Create( (isIssuer) ? StatusCodes.BadCertificateIssuerTimeInvalid : StatusCodes.BadCertificateTimeInvalid, "Certificate has is expired or not yet valid. {0}: {1}", status.Status, status.StatusInformation)); } default: { return(ServiceResult.Create( StatusCodes.BadCertificateInvalid, "Certificate validation failed. {0}: {1}", status.Status, status.StatusInformation)); } } return(null); }
/// <summary> /// Throws an exception if validation fails. /// </summary> /// <param name="certificates">The certificates to be checked.</param> /// <exception cref="ServiceResultException">If certificate[0] cannot be accepted</exception> protected virtual async Task InternalValidate(X509Certificate2Collection certificates) { X509Certificate2 certificate = certificates[0]; // check for previously validated certificate. X509Certificate2 certificate2 = null; if (m_validatedCertificates.TryGetValue(certificate.Thumbprint, out certificate2)) { if (Utils.IsEqual(certificate2.RawData, certificate.RawData)) { return; } } CertificateIdentifier trustedCertificate = await GetTrustedCertificate(certificate); // get the issuers (checks the revocation lists if using directory stores). List <CertificateIdentifier> issuers = new List <CertificateIdentifier>(); bool isIssuerTrusted = await GetIssuers(certificates, issuers); // setup policy chain X509ChainPolicy policy = new X509ChainPolicy(); policy.RevocationFlag = X509RevocationFlag.EntireChain; policy.RevocationMode = X509RevocationMode.NoCheck; policy.VerificationFlags = X509VerificationFlags.NoFlag; foreach (CertificateIdentifier issuer in issuers) { if ((issuer.ValidationOptions & CertificateValidationOptions.SuppressRevocationStatusUnknown) != 0) { policy.VerificationFlags |= X509VerificationFlags.IgnoreCertificateAuthorityRevocationUnknown; policy.VerificationFlags |= X509VerificationFlags.IgnoreCtlSignerRevocationUnknown; policy.VerificationFlags |= X509VerificationFlags.IgnoreEndRevocationUnknown; policy.VerificationFlags |= X509VerificationFlags.IgnoreRootRevocationUnknown; } // we did the revocation check in the GetIssuers call. No need here. policy.RevocationMode = X509RevocationMode.NoCheck; policy.ExtraStore.Add(issuer.Certificate); } // build chain. X509Chain chain = new X509Chain(); chain.ChainPolicy = policy; chain.Build(certificate); // check the chain results. CertificateIdentifier target = trustedCertificate; if (target == null) { target = new CertificateIdentifier(certificate); } for (int ii = 0; ii < chain.ChainElements.Count; ii++) { X509ChainElement element = chain.ChainElements[ii]; CertificateIdentifier issuer = null; if (ii < issuers.Count) { issuer = issuers[ii]; } // check for chain status errors. foreach (X509ChainStatus status in element.ChainElementStatus) { ServiceResult result = CheckChainStatus(status, target, issuer, (ii != 0)); if (ServiceResult.IsBad(result)) { // check untrusted certificates. if (trustedCertificate == null) { throw new ServiceResultException(StatusCodes.BadSecurityChecksFailed); } throw new ServiceResultException(result); } } if (issuer != null) { target = issuer; } } bool issuedByCA = !Utils.CompareDistinguishedName(certificate.Subject, certificate.Issuer); // check if certificate issuer is trusted. if (issuedByCA && !isIssuerTrusted) { if (m_applicationCertificate == null || !Utils.IsEqual(m_applicationCertificate.RawData, certificate.RawData)) { throw ServiceResultException.Create( StatusCodes.BadCertificateUntrusted, "Certificate issuer is not trusted.\r\nSubjectName: {0}\r\nIssuerName: {1}", certificate.SubjectName.Name, certificate.IssuerName.Name); } } // check if certificate is trusted. if (trustedCertificate == null && !isIssuerTrusted) { if (m_applicationCertificate == null || !Utils.IsEqual(m_applicationCertificate.RawData, certificate.RawData)) { throw ServiceResultException.Create( StatusCodes.BadCertificateUntrusted, "Certificate is not trusted.\r\nSubjectName: {0}\r\nIssuerName: {1}", certificate.SubjectName.Name, certificate.IssuerName.Name); } } }
/// <summary> /// Returns the issuers for the certificates. /// </summary> public bool GetIssuersWithChainSupportEnabled(X509Certificate2Collection certificates, List <CertificateIdentifier> issuers) { bool isTrusted = false; bool isChainComplete = false; CertificateIdentifier issuer = null; X509Certificate2 certificate = certificates[0]; // application certificate is trusted CertificateIdentifier trustedCertificate = GetTrustedCertificate(certificate); if (trustedCertificate != null) { isTrusted = true; } if (Utils.CompareDistinguishedName(certificate.Subject, certificate.Issuer)) { if (!isTrusted) { throw ServiceResultException.Create( StatusCodes.BadCertificateUntrusted, "Self Signed Certificate is not trusted.\r\nIssuerName: {0}", certificate.IssuerName.Name); } return(isTrusted); } CertificateIdentifierCollection collection = new CertificateIdentifierCollection(); for (int ii = 1; ii < certificates.Count; ii++) { collection.Add(new CertificateIdentifier(certificates[ii])); } do { issuer = GetIssuer(certificate, m_trustedCertificateList, m_trustedCertificateStore, true); if (issuer != null) { isTrusted = true; } if (issuer == null) { issuer = GetIssuer(certificate, m_issuerCertificateList, m_issuerCertificateStore, true); if (issuer == null) { issuer = GetIssuer(certificate, collection, null, true); } } if (issuer != null) { //isTrusted = true; issuers.Add(issuer); certificate = issuer.Find(false); // check for root. if (Utils.CompareDistinguishedName(certificate.Subject, certificate.Issuer)) { isChainComplete = true; break; } } else { isTrusted = false; } } while (issuer != null); if (!isChainComplete) { throw ServiceResultException.Create( StatusCodes.BadSecurityChecksFailed, "Certificate chain not complete.\r\nSubjectName: {0}\r\nIssuerName: {1}", certificates[0].SubjectName.Name, certificates[0].IssuerName.Name); } if (!isTrusted) { throw ServiceResultException.Create( StatusCodes.BadCertificateUntrusted, "Certificate issuer is not trusted.\r\nSubjectName: {0}\r\nIssuerName: {1}", certificates[0].SubjectName.Name, certificates[0].IssuerName.Name); } return(isTrusted); }
/// <summary> /// Loads the private key from a PFX file in the certificate store. /// </summary> public X509Certificate2 LoadPrivateKey(string thumbprint, string subjectName, string password) { if (m_certificateSubdir == null || !m_certificateSubdir.Exists) { return(null); } if (string.IsNullOrEmpty(thumbprint) && string.IsNullOrEmpty(subjectName)) { return(null); } foreach (FileInfo file in m_certificateSubdir.GetFiles("*.der")) { try { X509Certificate2 certificate = new X509Certificate2(file.FullName); if (!String.IsNullOrEmpty(thumbprint)) { if (!string.Equals(certificate.Thumbprint, thumbprint, StringComparison.CurrentCultureIgnoreCase)) { continue; } } if (!String.IsNullOrEmpty(subjectName)) { if (!Utils.CompareDistinguishedName(subjectName, certificate.Subject)) { if (subjectName.Contains("=")) { continue; } if (!Utils.ParseDistinguishedName(certificate.Subject).Any(s => s.Equals("CN=" + subjectName, StringComparison.OrdinalIgnoreCase))) { continue; } } } string fileRoot = file.Name.Substring(0, file.Name.Length - file.Extension.Length); StringBuilder filePath = new StringBuilder(); filePath.Append(m_privateKeySubdir.FullName); filePath.Append(Path.DirectorySeparatorChar); filePath.Append(fileRoot); FileInfo privateKeyFile = new FileInfo(filePath.ToString() + ".pfx"); password = password ?? String.Empty; try { certificate = new X509Certificate2( privateKeyFile.FullName, password, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.UserKeySet); if (CertificateFactory.VerifyRSAKeyPair(certificate, certificate, true)) { return(certificate); } } catch (Exception) { certificate = new X509Certificate2( privateKeyFile.FullName, password, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet); if (CertificateFactory.VerifyRSAKeyPair(certificate, certificate, true)) { return(certificate); } } } catch (Exception e) { Utils.Trace(e, "Could not load private key for certificate " + subjectName); } } return(null); }
/// <summary> /// Loads the private key from a PFX file in the certificate store. /// </summary> public X509Certificate2 LoadPrivateKey(string thumbprint, string subjectName, System.Security.SecureString password) { if (m_certificateSubdir == null || !m_certificateSubdir.Exists) { return(null); } if (string.IsNullOrEmpty(thumbprint) && string.IsNullOrEmpty(subjectName)) { return(null); } foreach (FileInfo file in m_certificateSubdir.GetFiles("*.der")) { try { X509Certificate2 certificate = new X509Certificate2(file.FullName); if (!String.IsNullOrEmpty(thumbprint)) { if (!string.Equals(certificate.Thumbprint, thumbprint, StringComparison.CurrentCultureIgnoreCase)) { continue; } } if (!String.IsNullOrEmpty(subjectName)) { if (!Utils.CompareDistinguishedName(subjectName, certificate.Subject)) { if (subjectName.Contains("=") || !certificate.Subject.Contains("CN=" + subjectName)) { continue; } } } string fileRoot = file.Name.Substring(0, file.Name.Length - file.Extension.Length); StringBuilder filePath = new StringBuilder(); filePath.Append(m_privateKeySubdir.FullName); filePath.Append("\\"); filePath.Append(fileRoot); FileInfo privateKeyFile = new FileInfo(filePath.ToString() + ".pfx"); certificate = new X509Certificate2( privateKeyFile.FullName, (password == null)?new System.Security.SecureString():password, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet); System.Security.Cryptography.RSACryptoServiceProvider rsa = certificate.PrivateKey as System.Security.Cryptography.RSACryptoServiceProvider; if (rsa != null && rsa.CspKeyContainerInfo.Exportable) { int inputBlockSize = rsa.KeySize / 8 - 42; byte[] bytes1 = rsa.Encrypt(new byte[inputBlockSize], true); byte[] bytes2 = rsa.Decrypt(bytes1, true); if (bytes2 != null) { // Utils.Trace(1, "RSA: {0}", certificate.Thumbprint); return(certificate); } } return(certificate); } catch (Exception e) { Utils.Trace(e, "Could not load private key certificate from file: {0}", file.Name); } } return(null); }
/// <summary> /// Throws an exception if validation fails. /// </summary> /// <param name="certificates">The certificates to be checked.</param> /// <exception cref="ServiceResultException">If certificate[0] cannot be accepted</exception> protected virtual async Task InternalValidate(X509Certificate2Collection certificates) { X509Certificate2 certificate = certificates[0]; // check for previously validated certificate. X509Certificate2 certificate2 = null; if (m_validatedCertificates.TryGetValue(certificate.Thumbprint, out certificate2)) { if (Utils.IsEqual(certificate2.RawData, certificate.RawData)) { return; } } CertificateIdentifier trustedCertificate = await GetTrustedCertificate(certificate); // get the issuers (checks the revocation lists if using directory stores). List <CertificateIdentifier> issuers = new List <CertificateIdentifier>(); bool isIssuerTrusted = await GetIssuers(certificates, issuers); // setup policy chain X509ChainPolicy policy = new X509ChainPolicy(); policy.RevocationFlag = X509RevocationFlag.EntireChain; policy.RevocationMode = X509RevocationMode.NoCheck; policy.VerificationFlags = X509VerificationFlags.NoFlag; foreach (CertificateIdentifier issuer in issuers) { if ((issuer.ValidationOptions & CertificateValidationOptions.SuppressRevocationStatusUnknown) != 0) { policy.VerificationFlags |= X509VerificationFlags.IgnoreCertificateAuthorityRevocationUnknown; policy.VerificationFlags |= X509VerificationFlags.IgnoreCtlSignerRevocationUnknown; policy.VerificationFlags |= X509VerificationFlags.IgnoreEndRevocationUnknown; policy.VerificationFlags |= X509VerificationFlags.IgnoreRootRevocationUnknown; } // we did the revocation check in the GetIssuers call. No need here. policy.RevocationMode = X509RevocationMode.NoCheck; policy.ExtraStore.Add(issuer.Certificate); } // build chain. bool chainStatusChecked = false; X509Chain chain = new X509Chain(); chain.ChainPolicy = policy; chain.Build(certificate); // check the chain results. CertificateIdentifier target = trustedCertificate; if (target == null) { target = new CertificateIdentifier(certificate); } for (int ii = 0; ii < chain.ChainElements.Count; ii++) { X509ChainElement element = chain.ChainElements[ii]; CertificateIdentifier issuer = null; if (ii < issuers.Count) { issuer = issuers[ii]; } // check for chain status errors. foreach (X509ChainStatus status in element.ChainElementStatus) { ServiceResult result = CheckChainStatus(status, target, issuer, (ii != 0)); if (ServiceResult.IsBad(result)) { // check untrusted certificates. if (trustedCertificate == null) { throw new ServiceResultException(StatusCodes.BadSecurityChecksFailed); } throw new ServiceResultException(result); } chainStatusChecked = true; } if (issuer != null) { target = issuer; } } // check whether the chain is complete (if there is a chain) bool issuedByCA = !Utils.CompareDistinguishedName(certificate.Subject, certificate.Issuer); bool chainIncomplete = false; if (issuers.Count > 0) { var rootCertificate = issuers[issuers.Count - 1].Certificate; if (!Utils.CompareDistinguishedName(rootCertificate.Subject, rootCertificate.Issuer)) { chainIncomplete = true; } } else { if (issuedByCA) { // no issuer found at all chainIncomplete = true; } } if (issuedByCA && (!chainStatusChecked || chainIncomplete)) { throw ServiceResultException.Create( StatusCodes.BadCertificateChainIncomplete, "Certificate chain validation incomplete.\r\nSubjectName: {0}\r\nIssuerName: {1}", certificate.SubjectName.Name, certificate.IssuerName.Name); } // check if certificate issuer is trusted. if (issuedByCA && !isIssuerTrusted && trustedCertificate == null) { if (m_applicationCertificate == null || !Utils.IsEqual(m_applicationCertificate.RawData, certificate.RawData)) { throw ServiceResultException.Create( StatusCodes.BadCertificateUntrusted, "Certificate issuer is not trusted.\r\nSubjectName: {0}\r\nIssuerName: {1}", certificate.SubjectName.Name, certificate.IssuerName.Name); } } // check if certificate is trusted. if (trustedCertificate == null && !isIssuerTrusted) { if (m_applicationCertificate == null || !Utils.IsEqual(m_applicationCertificate.RawData, certificate.RawData)) { throw ServiceResultException.Create( StatusCodes.BadCertificateUntrusted, "Certificate is not trusted.\r\nSubjectName: {0}\r\nIssuerName: {1}", certificate.SubjectName.Name, certificate.IssuerName.Name); } } // check if certificate is valid for use as app/sw or user cert X509KeyUsageFlags certificateKeyUsage = CertificateFactory.GetKeyUsage(certificate); if ((certificateKeyUsage & X509KeyUsageFlags.DataEncipherment) == 0) { throw new ServiceResultException(StatusCodes.BadCertificateUseNotAllowed, "Usage of certificate is not allowed."); } // check if minimum requirements are met if (m_rejectSHA1SignedCertificates && IsSHA1SignatureAlgorithm(certificate.SignatureAlgorithm)) { throw new ServiceResultException(StatusCodes.BadCertificatePolicyCheckFailed, "SHA1 signed certificates are not trusted"); } if (certificate.GetRSAPublicKey().KeySize < m_minimumCertificateKeySize) { throw new ServiceResultException(StatusCodes.BadCertificatePolicyCheckFailed, "Certificate doesn't meet minimum key length requirement"); } }
/// <summary> /// Returns the certificate information for a trusted issuer certificate. /// </summary> private async Task <CertificateIdentifier> GetIssuer( X509Certificate2 certificate, CertificateIdentifierCollection explicitList, CertificateStoreIdentifier certificateStore, bool checkRecovationStatus) { // check if self-signed. if (Utils.CompareDistinguishedName(certificate.Subject, certificate.Issuer)) { return(null); } string subjectName = certificate.IssuerName.Name; string keyId = null; string serialNumber = null; // find the authority key identifier. X509AuthorityKeyIdentifierExtension authority = FindAuthorityKeyIdentifier(certificate); if (authority != null) { keyId = authority.KeyId; serialNumber = authority.SerialNumber; } // check in explicit list. if (explicitList != null) { for (int ii = 0; ii < explicitList.Count; ii++) { X509Certificate2 issuer = await explicitList[ii].Find(false); if (issuer != null) { if (!IsIssuerAllowed(issuer)) { continue; } if (Match(issuer, subjectName, serialNumber, keyId)) { // can't check revocation. return(new CertificateIdentifier(issuer, CertificateValidationOptions.SuppressRevocationStatusUnknown)); } } } } // check in certificate store. if (certificateStore != null) { ICertificateStore store = certificateStore.OpenStore(); try { X509Certificate2Collection certificates = await store.Enumerate(); for (int ii = 0; ii < certificates.Count; ii++) { X509Certificate2 issuer = certificates[ii]; if (issuer != null) { if (!IsIssuerAllowed(issuer)) { continue; } if (Match(issuer, subjectName, serialNumber, keyId)) { CertificateValidationOptions options = certificateStore.ValidationOptions; // already checked revocation for file based stores. windows based stores always suppress. options |= CertificateValidationOptions.SuppressRevocationStatusUnknown; if (checkRecovationStatus) { StatusCode status = store.IsRevoked(issuer, certificate); if (StatusCode.IsBad(status) && status != StatusCodes.BadNotSupported) { if (status == StatusCodes.BadCertificateRevocationUnknown) { if (CertificateFactory.IsCertificateAuthority(certificate)) { status.Code = StatusCodes.BadCertificateIssuerRevocationUnknown; } if (m_rejectUnknownRevocationStatus) { throw new ServiceResultException(status); } } else { throw new ServiceResultException(status); } } } return(new CertificateIdentifier(certificates[ii], options)); } } } } finally { store.Close(); } } // not a trusted issuer. return(null); }
/// <summary> /// Returns true the certificate is in the CRL. /// </summary> public bool IsRevoked(X509Certificate2 certificate) { IntPtr pData1 = IntPtr.Zero; IntPtr pData2 = IntPtr.Zero; int dwDataSize1 = 0; try { // check that the issuer matches. if (m_issuer == null || !Utils.CompareDistinguishedName(certificate.Issuer, m_issuer.Subject)) { throw new ServiceResultException(StatusCodes.BadCertificateInvalid, "Certificate was not created by the CRL issuer."); } // get the cert info for the target certificate. Win32.CERT_CONTEXT context = (Win32.CERT_CONTEXT)Marshal.PtrToStructure(certificate.Handle, typeof(Win32.CERT_CONTEXT)); // calculate amount of memory required. int bResult = Win32.CryptDecodeObjectEx( Win32.X509_ASN_ENCODING | Win32.PKCS_7_ASN_ENCODING, (IntPtr)Win32.X509_CERT_CRL_TO_BE_SIGNED, m_signedCrl.ToBeSigned.pbData, m_signedCrl.ToBeSigned.cbData, Win32.CRYPT_DECODE_NOCOPY_FLAG, IntPtr.Zero, pData1, ref dwDataSize1); if (bResult == 0) { throw Win32.GetLastError(StatusCodes.BadDecodingError, "Could not get size for CRL_INFO."); } // allocate memory. pData1 = Marshal.AllocHGlobal(dwDataSize1); // decode blob. bResult = Win32.CryptDecodeObjectEx( Win32.X509_ASN_ENCODING | Win32.PKCS_7_ASN_ENCODING, (IntPtr)Win32.X509_CERT_CRL_TO_BE_SIGNED, m_signedCrl.ToBeSigned.pbData, m_signedCrl.ToBeSigned.cbData, Win32.CRYPT_DECODE_NOCOPY_FLAG, IntPtr.Zero, pData1, ref dwDataSize1); if (bResult == 0) { throw Win32.GetLastError(StatusCodes.BadDecodingError, "Could not decode CRL_INFO."); } IntPtr[] pCRLs = new IntPtr[] { pData1 }; pData2 = Marshal.AllocHGlobal(IntPtr.Size * pCRLs.Length); Marshal.Copy(pCRLs, 0, pData2, pCRLs.Length); // check for revocation. bResult = Win32.CertVerifyCRLRevocation( Win32.X509_ASN_ENCODING | Win32.PKCS_7_ASN_ENCODING, context.pCertInfo, pCRLs.Length, pData2); if (bResult == 0) { return(true); } // not revoked. return(false); } finally { if (pData1 != IntPtr.Zero) { Marshal.FreeHGlobal(pData1); pData1 = IntPtr.Zero; } if (pData2 != IntPtr.Zero) { Marshal.FreeHGlobal(pData2); pData2 = IntPtr.Zero; } } }