private static byte[] ProtectOrUnprotect(byte[] inputData, byte[] optionalEntropy, DataProtectionScope scope, bool protect) { unsafe { fixed (byte* pInputData = inputData, pOptionalEntropy = optionalEntropy) { DATA_BLOB userDataBlob = new DATA_BLOB((IntPtr)pInputData, (uint)(inputData.Length)); DATA_BLOB optionalEntropyBlob = default(DATA_BLOB); if (optionalEntropy != null) { optionalEntropyBlob = new DATA_BLOB((IntPtr)pOptionalEntropy, (uint)(optionalEntropy.Length)); } // For desktop compat, we ignore unknown bits in the "scope" value rather than throwing. CryptProtectDataFlags flags = CryptProtectDataFlags.CRYPTPROTECT_UI_FORBIDDEN; if (scope == DataProtectionScope.LocalMachine) { flags |= CryptProtectDataFlags.CRYPTPROTECT_LOCAL_MACHINE; } DATA_BLOB outputBlob = default(DATA_BLOB); try { bool success = protect ? Interop.Crypt32.CryptProtectData(ref userDataBlob, null, ref optionalEntropyBlob, IntPtr.Zero, IntPtr.Zero, flags, out outputBlob) : Interop.Crypt32.CryptUnprotectData(ref userDataBlob, IntPtr.Zero, ref optionalEntropyBlob, IntPtr.Zero, IntPtr.Zero, flags, out outputBlob); if (!success) { int lastWin32Error = Marshal.GetLastWin32Error(); if (protect && ErrorMayBeCausedByUnloadedProfile(lastWin32Error)) throw new CryptographicException(SR.Cryptography_DpApi_ProfileMayNotBeLoaded); else throw lastWin32Error.ToCryptographicException(); } // In some cases, the API would fail due to OOM but simply return a null pointer. if (outputBlob.pbData == IntPtr.Zero) throw new OutOfMemoryException(); int length = (int)(outputBlob.cbData); byte[] outputBytes = new byte[length]; Marshal.Copy(outputBlob.pbData, outputBytes, 0, length); return outputBytes; } finally { if (outputBlob.pbData != IntPtr.Zero) { int length = (int)(outputBlob.cbData); byte* pOutputData = (byte*)(outputBlob.pbData); for (int i = 0; i < length; i++) { pOutputData[i] = 0; } Marshal.FreeHGlobal(outputBlob.pbData); } } } } }
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(); } } }
public static void TestHandleCtor() { IntPtr pCertContext = IntPtr.Zero; byte[] rawData = TestData.MsCertificate; unsafe { fixed(byte *pRawData = rawData) { Interop.Crypt32.DATA_BLOB certBlob = new Interop.Crypt32.DATA_BLOB(new IntPtr(pRawData), (uint)rawData.Length); bool success = Interop.Crypt32.CryptQueryObject( Interop.Crypt32.CertQueryObjectType.CERT_QUERY_OBJECT_BLOB, &certBlob, Interop.Crypt32.ExpectedContentTypeFlags.CERT_QUERY_CONTENT_FLAG_CERT, Interop.Crypt32.ExpectedFormatTypeFlags.CERT_QUERY_FORMAT_FLAG_BINARY, 0, IntPtr.Zero, out Interop.Crypt32.ContentType contentType, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out pCertContext ); if (!success) { int hr = Marshal.GetHRForLastWin32Error(); throw new CryptographicException(hr); } } } // Now, create an X509Certificate around our handle. using (X509Certificate2 c = new X509Certificate2(pCertContext)) { // And release our ref-count on the handle. X509Certificate better be maintaining its own. Interop.Crypt32.CertFreeCertificateContext(pCertContext); // Now, test various properties to make sure the X509Certificate actually wraps our CERT_CONTEXT. IntPtr h = c.Handle; Assert.Equal(pCertContext, h); pCertContext = IntPtr.Zero; Assert.Equal(rawData, c.GetRawCertData()); Assert.Equal(rawData, c.GetRawCertDataString().HexToByteArray()); string issuer = c.Issuer; Assert.Equal( "CN=Microsoft Code Signing PCA, O=Microsoft Corporation, L=Redmond, S=Washington, C=US", issuer); byte[] expectedPublicKey = ( "3082010a0282010100e8af5ca2200df8287cbc057b7fadeeeb76ac28533f3adb" + "407db38e33e6573fa551153454a5cfb48ba93fa837e12d50ed35164eef4d7adb" + "137688b02cf0595ca9ebe1d72975e41b85279bf3f82d9e41362b0b40fbbe3bba" + "b95c759316524bca33c537b0f3eb7ea8f541155c08651d2137f02cba220b10b1" + "109d772285847c4fb91b90b0f5a3fe8bf40c9a4ea0f5c90a21e2aae3013647fd" + "2f826a8103f5a935dc94579dfb4bd40e82db388f12fee3d67a748864e162c425" + "2e2aae9d181f0e1eb6c2af24b40e50bcde1c935c49a679b5b6dbcef9707b2801" + "84b82a29cfbfa90505e1e00f714dfdad5c238329ebc7c54ac8e82784d37ec643" + "0b950005b14f6571c50203010001").HexToByteArray(); byte[] publicKey = c.GetPublicKey(); Assert.Equal(expectedPublicKey, publicKey); byte[] expectedThumbPrint = "108e2ba23632620c427c570b6d9db51ac31387fe".HexToByteArray(); byte[] thumbPrint = c.GetCertHash(); Assert.Equal(expectedThumbPrint, thumbPrint); } }
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)); } } } }
public byte[]? Export(X509ContentType contentType, SafePasswordHandle password) { Debug.Assert(password != null); switch (contentType) { case X509ContentType.Cert: { SafeCertContextHandle?pCertContext = null; if (!Interop.crypt32.CertEnumCertificatesInStore(_certStore, ref pCertContext)) { return(null); } try { unsafe { byte[] rawData = new byte[pCertContext.CertContext->cbCertEncoded]; Marshal.Copy((IntPtr)(pCertContext.CertContext->pbCertEncoded), rawData, 0, rawData.Length); GC.KeepAlive(pCertContext); return(rawData); } } finally { pCertContext.Dispose(); } } case X509ContentType.SerializedCert: { SafeCertContextHandle?pCertContext = null; if (!Interop.crypt32.CertEnumCertificatesInStore(_certStore, ref pCertContext)) { return(null); } try { int cbEncoded = 0; if (!Interop.Crypt32.CertSerializeCertificateStoreElement(pCertContext, 0, null, ref cbEncoded)) { throw Marshal.GetHRForLastWin32Error().ToCryptographicException(); } byte[] pbEncoded = new byte[cbEncoded]; if (!Interop.Crypt32.CertSerializeCertificateStoreElement(pCertContext, 0, pbEncoded, ref cbEncoded)) { throw Marshal.GetHRForLastWin32Error().ToCryptographicException(); } return(pbEncoded); } finally { pCertContext.Dispose(); } } case X509ContentType.Pkcs12: { unsafe { Interop.Crypt32.DATA_BLOB dataBlob = new Interop.Crypt32.DATA_BLOB(IntPtr.Zero, 0); if (!Interop.Crypt32.PFXExportCertStore(_certStore, ref dataBlob, password, Interop.Crypt32.PFXExportFlags.EXPORT_PRIVATE_KEYS | Interop.Crypt32.PFXExportFlags.REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY)) { throw Marshal.GetHRForLastWin32Error().ToCryptographicException(); } byte[] pbEncoded = new byte[dataBlob.cbData]; fixed(byte *ppbEncoded = pbEncoded) { dataBlob.pbData = new IntPtr(ppbEncoded); if (!Interop.Crypt32.PFXExportCertStore(_certStore, ref dataBlob, password, Interop.Crypt32.PFXExportFlags.EXPORT_PRIVATE_KEYS | Interop.Crypt32.PFXExportFlags.REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY)) { throw Marshal.GetHRForLastWin32Error().ToCryptographicException(); } } return(pbEncoded); } } case X509ContentType.SerializedStore: return(SaveToMemoryStore(Interop.Crypt32.CertStoreSaveAs.CERT_STORE_SAVE_AS_STORE)); case X509ContentType.Pkcs7: return(SaveToMemoryStore(Interop.Crypt32.CertStoreSaveAs.CERT_STORE_SAVE_AS_PKCS7)); default: throw new CryptographicException(SR.Cryptography_X509_InvalidContentType); } }
private static byte[] ProtectOrUnprotect(byte[] inputData, byte[]?optionalEntropy, DataProtectionScope scope, bool protect) { unsafe { // The Win32 API will reject pbData == nullptr, and the fixed statement // maps empty arrays to nullptr... so when the input is empty use the address of a // different array, but still assign cbData to 0. byte[] relevantData = inputData.Length == 0 ? s_nonEmpty : inputData; fixed(byte *pInputData = relevantData, pOptionalEntropy = optionalEntropy) { DATA_BLOB userDataBlob = new DATA_BLOB((IntPtr)pInputData, (uint)(inputData.Length)); DATA_BLOB optionalEntropyBlob = default(DATA_BLOB); if (optionalEntropy != null) { optionalEntropyBlob = new DATA_BLOB((IntPtr)pOptionalEntropy, (uint)(optionalEntropy.Length)); } // For .NET Framework compat, we ignore unknown bits in the "scope" value rather than throwing. CryptProtectDataFlags flags = CryptProtectDataFlags.CRYPTPROTECT_UI_FORBIDDEN; if (scope == DataProtectionScope.LocalMachine) { flags |= CryptProtectDataFlags.CRYPTPROTECT_LOCAL_MACHINE; } DATA_BLOB outputBlob = default(DATA_BLOB); try { bool success = protect ? Interop.Crypt32.CryptProtectData(in userDataBlob, null, ref optionalEntropyBlob, IntPtr.Zero, IntPtr.Zero, flags, out outputBlob) : Interop.Crypt32.CryptUnprotectData(in userDataBlob, IntPtr.Zero, ref optionalEntropyBlob, IntPtr.Zero, IntPtr.Zero, flags, out outputBlob); if (!success) { int lastWin32Error = Marshal.GetLastWin32Error(); if (protect && ErrorMayBeCausedByUnloadedProfile(lastWin32Error)) { throw new CryptographicException(SR.Cryptography_DpApi_ProfileMayNotBeLoaded); } else { throw lastWin32Error.ToCryptographicException(); } } // In some cases, the API would fail due to OOM but simply return a null pointer. if (outputBlob.pbData == IntPtr.Zero) { throw new OutOfMemoryException(); } int length = (int)(outputBlob.cbData); byte[] outputBytes = new byte[length]; Marshal.Copy(outputBlob.pbData, outputBytes, 0, length); return(outputBytes); } finally { if (outputBlob.pbData != IntPtr.Zero) { int length = (int)(outputBlob.cbData); byte *pOutputData = (byte *)(outputBlob.pbData); for (int i = 0; i < length; i++) { pOutputData[i] = 0; } Marshal.FreeHGlobal(outputBlob.pbData); } } } } }
public static partial bool CertSaveStore(SafeCertStoreHandle hCertStore, Interop.Crypt32.CertEncodingType dwMsgAndCertEncodingType, CertStoreSaveAs dwSaveAs, CertStoreSaveTo dwSaveTo, ref Interop.Crypt32.DATA_BLOB pvSaveToPara, int dwFlags);