5587d6330e5cdc24d3cbb316c85ae7c7c8860a45
[platform/upstream/dotnet/runtime.git] /
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3
4 using System.Collections.Generic;
5 using System.Diagnostics;
6 using System.IO;
7 using System.Security.Cryptography.Apple;
8 using Microsoft.Win32.SafeHandles;
9
10 namespace System.Security.Cryptography.X509Certificates
11 {
12     internal sealed partial class StorePal
13     {
14         internal static partial IStorePal FromHandle(IntPtr storeHandle)
15         {
16             if (storeHandle == IntPtr.Zero)
17             {
18                 throw new ArgumentNullException(nameof(storeHandle));
19             }
20
21             var keychainHandle = new SafeKeychainHandle(storeHandle);
22             Interop.CoreFoundation.CFRetain(storeHandle);
23
24             return new AppleKeychainStore(keychainHandle, OpenFlags.MaxAllowed);
25         }
26
27         internal static partial ILoaderPal FromBlob(ReadOnlySpan<byte> rawData, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags)
28         {
29             Debug.Assert(password != null);
30
31             X509ContentType contentType = X509Certificate2.GetCertContentType(rawData);
32
33             if (contentType == X509ContentType.Pkcs12)
34             {
35                 if ((keyStorageFlags & X509KeyStorageFlags.EphemeralKeySet) == X509KeyStorageFlags.EphemeralKeySet)
36                 {
37                     throw new PlatformNotSupportedException(SR.Cryptography_X509_NoEphemeralPfx);
38                 }
39
40                 bool exportable = (keyStorageFlags & X509KeyStorageFlags.Exportable) == X509KeyStorageFlags.Exportable;
41
42                 bool persist =
43                     (keyStorageFlags & X509KeyStorageFlags.PersistKeySet) == X509KeyStorageFlags.PersistKeySet;
44
45                 SafeKeychainHandle keychain = persist
46                     ? Interop.AppleCrypto.SecKeychainCopyDefault()
47                     : Interop.AppleCrypto.CreateTemporaryKeychain();
48
49                 return ImportPkcs12(rawData, password, exportable, ephemeralSpecified: false, keychain);
50             }
51
52             SafeCFArrayHandle certs = Interop.AppleCrypto.X509ImportCollection(
53                 rawData,
54                 contentType,
55                 password,
56                 SafeTemporaryKeychainHandle.InvalidHandle,
57                 exportable: true);
58
59             return new AppleCertLoader(certs, null);
60         }
61
62         private static ILoaderPal ImportPkcs12(
63             ReadOnlySpan<byte> rawData,
64             SafePasswordHandle password,
65             bool exportable,
66             bool ephemeralSpecified,
67             SafeKeychainHandle keychain)
68         {
69             ApplePkcs12Reader reader = new ApplePkcs12Reader(rawData);
70
71             try
72             {
73                 reader.Decrypt(password, ephemeralSpecified);
74                 return new ApplePkcs12CertLoader(reader, keychain, password, exportable);
75             }
76             catch
77             {
78                 reader.Dispose();
79                 keychain.Dispose();
80                 throw;
81             }
82         }
83
84         internal static partial ILoaderPal FromFile(string fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags)
85         {
86             Debug.Assert(password != null);
87
88             byte[] fileBytes = File.ReadAllBytes(fileName);
89             return FromBlob(fileBytes, password, keyStorageFlags);
90         }
91
92         internal static partial IExportPal FromCertificate(ICertificatePalCore cert)
93         {
94             return new AppleCertificateExporter(cert);
95         }
96
97         internal static partial IExportPal LinkFromCertificateCollection(X509Certificate2Collection certificates)
98         {
99             return new AppleCertificateExporter(certificates);
100         }
101
102         internal static partial IStorePal FromSystemStore(string storeName, StoreLocation storeLocation, OpenFlags openFlags)
103         {
104             StringComparer ordinalIgnoreCase = StringComparer.OrdinalIgnoreCase;
105
106             switch (storeLocation)
107             {
108                 case StoreLocation.CurrentUser:
109                     if (ordinalIgnoreCase.Equals("My", storeName))
110                         return AppleKeychainStore.OpenDefaultKeychain(openFlags);
111                     if (ordinalIgnoreCase.Equals("Root", storeName))
112                         return AppleTrustStore.OpenStore(StoreName.Root, storeLocation, openFlags);
113                     if (ordinalIgnoreCase.Equals("Disallowed", storeName))
114                         return AppleTrustStore.OpenStore(StoreName.Disallowed, storeLocation, openFlags);
115                     return FromCustomKeychainStore(storeName, openFlags);
116
117                 case StoreLocation.LocalMachine:
118                     if (ordinalIgnoreCase.Equals("My", storeName))
119                         return AppleKeychainStore.OpenSystemSharedKeychain(openFlags);
120                     if (ordinalIgnoreCase.Equals("Root", storeName))
121                         return AppleTrustStore.OpenStore(StoreName.Root, storeLocation, openFlags);
122                     if (ordinalIgnoreCase.Equals("Disallowed", storeName))
123                         return AppleTrustStore.OpenStore(StoreName.Disallowed, storeLocation, openFlags);
124                     break;
125             }
126
127             if ((openFlags & OpenFlags.OpenExistingOnly) == OpenFlags.OpenExistingOnly)
128                 throw new CryptographicException(SR.Cryptography_X509_StoreNotFound);
129
130             string message = SR.Format(
131                 SR.Cryptography_X509_StoreCannotCreate,
132                 storeName,
133                 storeLocation);
134
135             throw new CryptographicException(message, new PlatformNotSupportedException(message));
136         }
137
138         private static IStorePal FromCustomKeychainStore(string storeName, OpenFlags openFlags)
139         {
140             string storePath;
141
142             if (!IsValidStoreName(storeName))
143                 throw new CryptographicException(SR.Format(SR.Security_InvalidValue, nameof(storeName)));
144
145             storePath = Path.Combine(
146                 Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
147                 "Library",
148                 "Keychains",
149                 storeName.ToLowerInvariant() + ".keychain");
150
151             return AppleKeychainStore.CreateOrOpenKeychain(storePath, openFlags);
152         }
153
154         private static bool IsValidStoreName(string storeName)
155         {
156             try
157             {
158                 return !string.IsNullOrWhiteSpace(storeName) && Path.GetFileName(storeName) == storeName;
159             }
160             catch (IOException)
161             {
162                 return false;
163             }
164         }
165
166         private static void ReadCollection(SafeCFArrayHandle matches, HashSet<X509Certificate2> collection)
167         {
168             if (matches.IsInvalid)
169             {
170                 return;
171             }
172
173             long count = Interop.CoreFoundation.CFArrayGetCount(matches);
174
175             for (int i = 0; i < count; i++)
176             {
177                 IntPtr handle = Interop.CoreFoundation.CFArrayGetValueAtIndex(matches, i);
178
179                 SafeSecCertificateHandle certHandle;
180                 SafeSecIdentityHandle identityHandle;
181
182                 if (Interop.AppleCrypto.X509DemuxAndRetainHandle(handle, out certHandle, out identityHandle))
183                 {
184                     X509Certificate2 cert;
185
186                     if (certHandle.IsInvalid)
187                     {
188                         certHandle.Dispose();
189                         cert = new X509Certificate2(new AppleCertificatePal(identityHandle));
190                     }
191                     else
192                     {
193                         identityHandle.Dispose();
194                         cert = new X509Certificate2(new AppleCertificatePal(certHandle));
195                     }
196
197                     if (!collection.Add(cert))
198                     {
199                         cert.Dispose();
200                     }
201                 }
202             }
203         }
204     }
205 }