Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.IdentityModel / System / IdentityModel / X509Util.cs
1 //-----------------------------------------------------------------------
2 // <copyright file="X509Util.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //-----------------------------------------------------------------------
6
7 namespace System.IdentityModel
8 {
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;
17
18     using Claim = System.Security.Claims.Claim;
19     using System.Runtime;
20
21     internal static class X509Util
22     {
23         internal static RSA EnsureAndGetPrivateRSAKey(X509Certificate2 certificate)
24         {
25             Fx.Assert(certificate != null, "certificate != null");
26
27             // Reject no private key
28             if (!certificate.HasPrivateKey)
29             {
30 #pragma warning suppress 56526 // no validation necessary for value.Thumbprint
31                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.ID1001, certificate.Thumbprint)));
32             }
33
34             // Check for accessibility of private key
35             RSA rsa;
36             try
37             {
38                 if (LocalAppContextSwitches.DisableCngCertificates)
39                 {
40                     rsa = certificate.PrivateKey as RSA;
41                 }
42                 else
43                 {
44                     rsa = CngLightup.GetRSAPrivateKey(certificate);
45                 }
46             }
47             catch (CryptographicException e)
48             {
49                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.ID1039, certificate.Thumbprint), e));
50             }
51
52             if (rsa == null)
53             {
54 #pragma warning suppress 56526 // no validation necessary for value.Thumbprint
55                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.ID1002, certificate.Thumbprint)));
56             }
57
58             return rsa;
59         }
60
61         internal static X509Certificate2 ResolveCertificate(StoreName storeName, StoreLocation storeLocation, X509FindType findType, object findValue)
62         {
63             X509Certificate2 certificate = null;
64
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))
68             {
69                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
70                         new InvalidOperationException(SR.GetString(SR.ID1025, storeName, storeLocation, findType, findValue)));
71             }
72
73             return certificate;
74         }
75
76         internal static bool TryResolveCertificate(StoreName storeName, StoreLocation storeLocation, X509FindType findType, object findValue, out X509Certificate2 certificate)
77         {
78             X509Store store = new X509Store(storeName, storeLocation);
79             store.Open(OpenFlags.ReadOnly);
80
81             certificate = null;
82             X509Certificate2Collection certs = null;
83             X509Certificate2Collection matches = null;
84             try
85             {
86                 certs = store.Certificates;
87                 matches = certs.Find(findType, findValue, false);
88
89                 // Throwing InvalidOperationException here, following WCF precedent. 
90                 // Might be worth introducing a more specific exception here.
91                 if (matches.Count == 1)
92                 {
93                     certificate = new X509Certificate2(matches[0]);
94                     return true;
95                 }
96             }
97             finally
98             {
99                 CryptoHelper.ResetAllCertificates(matches);
100                 CryptoHelper.ResetAllCertificates(certs);
101                 store.Close();
102             }
103
104             return false;
105         }
106
107         internal static string GetCertificateId(X509Certificate2 certificate)
108         {
109             if (certificate == null)
110             {
111                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("certificate");
112             }
113
114             string certificateId = certificate.SubjectName.Name;
115             if (string.IsNullOrEmpty(certificateId))
116             {
117                 certificateId = certificate.Thumbprint;
118             }
119
120             return certificateId;
121         }
122
123         internal static string GetCertificateIssuerName(X509Certificate2 certificate, IssuerNameRegistry issuerNameRegistry)
124         {
125             if (certificate == null)
126             {
127                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("certificate");
128             }
129
130             if (issuerNameRegistry == null)
131             {
132                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("issuerNameRegistry");
133             }
134
135             X509Chain chain = new X509Chain();
136             chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
137             chain.Build(certificate);
138             X509ChainElementCollection elements = chain.ChainElements;
139
140             string issuer = null;
141             if (elements.Count > 1)
142             {
143                 using (X509SecurityToken token = new X509SecurityToken(elements[1].Certificate))
144                 {
145                     issuer = issuerNameRegistry.GetIssuerName(token);
146                 }
147             }
148             else
149             {
150                 // This is a self-issued certificate. Use the thumbprint of the current certificate.
151                 using (X509SecurityToken token = new X509SecurityToken(certificate))
152                 {
153                     issuer = issuerNameRegistry.GetIssuerName(token);
154                 }
155             }
156
157             for (int i = 1; i < elements.Count; ++i)
158             {
159                 // Resets the state of the certificate and frees resources associated with it.
160                 elements[i].Certificate.Reset();
161             }
162
163             return issuer;
164         }
165
166         /// <summary>
167         /// Creates an X509CertificateValidator using the given parameters.
168         /// </summary>
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)
178         {
179             return new X509CertificateValidatorEx(certificateValidationMode, revocationMode, trustedStoreLocation);
180         }
181
182
183         public static IEnumerable<Claim> GetClaimsFromCertificate(X509Certificate2 certificate, string issuer)
184         {
185             if (certificate == null)
186             {
187                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("certificate");
188             }
189
190             ICollection<Claim> claimsCollection = new Collection<Claim>();
191
192             string thumbprint = Convert.ToBase64String(certificate.GetCertHash());
193             claimsCollection.Add(new Claim(ClaimTypes.Thumbprint, thumbprint, ClaimValueTypes.Base64Binary, issuer));
194
195             string value = certificate.SubjectName.Name;
196             if (!string.IsNullOrEmpty(value))
197             {
198                 claimsCollection.Add(new Claim(ClaimTypes.X500DistinguishedName, value, ClaimValueTypes.String, issuer));
199             }
200
201             value = certificate.GetNameInfo(X509NameType.DnsName, false);
202             if (!string.IsNullOrEmpty(value))
203             {
204                 claimsCollection.Add(new Claim(ClaimTypes.Dns, value, ClaimValueTypes.String, issuer));
205             }
206
207             value = certificate.GetNameInfo(X509NameType.SimpleName, false);
208             if (!string.IsNullOrEmpty(value))
209             {
210                 claimsCollection.Add(new Claim(ClaimTypes.Name, value, ClaimValueTypes.String, issuer));
211             }
212
213             value = certificate.GetNameInfo(X509NameType.EmailName, false);
214             if (!string.IsNullOrEmpty(value))
215             {
216                 claimsCollection.Add(new Claim(ClaimTypes.Email, value, ClaimValueTypes.String, issuer));
217             }
218
219             value = certificate.GetNameInfo(X509NameType.UpnName, false);
220             if (!string.IsNullOrEmpty(value))
221             {
222                 claimsCollection.Add(new Claim(ClaimTypes.Upn, value, ClaimValueTypes.String, issuer));
223             }
224
225             value = certificate.GetNameInfo(X509NameType.UrlName, false);
226             if (!string.IsNullOrEmpty(value))
227             {
228                 claimsCollection.Add(new Claim(ClaimTypes.Uri, value, ClaimValueTypes.String, issuer));
229             }
230
231             RSA rsa;
232             if (LocalAppContextSwitches.DisableCngCertificates)
233             {
234                 rsa = certificate.PublicKey.Key as RSA;
235             }
236             else
237             {
238                 rsa = CngLightup.GetRSAPublicKey(certificate);
239             }
240             if (rsa != null)
241             {
242                 claimsCollection.Add(new Claim(ClaimTypes.Rsa, rsa.ToXmlString(false), ClaimValueTypes.RsaKeyValue, issuer));
243             }
244
245             DSA dsa;
246             if (LocalAppContextSwitches.DisableCngCertificates)
247             {
248                 dsa = certificate.PublicKey.Key as DSA;
249             }
250             else
251             {
252                 dsa = CngLightup.GetDSAPublicKey(certificate);
253             }
254             if (dsa != null)
255             {
256                 claimsCollection.Add(new Claim(ClaimTypes.Dsa, dsa.ToXmlString(false), ClaimValueTypes.DsaKeyValue, issuer));
257             }
258
259             value = certificate.SerialNumber;
260             if (!string.IsNullOrEmpty(value))
261             {
262                 claimsCollection.Add(new Claim(ClaimTypes.SerialNumber, value, ClaimValueTypes.String, issuer));
263             }
264
265             return claimsCollection;
266         }
267     }
268 }