private static Interop.Crypt32.PfxCertStoreFlags MapKeyStorageFlags(X509KeyStorageFlags keyStorageFlags) { if ((keyStorageFlags & X509Certificate.KeyStorageFlagsAll) != keyStorageFlags) { throw new ArgumentException(SR.Argument_InvalidFlag, nameof(keyStorageFlags)); } Interop.Crypt32.PfxCertStoreFlags pfxCertStoreFlags = 0; if ((keyStorageFlags & X509KeyStorageFlags.UserKeySet) == X509KeyStorageFlags.UserKeySet) { pfxCertStoreFlags |= Interop.Crypt32.PfxCertStoreFlags.CRYPT_USER_KEYSET; } else if ((keyStorageFlags & X509KeyStorageFlags.MachineKeySet) == X509KeyStorageFlags.MachineKeySet) { pfxCertStoreFlags |= Interop.Crypt32.PfxCertStoreFlags.CRYPT_MACHINE_KEYSET; } if ((keyStorageFlags & X509KeyStorageFlags.Exportable) == X509KeyStorageFlags.Exportable) { pfxCertStoreFlags |= Interop.Crypt32.PfxCertStoreFlags.CRYPT_EXPORTABLE; } if ((keyStorageFlags & X509KeyStorageFlags.UserProtected) == X509KeyStorageFlags.UserProtected) { pfxCertStoreFlags |= Interop.Crypt32.PfxCertStoreFlags.CRYPT_USER_PROTECTED; } // If a user is asking for an Ephemeral key they should be willing to test their code to find out // that it will no longer import into CAPI. This solves problems of legacy CSPs being // difficult to do SHA-2 RSA signatures with, simplifies the story for UWP, and reduces the // complexity of pointer interpretation. if ((keyStorageFlags & X509KeyStorageFlags.EphemeralKeySet) == X509KeyStorageFlags.EphemeralKeySet) pfxCertStoreFlags |= Interop.Crypt32.PfxCertStoreFlags.PKCS12_NO_PERSIST_KEY | Interop.Crypt32.PfxCertStoreFlags.PKCS12_ALWAYS_CNG_KSP; }
// this method maps a X509KeyStorageFlags enum to a combination of crypto API flags private static Interop.Crypt32.PfxCertStoreFlags MapKeyStorageFlags(X509KeyStorageFlags keyStorageFlags) { Interop.Crypt32.PfxCertStoreFlags dwFlags = 0; if ((keyStorageFlags & X509KeyStorageFlags.UserKeySet) == X509KeyStorageFlags.UserKeySet) { dwFlags |= Interop.Crypt32.PfxCertStoreFlags.CRYPT_USER_KEYSET; } else if ((keyStorageFlags & X509KeyStorageFlags.MachineKeySet) == X509KeyStorageFlags.MachineKeySet) { dwFlags |= Interop.Crypt32.PfxCertStoreFlags.CRYPT_MACHINE_KEYSET; } if ((keyStorageFlags & X509KeyStorageFlags.Exportable) == X509KeyStorageFlags.Exportable) { dwFlags |= Interop.Crypt32.PfxCertStoreFlags.CRYPT_EXPORTABLE; } if ((keyStorageFlags & X509KeyStorageFlags.UserProtected) == X509KeyStorageFlags.UserProtected) { dwFlags |= Interop.Crypt32.PfxCertStoreFlags.CRYPT_USER_PROTECTED; } if ((keyStorageFlags & X509KeyStorageFlags.EphemeralKeySet) == X509KeyStorageFlags.EphemeralKeySet) { dwFlags |= Interop.Crypt32.PfxCertStoreFlags.PKCS12_NO_PERSIST_KEY | Interop.Crypt32.PfxCertStoreFlags.PKCS12_ALWAYS_CNG_KSP; } return(dwFlags); }
private static ICertificatePal FromBlobOrFile(ReadOnlySpan <byte> rawData, string?fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) { Debug.Assert(!rawData.IsEmpty || fileName != null); Debug.Assert(password != null); bool loadFromFile = (fileName != null); Interop.Crypt32.PfxCertStoreFlags pfxCertStoreFlags = MapKeyStorageFlags(keyStorageFlags); bool deleteKeyContainer = false; Interop.Crypt32.CertEncodingType msgAndCertEncodingType; Interop.Crypt32.ContentType contentType; Interop.Crypt32.FormatType formatType; SafeCertStoreHandle? hCertStore = null; SafeCryptMsgHandle? hCryptMsg = null; SafeCertContextHandle?pCertContext = null; try { unsafe { fixed(byte *pRawData = rawData) { fixed(char *pFileName = fileName) { Interop.Crypt32.DATA_BLOB certBlob = new Interop.Crypt32.DATA_BLOB(new IntPtr(pRawData), (uint)(loadFromFile ? 0 : rawData.Length)); Interop.Crypt32.CertQueryObjectType objectType = loadFromFile ? Interop.Crypt32.CertQueryObjectType.CERT_QUERY_OBJECT_FILE : Interop.Crypt32.CertQueryObjectType.CERT_QUERY_OBJECT_BLOB; void *pvObject = loadFromFile ? (void *)pFileName : (void *)&certBlob; bool success = Interop.Crypt32.CryptQueryObject( objectType, pvObject, X509ExpectedContentTypeFlags, X509ExpectedFormatTypeFlags, 0, out msgAndCertEncodingType, out contentType, out formatType, out hCertStore, out hCryptMsg, out pCertContext ); if (!success) { int hr = Marshal.GetHRForLastWin32Error(); throw hr.ToCryptographicException(); } } } if (contentType == Interop.Crypt32.ContentType.CERT_QUERY_CONTENT_PKCS7_SIGNED || contentType == Interop.Crypt32.ContentType.CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED) { pCertContext = GetSignerInPKCS7Store(hCertStore, hCryptMsg); } else if (contentType == Interop.Crypt32.ContentType.CERT_QUERY_CONTENT_PFX) { if (loadFromFile) { rawData = File.ReadAllBytes(fileName !); } pCertContext = FilterPFXStore(rawData, password, pfxCertStoreFlags); // If PersistKeySet is set we don't delete the key, so that it persists. // If EphemeralKeySet is set we don't delete the key, because there's no file, so it's a wasteful call. const X509KeyStorageFlags DeleteUnless = X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.EphemeralKeySet; deleteKeyContainer = ((keyStorageFlags & DeleteUnless) == 0); } CertificatePal pal = new CertificatePal(pCertContext, deleteKeyContainer); pCertContext = null; return(pal); } } finally { if (hCertStore != null) { hCertStore.Dispose(); } if (hCryptMsg != null) { hCryptMsg.Dispose(); } if (pCertContext != null) { pCertContext.Dispose(); } } }
private static SafeCertContextHandle FilterPFXStore( ReadOnlySpan <byte> rawData, SafePasswordHandle password, Interop.Crypt32.PfxCertStoreFlags pfxCertStoreFlags) { SafeCertStoreHandle hStore; unsafe { fixed(byte *pbRawData = rawData) { Interop.Crypt32.DATA_BLOB certBlob = new Interop.Crypt32.DATA_BLOB(new IntPtr(pbRawData), (uint)rawData.Length); hStore = Interop.Crypt32.PFXImportCertStore(ref certBlob, password, pfxCertStoreFlags); if (hStore.IsInvalid) { throw Marshal.GetHRForLastWin32Error().ToCryptographicException(); } } } try { // Find the first cert with private key. If none, then simply take the very first cert. Along the way, delete the keycontainers // of any cert we don't accept. SafeCertContextHandle pCertContext = SafeCertContextHandle.InvalidHandle; SafeCertContextHandle?pEnumContext = null; while (Interop.crypt32.CertEnumCertificatesInStore(hStore, ref pEnumContext)) { if (pEnumContext.ContainsPrivateKey) { if ((!pCertContext.IsInvalid) && pCertContext.ContainsPrivateKey) { // We already found our chosen one. Free up this one's key and move on. // If this one has a persisted private key, clean up the key file. // If it was an ephemeral private key no action is required. if (pEnumContext.HasPersistedPrivateKey) { SafeCertContextHandleWithKeyContainerDeletion.DeleteKeyContainer(pEnumContext); } } else { // Found our first cert that has a private key. Set it up as our chosen one but keep iterating // as we need to free up the keys of any remaining certs. pCertContext.Dispose(); pCertContext = pEnumContext.Duplicate(); } } else { if (pCertContext.IsInvalid) { // Doesn't have a private key but hang on to it anyway in case we don't find any certs with a private key. pCertContext = pEnumContext.Duplicate(); } } } if (pCertContext.IsInvalid) { throw new CryptographicException(SR.Cryptography_Pfx_NoCertificates); } return(pCertContext); } finally { hStore.Dispose(); } }
private static StorePal FromBlobOrFile(ReadOnlySpan <byte> rawData, string?fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) { Debug.Assert(password != null); bool fromFile = fileName != null; unsafe { fixed(byte *pRawData = rawData) { fixed(char *pFileName = fileName) { Interop.Crypt32.DATA_BLOB blob = new Interop.Crypt32.DATA_BLOB(new IntPtr(pRawData), (uint)(fromFile ? 0 : rawData !.Length)); bool persistKeySet = (0 != (keyStorageFlags & X509KeyStorageFlags.PersistKeySet)); Interop.Crypt32.PfxCertStoreFlags certStoreFlags = MapKeyStorageFlags(keyStorageFlags); void *pvObject = fromFile ? (void *)pFileName : (void *)&blob; Interop.Crypt32.ContentType contentType; SafeCertStoreHandle certStore; if (!Interop.Crypt32.CryptQueryObject( fromFile ? Interop.Crypt32.CertQueryObjectType.CERT_QUERY_OBJECT_FILE : Interop.Crypt32.CertQueryObjectType.CERT_QUERY_OBJECT_BLOB, pvObject, StoreExpectedContentFlags, Interop.Crypt32.ExpectedFormatTypeFlags.CERT_QUERY_FORMAT_FLAG_ALL, 0, IntPtr.Zero, out contentType, IntPtr.Zero, out certStore, IntPtr.Zero, IntPtr.Zero )) { Exception e = Marshal.GetLastPInvokeError().ToCryptographicException(); certStore.Dispose(); throw e; } if (contentType == Interop.Crypt32.ContentType.CERT_QUERY_CONTENT_PFX) { certStore.Dispose(); if (fromFile) { rawData = File.ReadAllBytes(fileName !); } fixed(byte *pRawData2 = rawData) { Interop.Crypt32.DATA_BLOB blob2 = new Interop.Crypt32.DATA_BLOB(new IntPtr(pRawData2), (uint)rawData !.Length); certStore = Interop.Crypt32.PFXImportCertStore(ref blob2, password, certStoreFlags); if (certStore == null || certStore.IsInvalid) { Exception e = Marshal.GetLastPInvokeError().ToCryptographicException(); certStore?.Dispose(); throw e; } } if (!persistKeySet) { // // If the user did not want us to persist private keys, then we should loop through all // the certificates in the collection and set our custom CERT_CLR_DELETE_KEY_PROP_ID property // so the key container will be deleted when the cert contexts will go away. // SafeCertContextHandle?pCertContext = null; while (Interop.crypt32.CertEnumCertificatesInStore(certStore, ref pCertContext)) { Interop.Crypt32.DATA_BLOB nullBlob = new Interop.Crypt32.DATA_BLOB(IntPtr.Zero, 0); if (!Interop.Crypt32.CertSetCertificateContextProperty(pCertContext, Interop.Crypt32.CertContextPropId.CERT_CLR_DELETE_KEY_PROP_ID, Interop.Crypt32.CertSetPropertyFlags.CERT_SET_PROPERTY_INHIBIT_PERSIST_FLAG, &nullBlob)) { Exception e = Marshal.GetLastPInvokeError().ToCryptographicException(); certStore.Dispose(); throw e; } } } } return(new StorePal(certStore)); } } } }