public void Reset() { // note: this call doesn't Reset the X509ChainPolicy if ((status != null) && (status.Length != 0)) { status = null; } if (elements.Count > 0) { elements.Clear(); } if (roots != null) { roots.Close(); roots = null; } if (cas != null) { cas.Close(); cas = null; } collection = null; bce_restriction = null; working_public_key = null; }
private void Process(int n) { X509ChainElement x509ChainElement = this.elements[n]; X509Certificate2 certificate = x509ChainElement.Certificate; if (n != this.elements.Count - 1 && certificate.MonoCertificate.KeyAlgorithm == "1.2.840.10040.4.1" && certificate.MonoCertificate.KeyAlgorithmParameters == null) { X509Certificate2 certificate2 = this.elements[n + 1].Certificate; certificate.MonoCertificate.KeyAlgorithmParameters = certificate2.MonoCertificate.KeyAlgorithmParameters; } bool flag = this.working_public_key == null; if (!this.IsSignedWith(certificate, (!flag) ? this.working_public_key : certificate.PublicKey.Key) && (flag || n != this.elements.Count - 1 || this.IsSelfIssued(certificate))) { x509ChainElement.StatusFlags |= X509ChainStatusFlags.NotSignatureValid; } if (this.ChainPolicy.VerificationTime < certificate.NotBefore || this.ChainPolicy.VerificationTime > certificate.NotAfter) { x509ChainElement.StatusFlags |= X509ChainStatusFlags.NotTimeValid; } if (flag) { return; } if (!X500DistinguishedName.AreEqual(certificate.IssuerName, this.working_issuer_name)) { x509ChainElement.StatusFlags |= X509ChainStatusFlags.InvalidNameConstraints; } if (this.IsSelfIssued(certificate) || n != 0) { } }
private void WrapUp() { X509ChainElement element = elements [0]; X509Certificate2 certificate = element.Certificate; // 6.1.5.a - TODO if certificate n (our 0) wasn't self issued and explicit_policy != 0 if (IsSelfIssued(certificate)) { // TODO... decrement explicit_policy by 1 } // 6.1.5.b - TODO // 6.1.5.c,d,e - not required by the X509Chain implementation // 6.1.5.f - recognize and process other critical extension present in the certificate ProcessCertificateExtensions(element); // 6.1.5.g - TODO // uncompressed the flags into several elements for (int i = elements.Count - 1; i >= 0; i--) { elements [i].UncompressFlags(); } }
// This isn't how RFC3280 (section 6.3) deals with CRL, but then we don't (yet) support DP, deltas... private X509ChainStatusFlags CheckRevocation(X509Certificate2 certificate, int ca, bool online) { X509ChainStatusFlags result = X509ChainStatusFlags.RevocationStatusUnknown; X509ChainElement element = elements [ca]; X509Certificate2 ca_cert = element.Certificate; // find the CRL from the "right" CA while (IsSelfIssued(ca_cert) && (ca < elements.Count - 1)) { // try with this self-issued result = CheckRevocation(certificate, ca_cert, online); if (result != X509ChainStatusFlags.RevocationStatusUnknown) { break; } ca++; element = elements [ca]; ca_cert = element.Certificate; } if (result == X509ChainStatusFlags.RevocationStatusUnknown) { result = CheckRevocation(certificate, ca_cert, online); } return(result); }
private void CheckRevocationOnChain(X509ChainStatusFlags flag) { bool flag2 = (flag & X509ChainStatusFlags.PartialChain) != X509ChainStatusFlags.NoError; bool online; switch (this.ChainPolicy.RevocationMode) { case X509RevocationMode.NoCheck: return; case X509RevocationMode.Online: online = true; break; case X509RevocationMode.Offline: online = false; break; default: throw new InvalidOperationException(Locale.GetText("Invalid revocation mode.")); } bool flag3 = flag2; for (int i = this.elements.Count - 1; i >= 0; i--) { bool flag4 = true; switch (this.ChainPolicy.RevocationFlag) { case X509RevocationFlag.EndCertificateOnly: flag4 = (i == 0); break; case X509RevocationFlag.EntireChain: flag4 = true; break; case X509RevocationFlag.ExcludeRoot: flag4 = (i != this.elements.Count - 1); break; } X509ChainElement x509ChainElement = this.elements[i]; if (!flag3) { flag3 |= ((x509ChainElement.StatusFlags & X509ChainStatusFlags.NotSignatureValid) != X509ChainStatusFlags.NoError); } if (flag3) { x509ChainElement.StatusFlags |= X509ChainStatusFlags.RevocationStatusUnknown; x509ChainElement.StatusFlags |= X509ChainStatusFlags.OfflineRevocation; } else if (flag4 && !flag2 && !this.IsSelfIssued(x509ChainElement.Certificate)) { x509ChainElement.StatusFlags |= this.CheckRevocation(x509ChainElement.Certificate, i + 1, online); flag3 |= ((x509ChainElement.StatusFlags & X509ChainStatusFlags.Revoked) != X509ChainStatusFlags.NoError); } } }
private static bool HasUnsuppressedError(X509VerificationFlags flags, X509ChainElement element, bool isEndEntity) { foreach (X509ChainStatus status in element.ChainElementStatus) { if (status.Status == X509ChainStatusFlags.NoError) { return(false); } Debug.Assert( (status.Status & (status.Status - 1)) == 0, $"Only one bit should be set in status.Status ({status})"); // The Windows certificate store API only checks the time error for a "peer trust" certificate, // but we don't have a concept for that in Unix. If we did, we'd need to do that logic that here. // Note also that the logic is skipped if CERT_CHAIN_POLICY_IGNORE_PEER_TRUST_FLAG is set. X509VerificationFlags?suppressionFlag; if (status.Status == X509ChainStatusFlags.RevocationStatusUnknown) { if (isEndEntity) { suppressionFlag = X509VerificationFlags.IgnoreEndRevocationUnknown; } else if (IsSelfSigned(element.Certificate)) { suppressionFlag = X509VerificationFlags.IgnoreRootRevocationUnknown; } else { suppressionFlag = X509VerificationFlags.IgnoreCertificateAuthorityRevocationUnknown; } } else if (status.Status == X509ChainStatusFlags.OfflineRevocation) { // This is mainly a warning code, it's redundant to RevocationStatusUnknown continue; } else { suppressionFlag = GetSuppressionFlag(status.Status); } // If an error was found, and we do NOT have the suppression flag for it enabled, // we have an unsuppressed error, so return true. (If there's no suppression for a given code, // we (by definition) don't have that flag set. if (!suppressionFlag.HasValue || (flags & suppressionFlag) == 0) { return(true); } } return(false); }
internal unsafe X509ChainElementCollection (IntPtr pSimpleChain) { CAPI.CERT_SIMPLE_CHAIN simpleChain = new CAPI.CERT_SIMPLE_CHAIN(Marshal.SizeOf(typeof(CAPI.CERT_SIMPLE_CHAIN))); uint cbSize = (uint) Marshal.ReadInt32(pSimpleChain); if (cbSize > Marshal.SizeOf(simpleChain)) cbSize = (uint) Marshal.SizeOf(simpleChain); X509Utils.memcpy(pSimpleChain, new IntPtr(&simpleChain), cbSize); m_elements = new X509ChainElement[simpleChain.cElement]; for (int index = 0; index < m_elements.Length; index++) { m_elements[index] = new X509ChainElement(Marshal.ReadIntPtr(new IntPtr((long) simpleChain.rgpElement + index * Marshal.SizeOf(typeof(IntPtr))))); } }
private static void SaveIntermediateCertificates( X509ChainElement[] chainElements, HashSet<X509Certificate2> downloaded) { List<X509Certificate2> chainDownloaded = new List<X509Certificate2>(chainElements.Length); // It should be very unlikely that we would have downloaded something, the chain succeed, // and the thing we downloaded not being a part of the chain, but safer is better. for (int i = 0; i < chainElements.Length; i++) { X509Certificate2 elementCert = chainElements[i].Certificate; if (downloaded.Contains(elementCert)) { chainDownloaded.Add(elementCert); } } if (chainDownloaded.Count == 0) { return; } using (var userIntermediate = new X509Store(StoreName.CertificateAuthority, StoreLocation.CurrentUser)) { try { userIntermediate.Open(OpenFlags.ReadWrite); } catch (CryptographicException) { // Saving is opportunistic, just ignore failures return; } foreach (X509Certificate2 cert in chainDownloaded) { try { userIntermediate.Add(cert); } catch (CryptographicException) { // Saving is opportunistic, just ignore failures } catch (IOException) { // Saving is opportunistic, just ignore failures } } } }
private static bool HasUnsuppressedError(X509VerificationFlags flags, X509ChainElement element, bool isEndEntity) { foreach (X509ChainStatus status in element.ChainElementStatus) { if (status.Status == X509ChainStatusFlags.NoError) { return false; } Debug.Assert( (status.Status & (status.Status - 1)) == 0, "Only one bit is set in status.Status"); // The Windows certificate store API only checks the time error for a "peer trust" certificate, // but we don't have a concept for that in Unix. If we did, we'd need to do that logic that here. // Note also that that logic is skipped if CERT_CHAIN_POLICY_IGNORE_PEER_TRUST_FLAG is set. X509VerificationFlags? suppressionFlag; if (status.Status == X509ChainStatusFlags.RevocationStatusUnknown) { if (isEndEntity) { suppressionFlag = X509VerificationFlags.IgnoreEndRevocationUnknown; } else if (IsSelfSigned(element.Certificate)) { suppressionFlag = X509VerificationFlags.IgnoreRootRevocationUnknown; } else { suppressionFlag = X509VerificationFlags.IgnoreCertificateAuthorityRevocationUnknown; } } else { suppressionFlag = GetSuppressionFlag(status.Status); } // If an error was found, and we do NOT have the suppression flag for it enabled, // we have an unsuppressed error, so return true. (If there's no suppression for a given code, // we (by definition) don't have that flag set. if (!suppressionFlag.HasValue || (flags & suppressionFlag) == 0) { return true; } } return false; }
private void WrapUp() { X509ChainElement x509ChainElement = elements[0]; X509Certificate2 certificate = x509ChainElement.Certificate; if (IsSelfIssued(certificate)) { } ProcessCertificateExtensions(x509ChainElement); for (int num = elements.Count - 1; num >= 0; num--) { elements[num].UncompressFlags(); } }
internal void AssignCertificate(X509ChainElement chainElement) { const int CERT_HEIGHT = 175; const int CERT_PADDING = 5; var numberOfCertificates = Controls.Count; var currentOffset = (numberOfCertificates * CERT_HEIGHT) + (CERT_PADDING * numberOfCertificates) + CERT_PADDING; var newCertificate = new CertificateControl(); newCertificate.Top = currentOffset; newCertificate.Height = CERT_HEIGHT; newCertificate.Width = this.Width; newCertificate.Anchor = AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Left; newCertificate.SetCertificate(chainElement); Controls.Add(newCertificate); }
static bool ChainElementHasProblems(X509ChainElement chainElement, X509ChainStatusFlags problemFlags) { // If the builder finds problems with the cert, it will provide a list of "status" flags for the cert X509ChainStatus[] chainElementStatus = chainElement.ChainElementStatus; // If the list is empty or the list is null, then there were NO problems with the cert if (chainElementStatus.IsNullOrEmpty()) { return false; } // Return true if there are any status flags we care about return chainElementStatus.Any(s => (s.Status & problemFlags) != 0); }
private void WrapUp() { X509ChainElement x509ChainElement = this.elements[0]; X509Certificate2 certificate = x509ChainElement.Certificate; if (this.IsSelfIssued(certificate)) { } this.ProcessCertificateExtensions(x509ChainElement); for (int i = this.elements.Count - 1; i >= 0; i--) { this.elements[i].UncompressFlags(); } }
private void PrepareForNextCertificate(int n) { X509ChainElement x509ChainElement = this.elements[n]; X509Certificate2 certificate = x509ChainElement.Certificate; this.working_issuer_name = certificate.SubjectName; this.working_public_key = certificate.PublicKey.Key; X509BasicConstraintsExtension x509BasicConstraintsExtension = (X509BasicConstraintsExtension)certificate.Extensions["2.5.29.19"]; if (x509BasicConstraintsExtension != null) { if (!x509BasicConstraintsExtension.CertificateAuthority) { x509ChainElement.StatusFlags |= X509ChainStatusFlags.InvalidBasicConstraints; } } else if (certificate.Version >= 3) { x509ChainElement.StatusFlags |= X509ChainStatusFlags.InvalidBasicConstraints; } if (!this.IsSelfIssued(certificate)) { if (this.max_path_length > 0) { this.max_path_length--; } else if (this.bce_restriction != null) { this.bce_restriction.StatusFlags |= X509ChainStatusFlags.InvalidBasicConstraints; } } if (x509BasicConstraintsExtension != null && x509BasicConstraintsExtension.HasPathLengthConstraint && x509BasicConstraintsExtension.PathLengthConstraint < this.max_path_length) { this.max_path_length = x509BasicConstraintsExtension.PathLengthConstraint; this.bce_restriction = x509ChainElement; } X509KeyUsageExtension x509KeyUsageExtension = (X509KeyUsageExtension)certificate.Extensions["2.5.29.15"]; if (x509KeyUsageExtension != null) { X509KeyUsageFlags x509KeyUsageFlags = X509KeyUsageFlags.KeyCertSign; if ((x509KeyUsageExtension.KeyUsages & x509KeyUsageFlags) != x509KeyUsageFlags) { x509ChainElement.StatusFlags |= X509ChainStatusFlags.NotValidForUsage; } } this.ProcessCertificateExtensions(x509ChainElement); }
internal unsafe X509ChainElementCollection(IntPtr pSimpleChain) { CAPI.CERT_SIMPLE_CHAIN simpleChain = new CAPI.CERT_SIMPLE_CHAIN(Marshal.SizeOf(typeof(CAPI.CERT_SIMPLE_CHAIN))); uint cbSize = (uint)Marshal.ReadInt32(pSimpleChain); if (cbSize > Marshal.SizeOf(simpleChain)) { cbSize = (uint)Marshal.SizeOf(simpleChain); } X509Utils.memcpy(pSimpleChain, new IntPtr(&simpleChain), cbSize); m_elements = new X509ChainElement[simpleChain.cElement]; for (int index = 0; index < m_elements.Length; index++) { m_elements[index] = new X509ChainElement(Marshal.ReadIntPtr(new IntPtr((long)simpleChain.rgpElement + index * Marshal.SizeOf(typeof(IntPtr))))); } }
private void ProcessCertificateExtensions(X509ChainElement element) { X509ExtensionEnumerator enumerator = element.Certificate.Extensions.GetEnumerator(); while (enumerator.MoveNext()) { X509Extension current = enumerator.Current; if (current.Critical) { switch (current.Oid.Value) { case "2.5.29.15": case "2.5.29.19": continue; } element.StatusFlags |= X509ChainStatusFlags.InvalidExtension; } } }
private void ProcessCertificateExtensions(X509ChainElement element) { foreach (X509Extension ext in element.Certificate.Extensions) { if (ext.Critical) { switch (ext.Oid.Value) { case "2.5.29.15": // X509KeyUsageExtension case "2.5.29.19": // X509BasicConstraintsExtension // we processed this extension break; default: // note: Under Windows XP MS implementation seems to ignore // certificate with unknown critical extensions. element.StatusFlags |= X509ChainStatusFlags.InvalidExtension; break; } } } }
private X509ChainStatusFlags CheckRevocation(X509Certificate2 certificate, int ca, bool online) { X509ChainStatusFlags x509ChainStatusFlags = X509ChainStatusFlags.RevocationStatusUnknown; X509ChainElement x509ChainElement = this.elements[ca]; X509Certificate2 certificate2 = x509ChainElement.Certificate; while (this.IsSelfIssued(certificate2) && ca < this.elements.Count - 1) { x509ChainStatusFlags = this.CheckRevocation(certificate, certificate2, online); if (x509ChainStatusFlags != X509ChainStatusFlags.RevocationStatusUnknown) { break; } ca++; x509ChainElement = this.elements[ca]; certificate2 = x509ChainElement.Certificate; } if (x509ChainStatusFlags == X509ChainStatusFlags.RevocationStatusUnknown) { x509ChainStatusFlags = this.CheckRevocation(certificate, certificate2, online); } return(x509ChainStatusFlags); }
public static CertificateErrors GetCertificateErrors(X509ChainElement chainElement) { //If the length is not zero and the items aren't "OK", return critical. if (chainElement.ChainElementStatus.Length != 0 && !chainElement.ChainElementStatus.All(s => s.Status == X509ChainStatusFlags.NoError)) { return CertificateErrors.Critical; } //Recheck with revocation var chain = new X509Chain(); chain.ChainPolicy.RevocationMode = X509RevocationMode.Online; chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EndCertificateOnly; chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags & ~X509VerificationFlags.IgnoreEndRevocationUnknown; var builtChain = chain.Build(chainElement.Certificate); if (chain.ChainStatus.Any(s => s.Status == X509ChainStatusFlags.RevocationStatusUnknown)) { return CertificateErrors.UnknownRevocation; } if (!builtChain) { return CertificateErrors.Critical; } return CertificateErrors.None; }
public override void Reset() { // note: this call doesn't Reset the X509ChainPolicy if ((status != null) && (status.Length != 0)) { status = null; } if (elements.Count > 0) { elements.Clear(); } if (user_root_store != null) { user_root_store.Close(); user_root_store = null; } if (root_store != null) { root_store.Close(); root_store = null; } if (user_ca_store != null) { user_ca_store.Close(); user_ca_store = null; } if (ca_store != null) { ca_store.Close(); ca_store = null; } roots = null; cas = null; collection = null; bce_restriction = null; working_public_key = null; }
private void ProcessCertificateExtensions(X509ChainElement element) { foreach (X509Extension x509Extension in element.Certificate.Extensions) { if (x509Extension.Critical) { string value = x509Extension.Oid.Value; if (value != null) { if (X509Chain.< > f__switch$mapB == null) { X509Chain.< > f__switch$mapB = new Dictionary <string, int>(2) { { "2.5.29.15", 0 }, { "2.5.29.19", 0 } }; } int num; if (X509Chain.< > f__switch$mapB.TryGetValue(value, out num)) { if (num == 0) { continue; } } } element.StatusFlags |= X509ChainStatusFlags.InvalidExtension; } } }
/// <summary>Clears the current <see cref="T:System.Security.Cryptography.X509Certificates.X509Chain" /> object.</summary> public void Reset() { if (this.status != null && this.status.Length != 0) { this.status = null; } if (this.elements.Count > 0) { this.elements.Clear(); } if (this.roots != null) { this.roots.Close(); this.roots = null; } if (this.cas != null) { this.cas.Close(); this.cas = null; } this.collection = null; this.bce_restriction = null; this.working_public_key = null; }
/// <summary>Clears the current <see cref="T:System.Security.Cryptography.X509Certificates.X509Chain" /> object.</summary> public void Reset() { if (status != null && status.Length != 0) { status = null; } if (elements.Count > 0) { elements.Clear(); } if (roots != null) { roots.Close(); roots = null; } if (cas != null) { cas.Close(); cas = null; } collection = null; bce_restriction = null; working_public_key = null; }
void NotifyProblem(X509ChainElement chainElement) { if (this.Problem != null) { try { this.Problem(chainElement); } catch { } } }
public void CopyTo(X509ChainElement[] array, int index) { ((ICollection)this).CopyTo(array, index); }
internal X509ChainElementCollection(X509ChainElement[] chainElements) { Debug.Assert(chainElements != null, "chainElements != null"); _elements = chainElements; }
internal void OnCertificateProblem(X509ChainElement chainElement) { if (chainElement != null) { Logger.Error("Chain Element has problem {0}", this.Summarize(chainElement)); } }
public static IChainPal BuildChain( X509Certificate2 leaf, HashSet<X509Certificate2> candidates, HashSet<X509Certificate2> downloaded, HashSet<X509Certificate2> systemTrusted, OidCollection applicationPolicy, OidCollection certificatePolicy, X509RevocationMode revocationMode, X509RevocationFlag revocationFlag, DateTime verificationTime, ref TimeSpan remainingDownloadTime) { X509ChainElement[] elements; List<X509ChainStatus> overallStatus = new List<X509ChainStatus>(); WorkingChain workingChain = new WorkingChain(); Interop.Crypto.X509StoreVerifyCallback workingCallback = workingChain.VerifyCallback; // 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.Crypto.X509StoreCreate()) using (SafeX509StoreCtxHandle storeCtx = Interop.Crypto.X509StoreCtxCreate()) { Interop.Crypto.CheckValidOpenSslHandle(store); Interop.Crypto.CheckValidOpenSslHandle(storeCtx); bool lookupCrl = revocationMode != X509RevocationMode.NoCheck; foreach (X509Certificate2 cert in candidates) { OpenSslX509CertificateReader pal = (OpenSslX509CertificateReader)cert.Pal; if (!Interop.Crypto.X509StoreAddCert(store, pal.SafeHandle)) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } if (lookupCrl) { CrlCache.AddCrlForCertificate( cert, store, revocationMode, verificationTime, ref remainingDownloadTime); // If we only wanted the end-entity certificate CRL then don't look up // any more of them. lookupCrl = revocationFlag != X509RevocationFlag.EndCertificateOnly; } } if (revocationMode != X509RevocationMode.NoCheck) { if (!Interop.Crypto.X509StoreSetRevocationFlag(store, revocationFlag)) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } } SafeX509Handle leafHandle = ((OpenSslX509CertificateReader)leaf.Pal).SafeHandle; if (!Interop.Crypto.X509StoreCtxInit(storeCtx, store, leafHandle)) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } Interop.Crypto.X509StoreCtxSetVerifyCallback(storeCtx, workingCallback); Interop.Crypto.SetX509ChainVerifyTime(storeCtx, verificationTime); int verify = Interop.Crypto.X509VerifyCert(storeCtx); if (verify < 0) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } // Because our callback tells OpenSSL that every problem is ignorable, it should tell us that the // chain is just fine (unless it returned a negative code for an exception) Debug.Assert(verify == 1, "verify == 1"); using (SafeX509StackHandle chainStack = Interop.Crypto.X509StoreCtxGetChain(storeCtx)) { int chainSize = Interop.Crypto.GetX509StackFieldCount(chainStack); 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>(); List<Interop.Crypto.X509VerifyStatusCode> elementErrors = i < workingChain.Errors.Count ? workingChain.Errors[i] : null; if (elementErrors != null) { AddElementStatus(elementErrors, status, overallStatus); } IntPtr elementCertPtr = Interop.Crypto.GetX509StackField(chainStack, i); if (elementCertPtr == IntPtr.Zero) { throw Interop.Crypto.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 or the system // doesn't trust it, it's untrusted. if (downloaded.Contains(elementCert) || !systemTrusted.Contains(elementCert)) { AddElementStatus( Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_UNTRUSTED, status, overallStatus); } } elements[i] = new X509ChainElement(elementCert, status.ToArray(), ""); } } } GC.KeepAlive(workingCallback); 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.NotValidForUsage, 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 CertificateModel AssignCertificate(X509ChainElement chainElement, bool reportOnly, PublicPinnedKeys pinnedKey, X509Chain chain, int index) { var certificate = chainElement.Certificate; var algorithmBits = BitStrengthCalculator.CalculateStrength(certificate); var dn = DistinguishedNameParser.Parse(certificate.Subject); return new CertificateModel { CommonName = dn.ContainsKey("cn") ? dn["cn"].FirstOrDefault() ?? certificate.Thumbprint : certificate.Thumbprint, Thumbprint = certificate.Thumbprint, DistinguishedName = dn, SubjectAlternativeName = certificate.Extensions[KnownOids.X509Extensions.SubjectAltNameExtension]?.Format(false) ?? "None", PublicKey = new PublicKeyModel { Algorithm = algorithmBits.AlgorithmName, KeySizeBits = algorithmBits.BitSize, PublicKey = certificate.PublicKey.EncodedKeyValue.RawData }, BeginDate = certificate.NotBefore, EndDate = certificate.NotAfter, SerialNumber = certificate.SerialNumber ?? "None", SignatureAlgorithm = new SignatureAlgorithmModel { SignatureAlgorithm = certificate.SignatureAlgorithm, IsTrustedRoot = _rootStore.Certificates.Contains(certificate) || _userStore.Certificates.Contains(certificate) }, CertificateType = index == 0 ? GetCertificateType(certificate, chain) : CertificateType.None, CertificateCtModel = new AsyncProperty<CertificateCtModel>(Task.Factory.StartNew(() => GetCtModel(certificate))), Errors = new AsyncProperty<CertificateErrors>(Task.Factory.StartNew(() => CertificateErrorsCalculator.GetCertificateErrors(chainElement))), SpkiHashes = new AsyncProperty<SpkiHashesModel>(Task.Factory.StartNew(() => CalculateHashes(chainElement.Certificate, reportOnly, pinnedKey))), InstallCommand = new RelayCommand(parameter => CertificateUI.ShowImportCertificate(chainElement.Certificate, FiddlerApplication.UI)), ViewCommand = new RelayCommand(parameter => CertificateUI.ShowCertificate(chainElement.Certificate, FiddlerApplication.UI)), BrowseCommand = new RelayCommand(parameter => { var uri = parameter as Uri; if (uri?.Scheme == Uri.UriSchemeHttps) { Process.Start(uri.AbsoluteUri); } }) }; }
private CertificateModel AssignCertificate(X509ChainElement chainElement, bool reportOnly, PublicPinnedKeys pinnedKey) { var certificate = chainElement.Certificate; var algorithmBits = BitStrengthCalculator.CalculateStrength(certificate); var dn = DistinguishedNameParser.Parse(certificate.Subject); return new CertificateModel { CommonName = dn.ContainsKey("cn") ? dn["cn"].FirstOrDefault() ?? certificate.Thumbprint : certificate.Thumbprint, Thumbprint = certificate.Thumbprint, SubjectAlternativeName = certificate.Extensions[KnownOids.X509Extensions.SubjectAltNameExtension]?.Format(false) ?? "None", PublicKey = new PublicKeyModel { Algorithm = algorithmBits.AlgorithmName, KeySizeBits = algorithmBits.BitSize, PublicKey = certificate.PublicKey.EncodedKeyValue.RawData }, BeginDate = certificate.NotBefore, EndDate = certificate.NotAfter, SignatureAlgorithm = new SignatureAlgorithmModel { SignatureAlgorithm = certificate.SignatureAlgorithm, IsTrustedRoot = _rootStore.Certificates.Contains(certificate) || _userStore.Certificates.Contains(certificate) }, Errors = new AsyncProperty<CertificateErrors>(Task.Factory.StartNew(() => CertificateErrorsCalculator.GetCertificateErrors(chainElement))), SpkiHashes = new AsyncProperty<SpkiHashesModel>(Task.Factory.StartNew(() => CalculateHashes(chainElement.Certificate, reportOnly, pinnedKey))), InstallCommand = new RelayCommand(parameter => CertificateUI.ShowImportCertificate(chainElement.Certificate)), ViewCommand = new RelayCommand(parameter => CertificateUI.ShowCertificate(chainElement.Certificate)) }; }
// we check the revocation only once we have built the complete chain private void CheckRevocationOnChain(X509ChainStatusFlags flag) { bool partial = ((flag & X509ChainStatusFlags.PartialChain) != 0); bool online; switch (ChainPolicy.RevocationMode) { case X509RevocationMode.Online: // default online = true; break; case X509RevocationMode.Offline: online = false; break; case X509RevocationMode.NoCheck: return; default: throw new InvalidOperationException(Locale.GetText("Invalid revocation mode.")); } bool unknown = partial; // from the root down to the end-entity for (int i = elements.Count - 1; i >= 0; i--) { bool check = true; switch (ChainPolicy.RevocationFlag) { case X509RevocationFlag.EndCertificateOnly: check = (i == 0); break; case X509RevocationFlag.EntireChain: check = true; break; case X509RevocationFlag.ExcludeRoot: // default check = (i != (elements.Count - 1)); // anyway, who's gonna sign that the root is invalid ? break; } X509ChainElement element = elements [i]; // we can't assume the revocation status if the certificate is bad (e.g. invalid signature) if (!unknown) { unknown |= ((element.StatusFlags & X509ChainStatusFlags.NotSignatureValid) != 0); } if (unknown) { // we can skip the revocation checks as we can't be sure of them anyway element.StatusFlags |= X509ChainStatusFlags.RevocationStatusUnknown; element.StatusFlags |= X509ChainStatusFlags.OfflineRevocation; } else if (check && !partial && !IsSelfIssued(element.Certificate)) { // check for revocation (except for the trusted root and self-issued certs) element.StatusFlags |= CheckRevocation(element.Certificate, i + 1, online); // if revoked, then all others following in the chain are unknown... unknown |= ((element.StatusFlags & X509ChainStatusFlags.Revoked) != 0); } } }
internal string Summarize(X509ChainElement chainElement) { X509ChainStatusFlags problemFlags = m_agent.SecurityAgent.TrustModel.CertChainValidator.ProblemFlags; StringBuilder builder = new StringBuilder(); builder.Append(chainElement.Certificate.ExtractEmailNameOrName()); builder.Append(";"); foreach(X509ChainStatus status in chainElement.ChainElementStatus) { if ((status.Status & problemFlags) != 0) { builder.Append(status.Status); } } return builder.ToString(); }
public static IChainPal BuildChain( X509Certificate2 leaf, List<X509Certificate2> candidates, List<X509Certificate2> downloaded, List<X509Certificate2> systemTrusted, OidCollection applicationPolicy, OidCollection certificatePolicy, X509RevocationMode revocationMode, X509RevocationFlag revocationFlag, DateTime verificationTime, ref TimeSpan remainingDownloadTime) { 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); bool lookupCrl = revocationMode != X509RevocationMode.NoCheck; foreach (X509Certificate2 cert in candidates) { OpenSslX509CertificateReader pal = (OpenSslX509CertificateReader)cert.Pal; if (!Interop.libcrypto.X509_STORE_add_cert(store, pal.SafeHandle)) { throw Interop.libcrypto.CreateOpenSslCryptographicException(); } if (lookupCrl) { CrlCache.AddCrlForCertificate( cert, store, revocationMode, verificationTime, ref remainingDownloadTime); // If we only wanted the end-entity certificate CRL then don't look up // any more of them. lookupCrl = revocationFlag != X509RevocationFlag.EndCertificateOnly; } } if (revocationMode != X509RevocationMode.NoCheck) { Interop.libcrypto.X509VerifyFlags vfyFlags = Interop.libcrypto.X509VerifyFlags.X509_V_FLAG_CRL_CHECK; if (revocationFlag != X509RevocationFlag.EndCertificateOnly) { vfyFlags |= Interop.libcrypto.X509VerifyFlags.X509_V_FLAG_CRL_CHECK_ALL; } if (!Interop.libcrypto.X509_STORE_set_flags(store, vfyFlags)) { 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 or the system // doesn't trust it, it's untrusted. if (downloaded.Contains(elementCert) || !systemTrusted.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, }; }
/// <summary> /// Wraps the X509ChainElement creation method. /// </summary> /// <param name="element"> /// The X.509v3 chain element. /// </param> /// <returns> /// The <see cref="IX509ChainElement"/>. /// </returns> public IX509ChainElement Create(X509ChainElement element) { return new X509ChainElementWrap(new X509CertificateWrap(this.file, this.path, element.Certificate), element.ChainElementStatus); }
// methods public void CopyTo (X509ChainElement[] array, int index) { _list.CopyTo ((Array)array, index); }
private void Process(int n) { X509ChainElement element = elements [n]; X509Certificate2 certificate = element.Certificate; // pre-step: DSA certificates may inherit the parameters of their CA if ((n != elements.Count - 1) && (certificate.MonoCertificate.KeyAlgorithm == "1.2.840.10040.4.1")) { if (certificate.MonoCertificate.KeyAlgorithmParameters == null) { X509Certificate2 parent = elements [n + 1].Certificate; certificate.MonoCertificate.KeyAlgorithmParameters = parent.MonoCertificate.KeyAlgorithmParameters; } } bool root = (working_public_key == null); // 6.1.3.a.1 - check signature (with special case to deal with root certificates) if (!IsSignedWith(certificate, root ? certificate.PublicKey.Key : working_public_key)) { // another special case where only an end-entity is available and can't be verified. // In this case we do not report an invalid signature (since this is unknown) if (root || (n != elements.Count - 1) || IsSelfIssued(certificate)) { element.StatusFlags |= X509ChainStatusFlags.NotSignatureValid; } } // 6.1.3.a.2 - check validity period if ((ChainPolicy.VerificationTime < certificate.NotBefore) || (ChainPolicy.VerificationTime > certificate.NotAfter)) { element.StatusFlags |= X509ChainStatusFlags.NotTimeValid; } // TODO - for X509ChainStatusFlags.NotTimeNested (needs global structure) // note: most of them don't apply to the root certificate if (root) { return; } // 6.1.3.a.3 - revocation check (we're doing at the last stage) // note: you revoke a trusted root by removing it from your trusted store (i.e. no CRL can do this job) // 6.1.3.a.4 - check certificate issuer name if (!X500DistinguishedName.AreEqual(certificate.IssuerName, working_issuer_name)) { // NOTE: this is not the "right" error flag, but it's the closest one defined element.StatusFlags |= X509ChainStatusFlags.InvalidNameConstraints; } if (!IsSelfIssued(certificate) && (n != 0)) { // TODO 6.1.3.b - subject name in the permitted_subtrees ... // TODO 6.1.3.c - subject name not within excluded_subtrees... // TODO - check for X509ChainStatusFlags.InvalidNameConstraint // TODO - check for X509ChainStatusFlags.HasNotSupportedNameConstraint // TODO - check for X509ChainStatusFlags.HasNotPermittedNameConstraint // TODO - check for X509ChainStatusFlags.HasExcludedNameConstraint } // TODO 6.1.3.d - check if certificate policies extension is present //if (false) { // TODO - for X509ChainStatusFlags.InvalidPolicyConstraints // using X509ChainPolicy.ApplicationPolicy and X509ChainPolicy.CertificatePolicy // TODO - check for X509ChainStatusFlags.NoIssuanceChainPolicy //} else { // TODO 6.1.3.e - set valid_policy_tree to NULL //} // TODO 6.1.3.f - verify explict_policy > 0 if valid_policy_tree != NULL }
public static IChainPal BuildChain( X509Certificate2 leaf, X509Certificate2Collection candidates, 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; string errorMsg = null; if (verify == 0) { errorCode = Interop.libcrypto.X509_STORE_CTX_get_error(storeCtx); errorDepth = Interop.libcrypto.X509_STORE_CTX_get_error_depth(storeCtx); errorMsg = Interop.libcrypto.X509_verify_cert_error_string(errorCode); } elements = new X509ChainElement[chainSize]; for (int i = 0; i < chainSize; i++) { List<X509ChainStatus> status = new List<X509ChainStatus>(); if (i == errorDepth) { X509ChainStatus chainStatus = new X509ChainStatus { Status = MapVerifyErrorToChainStatus(errorCode), StatusInformation = errorMsg, }; status.Add(chainStatus); AddUniqueStatus(overallStatus, ref chainStatus); } IntPtr elementCertPtr = Interop.Crypto.GetX509StackField(chainStack, i); if (elementCertPtr == IntPtr.Zero) { throw Interop.libcrypto.CreateOpenSslCryptographicException(); } // Duplicate the certificate handle X509Certificate2 elementCert = new X509Certificate2(elementCertPtr); elements[i] = new X509ChainElement(elementCert, status.ToArray(), ""); } } } if ((certificatePolicy != null && certificatePolicy.Count > 0) || (applicationPolicy != null && applicationPolicy.Count > 0)) { X509Certificate2Collection certsToRead = new X509Certificate2Collection(); 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, }; }
// CTL == Certificate Trust List / NOT SUPPORTED // TODO - check for X509ChainStatusFlags.CtlNotTimeValid // TODO - check for X509ChainStatusFlags.CtlNotSignatureValid // TODO - check for X509ChainStatusFlags.CtlNotValidForUsage private void PrepareForNextCertificate(int n) { X509ChainElement element = elements [n]; X509Certificate2 certificate = element.Certificate; // TODO 6.1.4.a-b // 6.1.4.c working_issuer_name = certificate.SubjectName; // 6.1.4.d-e - our key includes both the public key and it's parameters working_public_key = certificate.PublicKey.Key; // 6.1.4.f // working_public_key_algorithm = certificate.PublicKey.Oid.Value; // TODO 6.1.4.g-j // 6.1.4.k - Verify that the certificate is a CA certificate X509BasicConstraintsExtension bce = (certificate.Extensions["2.5.29.19"] as X509BasicConstraintsExtension); if (bce != null) { if (!bce.CertificateAuthority) { element.StatusFlags |= X509ChainStatusFlags.InvalidBasicConstraints; } } else if (certificate.Version >= 3) { // recent (v3+) CA certificates must include BCE element.StatusFlags |= X509ChainStatusFlags.InvalidBasicConstraints; } // 6.1.4.l - if the certificate isn't self-issued... if (!IsSelfIssued(certificate)) { // ... verify that max_path_length > 0 if (max_path_length > 0) { max_path_length--; } else { // to match MS the reported status must be against the certificate // with the BCE and not where the path is too long. It also means // that this condition has to be reported only once if (bce_restriction != null) { bce_restriction.StatusFlags |= X509ChainStatusFlags.InvalidBasicConstraints; } } } // 6.1.4.m - if pathLengthConstraint is present... if ((bce != null) && (bce.HasPathLengthConstraint)) { // ... and is less that max_path_length, set max_path_length to it's value if (bce.PathLengthConstraint < max_path_length) { max_path_length = bce.PathLengthConstraint; bce_restriction = element; } } // 6.1.4.n - if key usage extension is present... X509KeyUsageExtension kue = (certificate.Extensions["2.5.29.15"] as X509KeyUsageExtension); if (kue != null) { // ... verify keyCertSign is set X509KeyUsageFlags success = X509KeyUsageFlags.KeyCertSign; if ((kue.KeyUsages & success) != success) { element.StatusFlags |= X509ChainStatusFlags.NotValidForUsage; } } // 6.1.4.o - recognize and process other critical extension present in the certificate ProcessCertificateExtensions(element); }
// CTL == Certificate Trust List / NOT SUPPORTED // TODO - check for X509ChainStatusFlags.CtlNotTimeValid // TODO - check for X509ChainStatusFlags.CtlNotSignatureValid // TODO - check for X509ChainStatusFlags.CtlNotValidForUsage private void PrepareForNextCertificate (int n) { X509ChainElement element = elements [n]; X509Certificate2 certificate = element.Certificate; // TODO 6.1.4.a-b // 6.1.4.c working_issuer_name = certificate.SubjectName; // 6.1.4.d-e - our key includes both the public key and it's parameters working_public_key = certificate.PublicKey.Key; // 6.1.4.f // working_public_key_algorithm = certificate.PublicKey.Oid.Value; // TODO 6.1.4.g-j // 6.1.4.k - Verify that the certificate is a CA certificate X509BasicConstraintsExtension bce = (certificate.Extensions["2.5.29.19"] as X509BasicConstraintsExtension); if (bce != null) { if (!bce.CertificateAuthority) { element.StatusFlags |= X509ChainStatusFlags.InvalidBasicConstraints; } } else if (certificate.Version >= 3) { // recent (v3+) CA certificates must include BCE element.StatusFlags |= X509ChainStatusFlags.InvalidBasicConstraints; } // 6.1.4.l - if the certificate isn't self-issued... if (!IsSelfIssued (certificate)) { // ... verify that max_path_length > 0 if (max_path_length > 0) { max_path_length--; } else { // to match MS the reported status must be against the certificate // with the BCE and not where the path is too long. It also means // that this condition has to be reported only once if (bce_restriction != null) { bce_restriction.StatusFlags |= X509ChainStatusFlags.InvalidBasicConstraints; } } } // 6.1.4.m - if pathLengthConstraint is present... if ((bce != null) && (bce.HasPathLengthConstraint)) { // ... and is less that max_path_length, set max_path_length to it's value if (bce.PathLengthConstraint < max_path_length) { max_path_length = bce.PathLengthConstraint; bce_restriction = element; } } // 6.1.4.n - if key usage extension is present... X509KeyUsageExtension kue = (certificate.Extensions["2.5.29.15"] as X509KeyUsageExtension); if (kue != null) { // ... verify keyCertSign is set X509KeyUsageFlags success = X509KeyUsageFlags.KeyCertSign; if ((kue.KeyUsages & success) != success) element.StatusFlags |= X509ChainStatusFlags.NotValidForUsage; } // 6.1.4.o - recognize and process other critical extension present in the certificate ProcessCertificateExtensions (element); }
public void CopyTo(X509ChainElement[] array, int index) { }
internal void Evaluate( DateTime verificationTime, OidCollection?applicationPolicy, OidCollection?certificatePolicy, X509RevocationMode revocationMode, X509RevocationFlag revocationFlag) { Debug.Assert(_chainContext != null); long timeInMsFromUnixEpoch = new DateTimeOffset(verificationTime).ToUnixTimeMilliseconds(); _isValid = Interop.AndroidCrypto.X509ChainBuild(_chainContext, timeInMsFromUnixEpoch); if (!_isValid) { // Android always validates name, time, signature, and trusted root. // There is no way bypass that validation and build a path. ChainElements = Array.Empty <X509ChainElement>(); Interop.AndroidCrypto.ValidationError[] errors = Interop.AndroidCrypto.X509ChainGetErrors(_chainContext); var chainStatus = new X509ChainStatus[errors.Length]; for (int i = 0; i < errors.Length; i++) { Interop.AndroidCrypto.ValidationError error = errors[i]; chainStatus[i] = ValidationErrorToChainStatus(error); Marshal.FreeHGlobal(error.Message); } ChainStatus = chainStatus; return; } byte checkedRevocation; int res = Interop.AndroidCrypto.X509ChainValidate(_chainContext, revocationMode, revocationFlag, out checkedRevocation); if (res != 1) { throw new CryptographicException(); } X509Certificate2[] certs = Interop.AndroidCrypto.X509ChainGetCertificates(_chainContext); List <X509ChainStatus> overallStatus = new List <X509ChainStatus>(); List <X509ChainStatus>[] statuses = new List <X509ChainStatus> [certs.Length]; // Android will stop checking after the first error it hits, so we track the first // instances of revocation and non-revocation errors to fix-up the status of elements // beyond the first error int firstNonRevocationErrorIndex = -1; int firstRevocationErrorIndex = -1; Dictionary <int, List <X509ChainStatus> > errorsByIndex = GetStatusByIndex(_chainContext); foreach (int index in errorsByIndex.Keys) { List <X509ChainStatus> errors = errorsByIndex[index]; for (int i = 0; i < errors.Count; i++) { X509ChainStatus status = errors[i]; AddUniqueStatus(overallStatus, ref status); } // -1 indicates that error is not tied to a specific index if (index != -1) { statuses[index] = errorsByIndex[index]; if (errorsByIndex[index].Exists(s => s.Status == X509ChainStatusFlags.Revoked || s.Status == X509ChainStatusFlags.RevocationStatusUnknown)) { firstRevocationErrorIndex = Math.Max(index, firstRevocationErrorIndex); } else { firstNonRevocationErrorIndex = Math.Max(index, firstNonRevocationErrorIndex); } } } if (firstNonRevocationErrorIndex > 0) { // Assign PartialChain to everything from the first non-revocation error to the end certificate X509ChainStatus partialChainStatus = new X509ChainStatus { Status = X509ChainStatusFlags.PartialChain, StatusInformation = SR.Chain_PartialChain, }; AddStatusFromIndexToEndCertificate(firstNonRevocationErrorIndex - 1, ref partialChainStatus, statuses, overallStatus); } if (firstRevocationErrorIndex > 0) { // Assign RevocationStatusUnknown to everything from the first revocation error to the end certificate X509ChainStatus revocationUnknownStatus = new X509ChainStatus { Status = X509ChainStatusFlags.RevocationStatusUnknown, StatusInformation = SR.Chain_RevocationStatusUnknown, }; AddStatusFromIndexToEndCertificate(firstRevocationErrorIndex - 1, ref revocationUnknownStatus, statuses, overallStatus); } if (revocationMode != X509RevocationMode.NoCheck && checkedRevocation == 0) { // Revocation checking was requested, but not performed (due to basic validation failing) // Assign RevocationStatusUnknown to everything X509ChainStatus revocationUnknownStatus = new X509ChainStatus { Status = X509ChainStatusFlags.RevocationStatusUnknown, StatusInformation = SR.Chain_RevocationStatusUnknown, }; AddStatusFromIndexToEndCertificate(statuses.Length - 1, ref revocationUnknownStatus, statuses, overallStatus); } if (!IsPolicyMatch(certs, applicationPolicy, certificatePolicy)) { // Assign NotValidForUsage to everything X509ChainStatus policyFailStatus = new X509ChainStatus { Status = X509ChainStatusFlags.NotValidForUsage, StatusInformation = SR.Chain_NoPolicyMatch, }; AddStatusFromIndexToEndCertificate(statuses.Length - 1, ref policyFailStatus, statuses, overallStatus); } X509ChainElement[] elements = new X509ChainElement[certs.Length]; for (int i = 0; i < certs.Length; i++) { X509ChainStatus[] elementStatus = statuses[i] == null?Array.Empty <X509ChainStatus>() : statuses[i].ToArray(); elements[i] = new X509ChainElement(certs[i], elementStatus, string.Empty); } ChainElements = elements; ChainStatus = overallStatus.ToArray(); }
public void SetCertificate(X509ChainElement chainElement) { _chainElement = chainElement; var certificate = chainElement.Certificate; var dn = DistinguishedNameParser.Parse(certificate.SubjectName.Name); if (dn.ContainsKey("cn")) { commonNameLabel.Text = $"Issued To: {string.Join(", ", dn["cn"])}"; var toolTip = new ToolTip {ShowAlways = true}; toolTip.SetToolTip(commonNameLabel, commonNameLabel.Text); } else { commonNameLabel.Text = certificate.Thumbprint; } subjectAltNameLabel.Text = certificate.Extensions[KnownOids.SubjectAltNameExtension]?.Format(false) ?? "None"; thumbprintLabel.Text = certificate.Thumbprint; if (certificate.PublicKey.Oid.Value == KnownOids.EccPublicKey) { var parameterOid = OidParser.ReadFromBytes(certificate.PublicKey.EncodedParameters.RawData); algorithmLabel.Text = $"{certificate.PublicKey.Oid.FriendlyName} ({parameterOid.FriendlyName})"; switch (parameterOid.Value) { case KnownOids.EcdsaP256: keySizeLabel.Text = "256-bit"; break; case KnownOids.EcdsaP384: keySizeLabel.Text = "384-bit"; break; case KnownOids.EcdsaP521: keySizeLabel.Text = "521-bit"; break; default: keySizeLabel.Text = "Unknown"; break; } } else { algorithmLabel.Text = certificate.PublicKey.Oid.FriendlyName; keySizeLabel.Text = $"{certificate.PublicKey.Key.KeySize}-bit"; } validDatesLabel.Text = $"{certificate.NotBefore.ToString("U")} to {certificate.NotAfter.ToString("U")}"; hashAlgorithmLabel.Text = certificate.SignatureAlgorithm.FriendlyName; if (chainElement.ChainElementStatus.Length == 0 || chainElement.ChainElementStatus.All(status => status.Status == X509ChainStatusFlags.NoError)) { certStatusImage.Image = Resources.Security_Shields_Complete_and_ok_16xLG_color; certStatusToolTip.SetToolTip(certStatusImage, "Certificate is OK."); } else if (chainElement.ChainElementStatus.All(status => status.Status == X509ChainStatusFlags.OfflineRevocation || status.Status == X509ChainStatusFlags.RevocationStatusUnknown)) { certStatusImage.Image = Resources.Security_Shields_Alert_16xLG_color; certStatusToolTip.SetToolTip(certStatusImage, "Unable to check revocation status."); } else { certStatusImage.Image = Resources.Security_Shields_Critical_16xLG_color; certStatusToolTip.SetToolTip(certStatusImage, $"Errors: {string.Join(", ", chainElement.ChainElementStatus.Select(status => status.Status))}"); } var hashAlgorithm = CryptoConfig.AllowOnlyFipsAlgorithms ? (SHA256)new SHA256CryptoServiceProvider() : new SHA256Managed(); var hash = CertificateHashBuilder.BuildHashForPublicKey(chainElement.Certificate, hashAlgorithm); keyHashLabel.Text = $@"""{hash}"""; }
//this code comes from AgentDiagnostics. Need a better way to hook ino Agent. //Agent hooks TrustChainValidator and the build in DnsCertResolver, but the DnsCertResolver is strongly //linked rather than via interface for notifications and exceptions. static string Summarize(X509ChainElement chainElement, X509ChainStatusFlags problemFlags) { StringBuilder builder = new StringBuilder(); builder.Append(chainElement.Certificate.ExtractEmailNameOrName()); builder.Append(";"); foreach (X509ChainStatus status in chainElement.ChainElementStatus) { if ((status.Status & problemFlags) != 0) { builder.Append(status.Status); } } return builder.ToString(); }
public void Reset () { // note: this call doesn't Reset the X509ChainPolicy if ((status != null) && (status.Length != 0)) status = null; if (elements.Count > 0) elements.Clear (); if (user_root_store != null) { user_root_store.Close (); user_root_store = null; } if (root_store != null) { root_store.Close (); root_store = null; } if (user_ca_store != null) { user_ca_store.Close (); user_ca_store = null; } if (ca_store != null) { ca_store.Close (); ca_store = null; } roots = null; cas = null; collection = null; bce_restriction = null; working_public_key = null; }
public bool Build(X509Certificate2 certificate) { if (certificate == null) { throw new ArgumentException("certificate"); } Reset(); X509ChainStatusFlags x509ChainStatusFlags; try { x509ChainStatusFlags = BuildChainFrom(certificate); ValidateChain(x509ChainStatusFlags); } catch (CryptographicException innerException) { throw new ArgumentException("certificate", innerException); IL_0038:; } X509ChainStatusFlags x509ChainStatusFlags2 = X509ChainStatusFlags.NoError; ArrayList arrayList = new ArrayList(); X509ChainElementEnumerator enumerator = elements.GetEnumerator(); while (enumerator.MoveNext()) { X509ChainElement current = enumerator.Current; X509ChainStatus[] chainElementStatus = current.ChainElementStatus; for (int i = 0; i < chainElementStatus.Length; i++) { X509ChainStatus x509ChainStatus = chainElementStatus[i]; if ((x509ChainStatusFlags2 & x509ChainStatus.Status) != x509ChainStatus.Status) { arrayList.Add(x509ChainStatus); x509ChainStatusFlags2 |= x509ChainStatus.Status; } } } if (x509ChainStatusFlags != 0) { arrayList.Insert(0, new X509ChainStatus(x509ChainStatusFlags)); } status = (X509ChainStatus[])arrayList.ToArray(typeof(X509ChainStatus)); if (status.Length == 0 || ChainPolicy.VerificationFlags == X509VerificationFlags.AllFlags) { return(true); } bool flag = true; X509ChainStatus[] array = status; for (int j = 0; j < array.Length; j++) { X509ChainStatus x509ChainStatus2 = array[j]; switch (x509ChainStatus2.Status) { case X509ChainStatusFlags.UntrustedRoot: case X509ChainStatusFlags.PartialChain: flag &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.AllowUnknownCertificateAuthority) != X509VerificationFlags.NoFlag); break; case X509ChainStatusFlags.NotTimeValid: flag &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreNotTimeValid) != X509VerificationFlags.NoFlag); break; case X509ChainStatusFlags.NotTimeNested: flag &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreNotTimeNested) != X509VerificationFlags.NoFlag); break; case X509ChainStatusFlags.InvalidBasicConstraints: flag &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreInvalidBasicConstraints) != X509VerificationFlags.NoFlag); break; case X509ChainStatusFlags.InvalidPolicyConstraints: case X509ChainStatusFlags.NoIssuanceChainPolicy: flag &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreInvalidPolicy) != X509VerificationFlags.NoFlag); break; case X509ChainStatusFlags.InvalidNameConstraints: case X509ChainStatusFlags.HasNotSupportedNameConstraint: case X509ChainStatusFlags.HasNotPermittedNameConstraint: case X509ChainStatusFlags.HasExcludedNameConstraint: flag &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreInvalidName) != X509VerificationFlags.NoFlag); break; case X509ChainStatusFlags.InvalidExtension: flag &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreWrongUsage) != X509VerificationFlags.NoFlag); break; case X509ChainStatusFlags.CtlNotTimeValid: flag &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreCtlNotTimeValid) != X509VerificationFlags.NoFlag); break; case X509ChainStatusFlags.CtlNotValidForUsage: flag &= ((ChainPolicy.VerificationFlags & X509VerificationFlags.IgnoreWrongUsage) != X509VerificationFlags.NoFlag); break; default: flag = false; break; case X509ChainStatusFlags.CtlNotSignatureValid: break; } if (!flag) { return(false); } } return(true); }
private void ProcessCertificateExtensions (X509ChainElement element) { foreach (X509Extension ext in element.Certificate.Extensions) { if (ext.Critical) { switch (ext.Oid.Value) { case "2.5.29.15": // X509KeyUsageExtension case "2.5.29.19": // X509BasicConstraintsExtension // we processed this extension break; default: // note: Under Windows XP MS implementation seems to ignore // certificate with unknown critical extensions. element.StatusFlags |= X509ChainStatusFlags.InvalidExtension; break; } } } }
internal ChainElement(X509ChainElement source) : this() { this.Certificate = source.Certificate; this.ChainElementStatus.AddRange(source.ChainElementStatus); }