예제 #1
0
        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);
                        }
                    }
                }
            }
        }
예제 #2
0
        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();
                }
            }
        }
예제 #3
0
        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);
            }
        }
예제 #4
0
        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();
            }
        }
예제 #5
0
        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));
                    }
                }
            }
        }
예제 #6
0
        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);
            }
        }
예제 #7
0
        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);
                        }
                    }
                }
            }
        }
예제 #8
0
 public static partial bool CertSaveStore(SafeCertStoreHandle hCertStore, Interop.Crypt32.CertEncodingType dwMsgAndCertEncodingType, CertStoreSaveAs dwSaveAs, CertStoreSaveTo dwSaveTo, ref Interop.Crypt32.DATA_BLOB pvSaveToPara, int dwFlags);