1 //-----------------------------------------------------------------------
2 // <copyright file="X509Util.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //-----------------------------------------------------------------------
7 namespace System.IdentityModel
9 using System.Collections.Generic;
10 using System.Collections.ObjectModel;
11 using System.Diagnostics;
12 using System.IdentityModel.Selectors;
13 using System.IdentityModel.Tokens;
14 using System.Security.Claims;
15 using System.Security.Cryptography;
16 using System.Security.Cryptography.X509Certificates;
18 using Claim = System.Security.Claims.Claim;
21 internal static class X509Util
23 internal static RSA EnsureAndGetPrivateRSAKey(X509Certificate2 certificate)
25 Fx.Assert(certificate != null, "certificate != null");
27 // Reject no private key
28 if (!certificate.HasPrivateKey)
30 #pragma warning suppress 56526 // no validation necessary for value.Thumbprint
31 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.ID1001, certificate.Thumbprint)));
34 // Check for accessibility of private key
38 if (LocalAppContextSwitches.DisableCngCertificates)
40 rsa = certificate.PrivateKey as RSA;
44 rsa = CngLightup.GetRSAPrivateKey(certificate);
47 catch (CryptographicException e)
49 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.ID1039, certificate.Thumbprint), e));
54 #pragma warning suppress 56526 // no validation necessary for value.Thumbprint
55 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.ID1002, certificate.Thumbprint)));
61 internal static X509Certificate2 ResolveCertificate(StoreName storeName, StoreLocation storeLocation, X509FindType findType, object findValue)
63 X509Certificate2 certificate = null;
65 // Throwing InvalidOperationException here, following WCF precedent.
66 // Might be worth introducing a more specific exception here.
67 if (!TryResolveCertificate(storeName, storeLocation, findType, findValue, out certificate))
69 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
70 new InvalidOperationException(SR.GetString(SR.ID1025, storeName, storeLocation, findType, findValue)));
76 internal static bool TryResolveCertificate(StoreName storeName, StoreLocation storeLocation, X509FindType findType, object findValue, out X509Certificate2 certificate)
78 X509Store store = new X509Store(storeName, storeLocation);
79 store.Open(OpenFlags.ReadOnly);
82 X509Certificate2Collection certs = null;
83 X509Certificate2Collection matches = null;
86 certs = store.Certificates;
87 matches = certs.Find(findType, findValue, false);
89 // Throwing InvalidOperationException here, following WCF precedent.
90 // Might be worth introducing a more specific exception here.
91 if (matches.Count == 1)
93 certificate = new X509Certificate2(matches[0]);
99 CryptoHelper.ResetAllCertificates(matches);
100 CryptoHelper.ResetAllCertificates(certs);
107 internal static string GetCertificateId(X509Certificate2 certificate)
109 if (certificate == null)
111 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("certificate");
114 string certificateId = certificate.SubjectName.Name;
115 if (string.IsNullOrEmpty(certificateId))
117 certificateId = certificate.Thumbprint;
120 return certificateId;
123 internal static string GetCertificateIssuerName(X509Certificate2 certificate, IssuerNameRegistry issuerNameRegistry)
125 if (certificate == null)
127 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("certificate");
130 if (issuerNameRegistry == null)
132 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("issuerNameRegistry");
135 X509Chain chain = new X509Chain();
136 chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
137 chain.Build(certificate);
138 X509ChainElementCollection elements = chain.ChainElements;
140 string issuer = null;
141 if (elements.Count > 1)
143 using (X509SecurityToken token = new X509SecurityToken(elements[1].Certificate))
145 issuer = issuerNameRegistry.GetIssuerName(token);
150 // This is a self-issued certificate. Use the thumbprint of the current certificate.
151 using (X509SecurityToken token = new X509SecurityToken(certificate))
153 issuer = issuerNameRegistry.GetIssuerName(token);
157 for (int i = 1; i < elements.Count; ++i)
159 // Resets the state of the certificate and frees resources associated with it.
160 elements[i].Certificate.Reset();
167 /// Creates an X509CertificateValidator using the given parameters.
169 /// <param name="certificateValidationMode">The certificate validation mode to use.</param>
170 /// <param name="revocationMode">The revocation mode to use.</param>
171 /// <param name="trustedStoreLocation">The store to use.</param>
172 /// <returns>The X509CertificateValidator.</returns>
173 /// <remarks>Due to a WCF bug, X509CertificateValidatorEx must be used rather than WCF's validators directly</remarks>
174 internal static X509CertificateValidator CreateCertificateValidator(
175 System.ServiceModel.Security.X509CertificateValidationMode certificateValidationMode,
176 X509RevocationMode revocationMode,
177 StoreLocation trustedStoreLocation)
179 return new X509CertificateValidatorEx(certificateValidationMode, revocationMode, trustedStoreLocation);
183 public static IEnumerable<Claim> GetClaimsFromCertificate(X509Certificate2 certificate, string issuer)
185 if (certificate == null)
187 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("certificate");
190 ICollection<Claim> claimsCollection = new Collection<Claim>();
192 string thumbprint = Convert.ToBase64String(certificate.GetCertHash());
193 claimsCollection.Add(new Claim(ClaimTypes.Thumbprint, thumbprint, ClaimValueTypes.Base64Binary, issuer));
195 string value = certificate.SubjectName.Name;
196 if (!string.IsNullOrEmpty(value))
198 claimsCollection.Add(new Claim(ClaimTypes.X500DistinguishedName, value, ClaimValueTypes.String, issuer));
201 value = certificate.GetNameInfo(X509NameType.DnsName, false);
202 if (!string.IsNullOrEmpty(value))
204 claimsCollection.Add(new Claim(ClaimTypes.Dns, value, ClaimValueTypes.String, issuer));
207 value = certificate.GetNameInfo(X509NameType.SimpleName, false);
208 if (!string.IsNullOrEmpty(value))
210 claimsCollection.Add(new Claim(ClaimTypes.Name, value, ClaimValueTypes.String, issuer));
213 value = certificate.GetNameInfo(X509NameType.EmailName, false);
214 if (!string.IsNullOrEmpty(value))
216 claimsCollection.Add(new Claim(ClaimTypes.Email, value, ClaimValueTypes.String, issuer));
219 value = certificate.GetNameInfo(X509NameType.UpnName, false);
220 if (!string.IsNullOrEmpty(value))
222 claimsCollection.Add(new Claim(ClaimTypes.Upn, value, ClaimValueTypes.String, issuer));
225 value = certificate.GetNameInfo(X509NameType.UrlName, false);
226 if (!string.IsNullOrEmpty(value))
228 claimsCollection.Add(new Claim(ClaimTypes.Uri, value, ClaimValueTypes.String, issuer));
232 if (LocalAppContextSwitches.DisableCngCertificates)
234 rsa = certificate.PublicKey.Key as RSA;
238 rsa = CngLightup.GetRSAPublicKey(certificate);
242 claimsCollection.Add(new Claim(ClaimTypes.Rsa, rsa.ToXmlString(false), ClaimValueTypes.RsaKeyValue, issuer));
246 if (LocalAppContextSwitches.DisableCngCertificates)
248 dsa = certificate.PublicKey.Key as DSA;
252 dsa = CngLightup.GetDSAPublicKey(certificate);
256 claimsCollection.Add(new Claim(ClaimTypes.Dsa, dsa.ToXmlString(false), ClaimValueTypes.DsaKeyValue, issuer));
259 value = certificate.SerialNumber;
260 if (!string.IsNullOrEmpty(value))
262 claimsCollection.Add(new Claim(ClaimTypes.SerialNumber, value, ClaimValueTypes.String, issuer));
265 return claimsCollection;