private bool ProcessCrlEntryExtensions (MX.X509Crl.X509CrlEntry entry) { foreach (MX.X509Extension ext in entry.Extensions) { if (ext.Critical) { switch (ext.Oid) { case "2.5.29.21": // cRLReason // we processed/know about this extension break; default: return false; } } } return true; }
static MX.X509Crl CheckCrls (string subject, string ski, MX.X509Store store) { if (store == null) return null; var crls = store.Crls; foreach (MX.X509Crl crl in crls) { if (crl.IssuerName == subject && (ski.Length == 0 || ski == GetAuthorityKeyIdentifier (crl))) return crl; } return null; // No CRL found }
private bool ProcessCrlExtensions (MX.X509Crl crl) { foreach (MX.X509Extension ext in crl.Extensions) { if (ext.Critical) { switch (ext.Oid) { case "2.5.29.20": // cRLNumber case "2.5.29.35": // authorityKeyIdentifier // we processed/know about this extension break; default: return false; } } } return true; }
// but anyway System.dll v2 doesn't expose CRL in any way so... static string GetAuthorityKeyIdentifier (MX.X509Crl crl) { return GetAuthorityKeyIdentifier (crl.Extensions ["2.5.29.35"]); }
static string GetAuthorityKeyIdentifier (MX.X509Extension ext) { if (ext == null) return String.Empty; MX.Extensions.AuthorityKeyIdentifierExtension aki = new MX.Extensions.AuthorityKeyIdentifierExtension (ext); byte[] id = aki.Identifier; if (id == null) return String.Empty; StringBuilder sb = new StringBuilder (); foreach (byte b in id) sb.Append (b.ToString ("X02")); return sb.ToString (); }
// Used when the obsolete ICertificatePolicy is set to DefaultCertificatePolicy // and the new ServerCertificateValidationCallback is not null internal ValidationResult ValidateChain (MSX.X509CertificateCollection certs) { // user_denied is true if the user callback is called and returns false bool user_denied = false; if (certs == null || certs.Count == 0) return null; ICertificatePolicy policy = ServicePointManager.CertificatePolicy; RemoteCertificateValidationCallback cb = ServicePointManager.ServerCertificateValidationCallback; X509Certificate2 leaf = new X509Certificate2 (certs [0].RawData); int status11 = 0; // Error code passed to the obsolete ICertificatePolicy callback SslPolicyErrors errors = 0; X509Chain chain = null; bool result = false; #if MONOTOUCH // The X509Chain is not really usable with MonoTouch (since the decision is not based on this data) // However if someone wants to override the results (good or bad) from iOS then they will want all // the certificates that the server provided (which generally does not include the root) so, only // if there's a user callback, we'll create the X509Chain but won't build it // ref: https://bugzilla.xamarin.com/show_bug.cgi?id=7245 if (cb != null) { #endif chain = new X509Chain (); chain.ChainPolicy = new X509ChainPolicy (); #if !MONOTOUCH chain.ChainPolicy.RevocationMode = revocation_mode; #endif for (int i = 1; i < certs.Count; i++) { X509Certificate2 c2 = new X509Certificate2 (certs [i].RawData); chain.ChainPolicy.ExtraStore.Add (c2); } #if MONOTOUCH } #else try { if (!chain.Build (leaf)) errors |= GetErrorsFromChain (chain); } catch (Exception e) { Console.Error.WriteLine ("ERROR building certificate chain: {0}", e); Console.Error.WriteLine ("Please, report this problem to the Mono team"); errors |= SslPolicyErrors.RemoteCertificateChainErrors; } // for OSX and iOS we're using the native API to check for the SSL server policy and host names if (!is_macosx) { if (!CheckCertificateUsage (leaf)) { errors |= SslPolicyErrors.RemoteCertificateChainErrors; status11 = -2146762490; //CERT_E_PURPOSE 0x800B0106 } if (!CheckServerIdentity (certs [0], Host)) { errors |= SslPolicyErrors.RemoteCertificateNameMismatch; status11 = -2146762481; // CERT_E_CN_NO_MATCH 0x800B010F } } else { #endif // Attempt to use OSX certificates // Ideally we should return the SecTrustResult OSX509Certificates.SecTrustResult trustResult = OSX509Certificates.SecTrustResult.Deny; try { trustResult = OSX509Certificates.TrustEvaluateSsl (certs, Host); // We could use the other values of trustResult to pass this extra information // to the .NET 2 callback for values like SecTrustResult.Confirm result = (trustResult == OSX509Certificates.SecTrustResult.Proceed || trustResult == OSX509Certificates.SecTrustResult.Unspecified); } catch { // Ignore } if (result) { // TrustEvaluateSsl was successful so there's no trust error // IOW we discard our own chain (since we trust OSX one instead) errors = 0; } else { // callback and DefaultCertificatePolicy needs this since 'result' is not specified status11 = (int) trustResult; errors |= SslPolicyErrors.RemoteCertificateChainErrors; } #if !MONOTOUCH } #endif #if MONODROID && SECURITY_DEP result = AndroidPlatform.TrustEvaluateSsl (certs, sender, leaf, chain, errors); if (result) { // chain.Build() + GetErrorsFromChain() (above) will ALWAYS fail on // Android (there are no mozroots or preinstalled root certificates), // thus `errors` will ALWAYS have RemoteCertificateChainErrors. // Android just verified the chain; clear RemoteCertificateChainErrors. errors &= ~SslPolicyErrors.RemoteCertificateChainErrors; } #endif if (policy != null && (!(policy is DefaultCertificatePolicy) || cb == null)) { ServicePoint sp = null; HttpWebRequest req = sender as HttpWebRequest; if (req != null) sp = req.ServicePointNoLock; if (status11 == 0 && errors != 0) status11 = GetStatusFromChain (chain); // pre 2.0 callback result = policy.CheckValidationResult (sp, leaf, req, status11); user_denied = !result && !(policy is DefaultCertificatePolicy); } // If there's a 2.0 callback, it takes precedence if (cb != null) { result = cb (sender, leaf, chain, errors); user_denied = !result; } return new ValidationResult (result, user_denied, status11); }
// RFC2818 - HTTP Over TLS, Section 3.1 // http://www.ietf.org/rfc/rfc2818.txt // // 1. if present MUST use subjectAltName dNSName as identity // 1.1. if multiples entries a match of any one is acceptable // 1.2. wildcard * is acceptable // 2. URI may be an IP address -> subjectAltName.iPAddress // 2.1. exact match is required // 3. Use of the most specific Common Name (CN=) in the Subject // 3.1 Existing practice but DEPRECATED static bool CheckServerIdentity (MSX.X509Certificate cert, string targetHost) { try { MSX.X509Extension ext = cert.Extensions ["2.5.29.17"]; // 1. subjectAltName if (ext != null) { SubjectAltNameExtension subjectAltName = new SubjectAltNameExtension (ext); // 1.1 - multiple dNSName foreach (string dns in subjectAltName.DNSNames) { // 1.2 TODO - wildcard support if (Match (targetHost, dns)) return true; } // 2. ipAddress foreach (string ip in subjectAltName.IPAddresses) { // 2.1. Exact match required if (ip == targetHost) return true; } } // 3. Common Name (CN=) return CheckDomainName (cert.SubjectName, targetHost); } catch (Exception e) { Console.Error.WriteLine ("ERROR processing certificate: {0}", e); Console.Error.WriteLine ("Please, report this problem to the Mono team"); return false; } }
internal ValidationResult ValidateChain (string host, MSX.X509CertificateCollection certs) { try { var certs2 = Convert (certs); var result = ValidateChain (host, certs2, 0); if (tlsStream != null) tlsStream.CertificateValidationFailed = result == null || !result.Trusted || result.UserDenied; return result; } catch { if (tlsStream != null) tlsStream.CertificateValidationFailed = true; throw; } }
static X509Certificate2Collection Convert (MSX.X509CertificateCollection certificates) { if (certificates == null) return null; var certs2 = new X509Certificate2Collection (); for (int i = 0; i < certificates.Count; i++) certs2.Add (new X509Certificate2 (certificates [i].RawData)); return certs2; }
internal static bool TrustEvaluateSsl (MSX.X509CertificateCollection collection, object sender, X509Certificate2 certificate, X509Chain chain, SslPolicyErrors errors) { if (trustEvaluateSsl2 != null) return trustEvaluateSsl2 (collection, sender, certificate, chain, errors); return trustEvaluateSsl (collection); }
public static SecTrustResult TrustEvaluateSsl (MSX.X509CertificateCollection certificates, string host) { if (certificates == null) return SecTrustResult.Deny; try { return _TrustEvaluateSsl (certificates, host); } catch { return SecTrustResult.Deny; } }
static SecTrustResult _TrustEvaluateSsl (MSX.X509CertificateCollection certificates, string hostName) { int certCount = certificates.Count; IntPtr [] cfDataPtrs = new IntPtr [certCount]; IntPtr [] secCerts = new IntPtr [certCount]; IntPtr certArray = IntPtr.Zero; IntPtr sslsecpolicy = IntPtr.Zero; IntPtr host = IntPtr.Zero; IntPtr sectrust = IntPtr.Zero; SecTrustResult result = SecTrustResult.Deny; try { for (int i = 0; i < certCount; i++) cfDataPtrs [i] = MakeCFData (certificates [i].RawData); for (int i = 0; i < certCount; i++){ secCerts [i] = SecCertificateCreateWithData (IntPtr.Zero, cfDataPtrs [i]); if (secCerts [i] == IntPtr.Zero) return SecTrustResult.Deny; } certArray = FromIntPtrs (secCerts); host = CFStringCreateWithCharacters (IntPtr.Zero, hostName, hostName.Length); sslsecpolicy = SecPolicyCreateSSL (true, host); int code = SecTrustCreateWithCertificates (certArray, sslsecpolicy, out sectrust); if (code == 0) code = SecTrustEvaluate (sectrust, out result); return result; } finally { for (int i = 0; i < certCount; i++) if (cfDataPtrs [i] != IntPtr.Zero) CFRelease (cfDataPtrs [i]); if (certArray != IntPtr.Zero) CFRelease (certArray); for (int i = 0; i < certCount; i++) if (secCerts [i] != IntPtr.Zero) CFRelease (secCerts [i]); if (sslsecpolicy != IntPtr.Zero) CFRelease (sslsecpolicy); if (host != IntPtr.Zero) CFRelease (host); if (sectrust != IntPtr.Zero) CFRelease (sectrust); } }