Moving BSTR conv to native code in SecureStringToBSTR.
[mono.git] / mcs / class / referencesource / mscorlib / system / security / cryptography / x509certificates / x509utils.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6
7 //
8 // X509Utils.cs
9 //
10
11 namespace System.Security.Cryptography.X509Certificates
12 {
13     using System.Runtime.CompilerServices;
14     using System.Runtime.Versioning;
15     using System.Runtime.InteropServices;
16     using Microsoft.Win32;
17     using System.Diagnostics.Contracts;
18
19     internal static class X509Constants {
20         internal const uint CRYPT_EXPORTABLE     = 0x00000001;
21         internal const uint CRYPT_USER_PROTECTED = 0x00000002;
22         internal const uint CRYPT_MACHINE_KEYSET = 0x00000020;
23         internal const uint CRYPT_USER_KEYSET    = 0x00001000;
24
25         internal const uint CERT_QUERY_CONTENT_CERT               = 1;
26         internal const uint CERT_QUERY_CONTENT_CTL                = 2;
27         internal const uint CERT_QUERY_CONTENT_CRL                = 3;
28         internal const uint CERT_QUERY_CONTENT_SERIALIZED_STORE   = 4;
29         internal const uint CERT_QUERY_CONTENT_SERIALIZED_CERT    = 5;
30         internal const uint CERT_QUERY_CONTENT_SERIALIZED_CTL     = 6;
31         internal const uint CERT_QUERY_CONTENT_SERIALIZED_CRL     = 7;
32         internal const uint CERT_QUERY_CONTENT_PKCS7_SIGNED       = 8;
33         internal const uint CERT_QUERY_CONTENT_PKCS7_UNSIGNED     = 9;
34         internal const uint CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED = 10;
35         internal const uint CERT_QUERY_CONTENT_PKCS10             = 11;
36         internal const uint CERT_QUERY_CONTENT_PFX                = 12;
37         internal const uint CERT_QUERY_CONTENT_CERT_PAIR          = 13;
38
39         internal const uint CERT_STORE_PROV_MEMORY   = 2;
40         internal const uint CERT_STORE_PROV_SYSTEM   = 10;
41
42         // cert store flags
43         internal const uint CERT_STORE_NO_CRYPT_RELEASE_FLAG            = 0x00000001;
44         internal const uint CERT_STORE_SET_LOCALIZED_NAME_FLAG          = 0x00000002;
45         internal const uint CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG = 0x00000004;
46         internal const uint CERT_STORE_DELETE_FLAG                      = 0x00000010;
47         internal const uint CERT_STORE_SHARE_STORE_FLAG                 = 0x00000040;
48         internal const uint CERT_STORE_SHARE_CONTEXT_FLAG               = 0x00000080;
49         internal const uint CERT_STORE_MANIFOLD_FLAG                    = 0x00000100;
50         internal const uint CERT_STORE_ENUM_ARCHIVED_FLAG               = 0x00000200;
51         internal const uint CERT_STORE_UPDATE_KEYID_FLAG                = 0x00000400;
52         internal const uint CERT_STORE_BACKUP_RESTORE_FLAG              = 0x00000800;
53         internal const uint CERT_STORE_READONLY_FLAG                    = 0x00008000;
54         internal const uint CERT_STORE_OPEN_EXISTING_FLAG               = 0x00004000;
55         internal const uint CERT_STORE_CREATE_NEW_FLAG                  = 0x00002000;
56         internal const uint CERT_STORE_MAXIMUM_ALLOWED_FLAG             = 0x00001000;
57
58         internal const uint CERT_NAME_EMAIL_TYPE            = 1;
59         internal const uint CERT_NAME_RDN_TYPE              = 2;
60         internal const uint CERT_NAME_SIMPLE_DISPLAY_TYPE   = 4;
61         internal const uint CERT_NAME_FRIENDLY_DISPLAY_TYPE = 5;
62         internal const uint CERT_NAME_DNS_TYPE              = 6;
63         internal const uint CERT_NAME_URL_TYPE              = 7;
64         internal const uint CERT_NAME_UPN_TYPE              = 8;
65     }
66
67     /// <summary>
68     ///     Groups of OIDs supported by CryptFindOIDInfo
69     /// </summary>
70     internal enum OidGroup {
71         AllGroups = 0,
72         HashAlgorithm = 1,                              // CRYPT_HASH_ALG_OID_GROUP_ID
73         EncryptionAlgorithm = 2,                        // CRYPT_ENCRYPT_ALG_OID_GROUP_ID
74         PublicKeyAlgorithm = 3,                         // CRYPT_PUBKEY_ALG_OID_GROUP_ID
75         SignatureAlgorithm = 4,                         // CRYPT_SIGN_ALG_OID_GROUP_ID
76         Attribute = 5,                                  // CRYPT_RDN_ATTR_OID_GROUP_ID
77         ExtensionOrAttribute = 6,                       // CRYPT_EXT_OR_ATTR_OID_GROUP_ID
78         EnhancedKeyUsage = 7,                           // CRYPT_ENHKEY_USAGE_OID_GROUP_ID
79         Policy = 8,                                     // CRYPT_POLICY_OID_GROUP_ID
80         Template = 9,                                   // CRYPT_TEMPLATE_OID_GROUP_ID
81         KeyDerivationFunction = 10,                     // CRYPT_KDF_OID_GROUP_ID
82
83         // This can be ORed into the above groups to turn off an AD search
84         DisableSearchDS = unchecked((int)0x80000000)    // CRYPT_OID_DISABLE_SEARCH_DS_FLAG
85     }
86
87     /// <summary>
88     ///     Keys that can be used to query information on via CryptFindOIDInfo
89     /// </summary>
90     internal enum OidKeyType {
91         Oid = 1,                                        // CRYPT_OID_INFO_OID_KEY
92         Name = 2,                                       // CRYPT_OID_INFO_NAME_KEY
93         AlgorithmID = 3,                                // CRYPT_OID_INFO_ALGID_KEY
94         SignatureID = 4,                                // CRYPT_OID_INFO_SIGN_KEY
95         CngAlgorithmID = 5,                             // CRYPT_OID_INFO_CNG_ALGID_KEY
96         CngSignatureID = 6,                             // CRYPT_OID_INFO_CNG_SIGN_KEY
97     }
98 #if !MONO
99     [StructLayout(LayoutKind.Sequential)]
100     internal struct CRYPT_OID_INFO {
101         internal int cbSize;
102         [MarshalAs(UnmanagedType.LPStr)]
103         internal string pszOID;
104         [MarshalAs(UnmanagedType.LPWStr)]
105         internal string pwszName;
106         internal OidGroup dwGroupId;
107         internal int AlgId;
108         internal int cbData;
109         internal IntPtr pbData;
110     }
111
112     internal static class X509Utils
113     {
114 #if FEATURE_CRYPTO || FEATURE_LEGACYNETCFCRYPTO
115         private static bool OidGroupWillNotUseActiveDirectory(OidGroup group) {
116             // These groups will never cause an Active Directory query
117             return group == OidGroup.HashAlgorithm ||
118                    group == OidGroup.EncryptionAlgorithm ||
119                    group == OidGroup.PublicKeyAlgorithm ||
120                    group == OidGroup.SignatureAlgorithm  ||
121                    group == OidGroup.Attribute ||
122                    group == OidGroup.ExtensionOrAttribute ||
123                    group == OidGroup.KeyDerivationFunction;
124         }
125
126         [SecurityCritical]
127         private static CRYPT_OID_INFO FindOidInfo(OidKeyType keyType, string key, OidGroup group) {
128             Contract.Requires(key != null);
129
130             IntPtr rawKey = IntPtr.Zero;
131
132             RuntimeHelpers.PrepareConstrainedRegions();
133             try {
134                 if (keyType == OidKeyType.Oid) {
135                     rawKey = Marshal.StringToCoTaskMemAnsi(key);
136                 }
137                 else {
138                     rawKey = Marshal.StringToCoTaskMemUni(key);
139                 }
140
141                 // If the group alone isn't sufficient to suppress an active directory lookup, then our
142                 // first attempt should also include the suppression flag
143                 if (!OidGroupWillNotUseActiveDirectory(group)) {
144                     OidGroup localGroup = group | OidGroup.DisableSearchDS;
145                     IntPtr localOidInfo = CryptFindOIDInfo(keyType, rawKey, localGroup);
146                     if (localOidInfo != IntPtr.Zero) {
147                         return (CRYPT_OID_INFO)Marshal.PtrToStructure(localOidInfo, typeof(CRYPT_OID_INFO));
148                     }
149                 }
150
151                 // Attempt to query with a specific group, to make try to avoid an AD lookup if possible
152                 IntPtr fullOidInfo = CryptFindOIDInfo(keyType, rawKey, group);
153                 if (fullOidInfo != IntPtr.Zero) {
154                     return (CRYPT_OID_INFO)Marshal.PtrToStructure(fullOidInfo, typeof(CRYPT_OID_INFO));
155                 }
156
157                 // Finally, for compatibility with previous runtimes, if we have a group specified retry the
158                 // query with no group
159                 if (group != OidGroup.AllGroups) {
160                     IntPtr allGroupOidInfo = CryptFindOIDInfo(keyType, rawKey, OidGroup.AllGroups);
161                     if (allGroupOidInfo != IntPtr.Zero) {
162                         return (CRYPT_OID_INFO)Marshal.PtrToStructure(fullOidInfo, typeof(CRYPT_OID_INFO));
163                     }
164                 }
165
166                 // Otherwise the lookup failed
167                 return new CRYPT_OID_INFO();
168             }
169             finally {
170                 if (rawKey != IntPtr.Zero) {
171                     Marshal.FreeCoTaskMem(rawKey);
172                 }
173             }
174         }
175
176         [SecuritySafeCritical]
177         internal static int GetAlgIdFromOid(string oid, OidGroup oidGroup) {
178             Contract.Requires(oid != null);
179
180             // CAPI does not have ALGID mappings for all of the hash algorithms - see if we know the mapping
181             // first to avoid doing an AD lookup on these values
182             if (String.Equals(oid, Constants.OID_OIWSEC_SHA256, StringComparison.Ordinal)) {
183                 return Constants.CALG_SHA_256;
184             }
185             else if (String.Equals(oid, Constants.OID_OIWSEC_SHA384, StringComparison.Ordinal)) {
186                 return Constants.CALG_SHA_384;
187             }
188             else if (String.Equals(oid, Constants.OID_OIWSEC_SHA512, StringComparison.Ordinal)) {
189                 return Constants.CALG_SHA_512;
190             }
191             else {
192                 return FindOidInfo(OidKeyType.Oid, oid, oidGroup).AlgId;
193             }
194         }
195
196         [SecuritySafeCritical]
197         internal static string GetFriendlyNameFromOid(string oid, OidGroup oidGroup) {
198             Contract.Requires(oid != null);
199             CRYPT_OID_INFO oidInfo = FindOidInfo(OidKeyType.Oid, oid, oidGroup);
200             return oidInfo.pwszName;
201         }
202
203         [SecuritySafeCritical]
204         internal static string GetOidFromFriendlyName(string friendlyName, OidGroup oidGroup) {
205             Contract.Requires(friendlyName != null);
206             CRYPT_OID_INFO oidInfo = FindOidInfo(OidKeyType.Name, friendlyName, oidGroup);
207             return oidInfo.pszOID;
208         }
209
210         internal static int NameOrOidToAlgId (string oid, OidGroup oidGroup) {
211             // Default Algorithm Id is CALG_SHA1
212             if (oid == null)
213                 return Constants.CALG_SHA1;
214             string oidValue = CryptoConfig.MapNameToOID(oid, oidGroup);
215             if (oidValue == null)
216                 oidValue = oid; // we were probably passed an OID value directly
217
218             int algId = GetAlgIdFromOid(oidValue, oidGroup);
219             if (algId == 0 || algId == -1) {
220                 throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidOID"));
221             }
222             return algId;
223         }
224 #endif // FEATURE_CRYPTO
225
226         // this method maps a cert content type returned from CryptQueryObject
227         // to a value in the managed X509ContentType enum
228         internal static X509ContentType MapContentType (uint contentType) {
229             switch (contentType) {
230             case X509Constants.CERT_QUERY_CONTENT_CERT:
231                 return X509ContentType.Cert;
232 #if !FEATURE_CORECLR
233             case X509Constants.CERT_QUERY_CONTENT_SERIALIZED_STORE:
234                 return X509ContentType.SerializedStore;
235             case X509Constants.CERT_QUERY_CONTENT_SERIALIZED_CERT:
236                 return X509ContentType.SerializedCert;
237             case X509Constants.CERT_QUERY_CONTENT_PKCS7_SIGNED:
238             case X509Constants.CERT_QUERY_CONTENT_PKCS7_UNSIGNED:
239                 return X509ContentType.Pkcs7;
240             case X509Constants.CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED:
241                 return X509ContentType.Authenticode;
242 #if !FEATURE_PAL
243             case X509Constants.CERT_QUERY_CONTENT_PFX:
244                 return X509ContentType.Pkcs12;
245 #endif // !FEATURE_PAL
246 #endif // !FEATURE_CORECLR
247             default:
248                 return X509ContentType.Unknown;
249             }
250         }
251
252         // this method maps a X509KeyStorageFlags enum to a combination of crypto API flags
253         internal static uint MapKeyStorageFlags(X509KeyStorageFlags keyStorageFlags) {
254 #if FEATURE_LEGACYNETCF
255             if (CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) {
256                 if (keyStorageFlags != X509KeyStorageFlags.DefaultKeySet)
257                     throw new NotSupportedException(Environment.GetResourceString("Argument_InvalidFlag"), 
258                                                     new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "keyStorageFlags"));
259             }            
260 #endif
261             if ((keyStorageFlags & (X509KeyStorageFlags) ~0x1F) != 0)
262                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "keyStorageFlags");
263
264 #if !FEATURE_LEGACYNETCF  // CompatibilitySwitches causes problems with CCRewrite
265             Contract.EndContractBlock();
266 #endif
267
268             uint dwFlags = 0;
269 #if FEATURE_CORECLR
270             if (keyStorageFlags != X509KeyStorageFlags.DefaultKeySet) {
271                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "keyStorageFlags",
272                                             new NotSupportedException());
273             }
274 #else // FEATURE_CORECLR                        
275             if ((keyStorageFlags & X509KeyStorageFlags.UserKeySet) == X509KeyStorageFlags.UserKeySet)
276                 dwFlags |= X509Constants.CRYPT_USER_KEYSET;
277             else if ((keyStorageFlags & X509KeyStorageFlags.MachineKeySet) == X509KeyStorageFlags.MachineKeySet)
278                 dwFlags |= X509Constants.CRYPT_MACHINE_KEYSET;
279
280             if ((keyStorageFlags & X509KeyStorageFlags.Exportable) == X509KeyStorageFlags.Exportable)
281                 dwFlags |= X509Constants.CRYPT_EXPORTABLE;
282             if ((keyStorageFlags & X509KeyStorageFlags.UserProtected) == X509KeyStorageFlags.UserProtected)
283                 dwFlags |= X509Constants.CRYPT_USER_PROTECTED;
284 #endif // FEATURE_CORECLR else
285
286             return dwFlags;
287         }
288
289 #if !FEATURE_CORECLR
290         // this method creates a memory store from a certificate
291         [System.Security.SecurityCritical]  // auto-generated
292         internal static SafeCertStoreHandle ExportCertToMemoryStore (X509Certificate certificate) {
293             SafeCertStoreHandle safeCertStoreHandle = SafeCertStoreHandle.InvalidHandle;
294             X509Utils._OpenX509Store(X509Constants.CERT_STORE_PROV_MEMORY, 
295                                      X509Constants.CERT_STORE_ENUM_ARCHIVED_FLAG | X509Constants.CERT_STORE_CREATE_NEW_FLAG,
296                                      null, 
297                                      ref safeCertStoreHandle);
298             X509Utils._AddCertificateToStore(safeCertStoreHandle, certificate.CertContext);
299             return safeCertStoreHandle;
300         }
301 #endif // !FEATURE_CORECLR
302
303         [System.Security.SecurityCritical]  // auto-generated
304         internal static IntPtr PasswordToHGlobalUni (object password) {
305             if (password != null) {
306                 string pwd = password as string;
307                 if (pwd != null)
308                     return Marshal.StringToHGlobalUni(pwd);
309 #if FEATURE_X509_SECURESTRINGS
310                 SecureString securePwd = password as SecureString;
311                 if (securePwd != null)
312                     return Marshal.SecureStringToGlobalAllocUnicode(securePwd);
313 #endif // FEATURE_X509_SECURESTRINGS
314             }
315             return IntPtr.Zero;
316         }
317
318 #if FEATURE_CRYPTO || FEATURE_LEGACYNETCFCRYPTO
319         [SecurityCritical]
320         [SuppressUnmanagedCodeSecurity]
321         [DllImport("crypt32")]
322         private static extern IntPtr CryptFindOIDInfo(OidKeyType dwKeyType, IntPtr pvKey, OidGroup dwGroupId);
323 #endif // FEATURE_CRYPTO
324
325 #if !FEATURE_CORECLR
326         [System.Security.SecurityCritical]  // auto-generated
327         [ResourceExposure(ResourceScope.None)]
328         [MethodImplAttribute(MethodImplOptions.InternalCall)]
329         internal static extern void _AddCertificateToStore(SafeCertStoreHandle safeCertStoreHandle, SafeCertContextHandle safeCertContext);
330 #endif // !FEATURE_CORECLR
331         [System.Security.SecurityCritical]  // auto-generated
332         [ResourceExposure(ResourceScope.None)]
333         [MethodImplAttribute(MethodImplOptions.InternalCall)]
334         internal static extern void _DuplicateCertContext(IntPtr handle, ref SafeCertContextHandle safeCertContext);
335 #if !FEATURE_CORECLR
336         [System.Security.SecurityCritical]  // auto-generated
337         [ResourceExposure(ResourceScope.None)]
338         [MethodImplAttribute(MethodImplOptions.InternalCall)]
339         internal static extern byte[] _ExportCertificatesToBlob(SafeCertStoreHandle safeCertStoreHandle, X509ContentType contentType, IntPtr password);
340 #endif // !FEATURE_CORECLR
341         [System.Security.SecurityCritical]  // auto-generated
342         [ResourceExposure(ResourceScope.None)]
343         [MethodImplAttribute(MethodImplOptions.InternalCall)]
344         internal static extern byte[] _GetCertRawData(SafeCertContextHandle safeCertContext);
345         [System.Security.SecurityCritical]  // auto-generated
346         [ResourceExposure(ResourceScope.None)]
347         [MethodImplAttribute(MethodImplOptions.InternalCall)]
348         internal static extern void _GetDateNotAfter(SafeCertContextHandle safeCertContext, ref Win32Native.FILE_TIME fileTime);
349         [System.Security.SecurityCritical]  // auto-generated
350         [ResourceExposure(ResourceScope.None)]
351         [MethodImplAttribute(MethodImplOptions.InternalCall)]
352         internal static extern void _GetDateNotBefore(SafeCertContextHandle safeCertContext, ref Win32Native.FILE_TIME fileTime);
353         [System.Security.SecurityCritical]  // auto-generated
354         [ResourceExposure(ResourceScope.None)]
355         [MethodImplAttribute(MethodImplOptions.InternalCall)]
356         internal static extern string _GetIssuerName(SafeCertContextHandle safeCertContext, bool legacyV1Mode);
357         [System.Security.SecurityCritical]  // auto-generated
358         [ResourceExposure(ResourceScope.None)]
359         [MethodImplAttribute(MethodImplOptions.InternalCall)]
360         internal static extern string _GetPublicKeyOid(SafeCertContextHandle safeCertContext);
361         [System.Security.SecurityCritical]  // auto-generated
362         [ResourceExposure(ResourceScope.None)]
363         [MethodImplAttribute(MethodImplOptions.InternalCall)]
364         internal static extern byte[] _GetPublicKeyParameters(SafeCertContextHandle safeCertContext);
365         [System.Security.SecurityCritical]  // auto-generated
366         [ResourceExposure(ResourceScope.None)]
367         [MethodImplAttribute(MethodImplOptions.InternalCall)]
368         internal static extern byte[] _GetPublicKeyValue(SafeCertContextHandle safeCertContext);
369         [System.Security.SecurityCritical]  // auto-generated
370         [ResourceExposure(ResourceScope.None)]
371         [MethodImplAttribute(MethodImplOptions.InternalCall)]
372         internal static extern string _GetSubjectInfo(SafeCertContextHandle safeCertContext, uint displayType, bool legacyV1Mode);
373         [System.Security.SecurityCritical]  // auto-generated
374         [ResourceExposure(ResourceScope.None)]
375         [MethodImplAttribute(MethodImplOptions.InternalCall)]
376         internal static extern byte[] _GetSerialNumber(SafeCertContextHandle safeCertContext);
377         [System.Security.SecurityCritical]  // auto-generated
378         [ResourceExposure(ResourceScope.None)]
379         [MethodImplAttribute(MethodImplOptions.InternalCall)]
380         internal static extern byte[] _GetThumbprint(SafeCertContextHandle safeCertContext);
381         [System.Security.SecurityCritical]  // auto-generated
382         [ResourceExposure(ResourceScope.None)]
383         [MethodImplAttribute(MethodImplOptions.InternalCall)]
384         internal static extern void _LoadCertFromBlob(byte[] rawData, IntPtr password, uint dwFlags, bool persistKeySet, ref SafeCertContextHandle pCertCtx);
385         [System.Security.SecurityCritical]  // auto-generated
386         [ResourceExposure(ResourceScope.Machine)]
387         [MethodImplAttribute(MethodImplOptions.InternalCall)]
388         internal static extern void _LoadCertFromFile(string fileName, IntPtr password, uint dwFlags, bool persistKeySet, ref SafeCertContextHandle pCertCtx);
389 #if !FEATURE_CORECLR
390         [System.Security.SecurityCritical]  // auto-generated
391         [ResourceExposure(ResourceScope.None)]
392         [MethodImplAttribute(MethodImplOptions.InternalCall)]
393         internal static extern void _OpenX509Store(uint storeType, uint flags, string storeName, ref SafeCertStoreHandle safeCertStoreHandle);
394 #endif // !FEATURE_CORECLR
395         [System.Security.SecurityCritical]  // auto-generated
396         [ResourceExposure(ResourceScope.None)]
397         [MethodImplAttribute(MethodImplOptions.InternalCall)]
398         internal static extern uint _QueryCertBlobType(byte[] rawData);
399         [System.Security.SecurityCritical]  // auto-generated
400         [ResourceExposure(ResourceScope.Machine)]
401         [MethodImplAttribute(MethodImplOptions.InternalCall)]
402         internal static extern uint _QueryCertFileType(string fileName);
403     }
404 #endif
405 }