// end: gost private unsafe ICertificatePal CopyWithPersistedCapiKey(CspKeyContainerInfo keyContainerInfo) { if (string.IsNullOrEmpty(keyContainerInfo.KeyContainerName)) { return(null); } // Make a new pal from bytes. CertificatePal pal = (CertificatePal)FromBlob(RawData, SafePasswordHandle.InvalidHandle, X509KeyStorageFlags.PersistKeySet); CRYPT_KEY_PROV_INFO keyProvInfo = new CRYPT_KEY_PROV_INFO(); fixed(byte *keyName = Encoding.UTF32.GetBytes(keyContainerInfo.KeyContainerName)) fixed(byte *provName = Encoding.UTF32.GetBytes(keyContainerInfo.ProviderName)) { keyProvInfo.pwszContainerName = (char *)keyName; keyProvInfo.pwszProvName = (char *)provName; keyProvInfo.dwFlags = keyContainerInfo.MachineKeyStore ? CryptAcquireContextFlags.CRYPT_MACHINE_KEYSET : 0; keyProvInfo.dwProvType = keyContainerInfo.ProviderType; keyProvInfo.dwKeySpec = (int)keyContainerInfo.KeyNumber; if (!Interop.crypt32.CertSetCertificateContextProperty( pal._certContext, CertContextPropId.CERT_KEY_PROV_INFO_PROP_ID, CertSetPropertyFlags.None, &keyProvInfo)) { pal.Dispose(); throw Interop.CPError.GetHRForLastWin32Error().ToCryptographicException(); } } return(pal); }
/// <summary> /// Given a certificate, this will return back the Cng private key. /// You will have to use this method if the PrivateKey method on X509Certificate2 returns null or throws an exception /// if the X509Certificate2.HasPrivateKey property returns true. /// This situation indicates that the key is stored using Cng and not CryptoApi. /// The reason this happens is that .NET -le 4.7 does not provide an interface for Cng so Win32 interop must be used instead. /// The default constructor for this class will instantiate an instance of CngNativeProviderProxy which uses the win32 native apis /// For testing, dependency injection can be used to provide a mock of CngNativeProviderProxy via ICngNativeProvider /// </summary> /// <param name="cert"></param> /// <returns></returns> public CngKey GetCngKey(X509Certificate2 cert) { ValidateCertificateArgument(cert); IntPtr providerHandle = IntPtr.Zero; IntPtr keyHandle = IntPtr.Zero; try { CRYPT_KEY_PROV_INFO keyProvider = GetPrivateKeyProvider(cert); providerHandle = OpenKeyVault(keyProvider); keyHandle = OpenKey(keyProvider, providerHandle); uint keyNameSize = GetKeyNameBufferSize(keyHandle); string keyName = GetKeyName(keyHandle, keyNameSize); return(InitializeCngKey(keyName)); } catch (Exception e) { throw e; } finally { FreeCngHandles(providerHandle, keyHandle); } }
private static extern SafeCertContextHandle CertCreateSelfSignCertificate(SafeNCryptKeyHandle hCryptProvOrNCryptKey, [In] ref CRYPTOAPI_BLOB pSubjectIssuerBlob, X509CertificateCreationOptions dwFlags, [In] ref CRYPT_KEY_PROV_INFO pKeyProvInfo, [In] ref CRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm, [In] ref SYSTEMTIME pStartTime, [In] ref SYSTEMTIME pEndTime, [In] ref CERT_EXTENSIONS pExtensions);
public static extern IntPtr CertCreateSelfSignCertificate(IntPtr hProv, ref CERT_NAME_BLOB pSubjectIssuerBlob, uint dwFlagsm, ref CRYPT_KEY_PROV_INFO pKeyProvInfo, ref CRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm, ref SYSTEM_TIME pStartTime, ref SYSTEM_TIME pEndTime, IntPtr other);
internal static extern IntPtr CertCreateSelfSignCertificate( IntPtr providerHandle, [In] CryptoApiBlob subjectIssuerBlob, int flags, ref CRYPT_KEY_PROV_INFO pKeyProvInfo, ref CRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm, [In] SystemTime startTime, [In] SystemTime endTime, IntPtr extensions);
public static extern IntPtr CertCreateSelfSignCertificate( IntPtr hProv, ref CERT_NAME_BLOB pSubjectIssuerBlob, uint dwFlagsm, ref CRYPT_KEY_PROV_INFO pKeyProvInfo, IntPtr pSignatureAlgorithm, IntPtr pStartTime, IntPtr pEndTime, IntPtr other);
internal static extern SafeCertContextHandle CertCreateSelfSignCertificate( SafeCryptProvHandle providerHandle, [In] ref CRYPTOAPI_BLOB subjectIssuerBlob, uint flags, [In] ref CRYPT_KEY_PROV_INFO keyProviderInformation, [In] ref CRYPT_ALGORITHM_IDENTIFIER signatureAlgorithm, [In] ref SYSTEMTIME startTime, [In] ref SYSTEMTIME endTime, [In] ref CERT_EXTENSIONS extensions);
public unsafe X509Certificate2 GenerateCertificateAuthority(PrivateKey privateKey, X500DistinguishedName dn, HashAlgorithm signatureAlgorithm, DateTime?notBefore = null, DateTime?notAfter = null) { { fixed(byte *dnPtr = dn.RawData) { var blob = new NATIVE_CRYPTOAPI_BLOB { cbData = (uint)dn.RawData.Length, pbData = dnPtr }; var signatureAlgorithmIdentifier = new CRYPT_ALGORITHM_IDENTIFIER { pszObjId = HashAlgorithmToSignatureAlgorithm(privateKey, signatureAlgorithm) }; using (var extensions = new MarshalX509ExtensionCollection()) { using (extensions.Freeze()) { extensions.Add(new X509BasicConstraintsExtension(true, true, 1, true)); extensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.CrlSign | X509KeyUsageFlags.KeyCertSign, true)); extensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid(OIDs.EKU_SERVER) }, false)); using (var publicKey = privateKey.ToPublicKey()) { using (var sha1 = new SHA1CryptoServiceProvider()) { var pubKeyHash = sha1.ComputeHash(publicKey.Key); extensions.Add(new X509SubjectKeyIdentifierExtension(pubKeyHash, false)); extensions.Add(new X509AuthorityKeyIdentifierExtension(pubKeyHash, null)); } } } var certExtensions = extensions.Extensions; var keyProvInfo = new CRYPT_KEY_PROV_INFO { cProvParam = 0, dwKeySpec = privateKey.KeySpec, dwProvType = privateKey.Handle.IsNCryptKey ? ProviderType.CNG : ProviderType.PROV_RSA_AES, pwszProvName = privateKey.ProviderName, dwFlags = 0, pwszContainerName = privateKey.Name }; var beginning = new SYSTEMTIME(notBefore ?? DateTime.UtcNow.AddHours(-1)); var expiration = new SYSTEMTIME(notAfter ?? DateTime.UtcNow.AddHours(-1).AddYears(30)); var certContext = Crypt32.CertCreateSelfSignCertificate(privateKey.Handle, ref blob, SelfSignFlags.NONE, ref keyProvInfo, ref signatureAlgorithmIdentifier, beginning, expiration, ref certExtensions); if (certContext == IntPtr.Zero) { throw new Win32Exception(Marshal.GetLastWin32Error()); } return(new X509Certificate2(certContext)); } } } }
public static extern IntPtr CertCreateSelfSignCertificate( SafeCryptProviderHandle hCryptProvOrNCryptKey, [Out] CRYPTOAPI_BLOB pSubjectIssuerBlob, CertCreationFlags dwFlags, CRYPT_KEY_PROV_INFO pKeyProvInfo, CRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm, SYSTEMTIME pStartTime, SYSTEMTIME pEndTime, CERT_EXTENSIONS pExtension );
CriticalAllocHandle GetProviderInfo() { CRYPT_KEY_PROV_INFO provInfo = new CRYPT_KEY_PROV_INFO(); provInfo.container = this.keyContainerName; provInfo.providerType = (int)ProviderType.RsaSecureChannel; provInfo.paramsCount = 0; provInfo.keySpec = (int)AlgorithmType.KeyExchange; return(CriticalAllocHandleBlob.FromBlob <CRYPT_KEY_PROV_INFO>(provInfo)); }
private CriticalAllocHandle GetProviderInfo() { CRYPT_KEY_PROV_INFO id = new CRYPT_KEY_PROV_INFO { container = this.keyContainerName, providerType = 12, paramsCount = 0, keySpec = 1 }; return(CriticalAllocHandleBlob.FromBlob <CRYPT_KEY_PROV_INFO>(id)); }
/// <summary> /// Given a certificate, this will return information about how the private key is stored. /// If the certificate doesn't have a private key, an exception will be thrown. /// </summary> /// <param name="cert"></param> /// <returns></returns> public PrivateKeyStorageMetadata GetPrivateKeyStorageMetadata(X509Certificate2 cert) { ValidateCertificateArgument(cert); CRYPT_KEY_PROV_INFO provider = this.GetPrivateKeyProvider(cert); return(new PrivateKeyStorageMetadata() { ContainerName = provider.pwszContainerName, ProviderName = provider.pwszProvName, ProviderType = provider.dwProvType }); }
internal static bool SetCertificateKeyProvInfo( SafeCertContextHandle certificateContext, ref CRYPT_KEY_PROV_INFO provInfo) { Debug.Assert(certificateContext != null, "certificateContext != null"); Debug.Assert(!certificateContext.IsClosed && !certificateContext.IsInvalid, "!certificateContext.IsClosed && !certificateContext.IsInvalid"); return(UnsafeNativeMethods.CertSetCertificateContextProperty( certificateContext, CertificateProperty.KeyProviderInfo, CertSetPropertyFlags.None, ref provInfo)); }
/// <summary> /// Opens a private key and returns a handle to the opened key. /// </summary> /// <param name="keyProv"></param> /// <param name="providerHandle"></param> /// <returns></returns> private IntPtr OpenKey(CRYPT_KEY_PROV_INFO keyProv, IntPtr providerHandle) { IntPtr keyHandle = IntPtr.Zero; int result = cngProvider.NCryptOpenKey(providerHandle, out keyHandle, keyProv.pwszContainerName, 0, NCRYPT_MACHINE_KEY_FLAG); switch (result) { case SUCCESS: return(keyHandle); default: throw new Exception(string.Format("Unable to open key - {0}", result)); } }
/// <summary> /// Opens the specified key provider and returns back a handle to the opened provider /// </summary> /// <param name="keyProvider"></param> /// <returns></returns> private IntPtr OpenKeyVault(CRYPT_KEY_PROV_INFO keyProvider) { int result = int.MaxValue; IntPtr providerHandle = IntPtr.Zero; result = cngProvider.NCryptOpenStorageProvider(ref providerHandle, keyProvider.pwszProvName, 0); switch (result) { case SUCCESS: return(providerHandle); default: throw new Exception(string.Format("Unable to open provider - {0}", result)); } }
private unsafe ICertificatePal CopyWithPersistedCngKey(CngKey cngKey) { if (string.IsNullOrEmpty(cngKey.KeyName)) { return(null); } // Make a new pal from bytes. CertificatePal pal = (CertificatePal)FromBlob(RawData, SafePasswordHandle.InvalidHandle, X509KeyStorageFlags.PersistKeySet); CngProvider provider = cngKey.Provider; string keyName = cngKey.KeyName; bool machineKey = cngKey.IsMachineKey; // CAPI RSA_SIGN keys won't open correctly under CNG without the key number being specified, so // check to see if we can figure out what key number it needs to re-open. int keySpec = GuessKeySpec(provider, keyName, machineKey, cngKey.AlgorithmGroup); CRYPT_KEY_PROV_INFO keyProvInfo = new CRYPT_KEY_PROV_INFO(); fixed(char *keyNamePtr = cngKey.KeyName) fixed(char *provNamePtr = cngKey.Provider.Provider) { keyProvInfo.pwszContainerName = keyNamePtr; keyProvInfo.pwszProvName = provNamePtr; keyProvInfo.dwFlags = machineKey ? CryptAcquireContextFlags.CRYPT_MACHINE_KEYSET : 0; keyProvInfo.dwKeySpec = keySpec; if (!Interop.crypt32.CertSetCertificateContextProperty( pal._certContext, CertContextPropId.CERT_KEY_PROV_INFO_PROP_ID, CertSetPropertyFlags.None, &keyProvInfo)) { pal.Dispose(); throw Marshal.GetLastWin32Error().ToCryptographicException(); } } return(pal); }
public static void RegisterKeyContainer() { uint[] keySpecs = { AT_SIGNATURE, AT_KEYEXCHANGE }; string providerName = "Microsoft Base Smart Card Crypto Provider"; IntPtr hCryptProvider = new IntPtr(); if (!CryptAcquireContext(ref hCryptProvider, null, providerName, PROV_RSA_FULL, (uint)0)) { throw new RegisterException(new RegisterExceptionEventArgs(RegisterException.ACQUIRE_CONTEXT)); } uint bufferSize = 1024; StringBuilder containerName = new StringBuilder((int)bufferSize); // enumerate all containers while (CryptGetProvParam(hCryptProvider, PP_ENUMCONTAINERS, containerName, ref bufferSize, CRYPT_FIRST)) { Debug.WriteLine("Container Name: " + containerName.ToString()); if (!CryptAcquireContext(ref hCryptProvider, containerName.ToString(), providerName, PROV_RSA_FULL, 0)) { continue; } // loop over the key specs foreach (uint keySpec in keySpecs) { IntPtr hUserKey = new IntPtr(); uint certLen = 0; if (!CryptGetUserKey(hCryptProvider, keySpec, ref hUserKey)) { continue; } if (!CryptGetKeyParam(hUserKey, KP_CERTIFICATE, 0, ref certLen, 0)) // get certificate length { throw new Exception("Could not retrieve certificate length."); } Debug.WriteLine("got certificate length"); byte[] rawCert = new byte[certLen]; if (!CryptGetKeyParam(hUserKey, KP_CERTIFICATE, rawCert, ref certLen, 0)) // get certificate { throw new Exception("Could not retrieve certificate."); } Debug.WriteLine("got certificate"); IntPtr hCertContext = new IntPtr(); hCertContext = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, rawCert, certLen); CRYPT_KEY_PROV_INFO ProvInfo = new CRYPT_KEY_PROV_INFO(); ProvInfo.ContainerName = containerName.ToString(); ProvInfo.ProvName = providerName; ProvInfo.ProvType = PROV_RSA_FULL; ProvInfo.Flags = 0; ProvInfo.KeySpec = AT_SIGNATURE; ProvInfo.ProvParam = 0; ProvInfo.rgProvParam = new IntPtr(); X509Certificate2 c = new X509Certificate2(rawCert); string name = c.SubjectName.Name; if (!CertSetCertificateContextProperty(hCertContext, CERT_KEY_PROV_INFO_PROP_ID, 0, ref ProvInfo)) { throw new Exception("Could set certificate's context."); } Debug.WriteLine("Context set!"); IntPtr hCertStore = CertOpenStoreStringPara(CERT_STORE_PROV_SYSTEM_W, 0, new IntPtr(), CERT_STORE_OPEN_EXISTING_FLAG | CERT_SYSTEM_STORE_CURRENT_USER, "My"); if (!CertAddCertificateContextToStore(hCertStore, hCertContext, CERT_STORE_ADD_REPLACE_EXISTING, new IntPtr())) { throw new Exception("Could not add certificate to store."); } } } }
/// <summary> /// Creates and returns a self-signed X509 certificate. /// </summary> /// <param name="key">The key of the certificate.</param> /// <param name="issuerName">The encoded common name of the issuer.</param> /// <param name="settings">The settings of the X509 certificate.</param> /// <returns>An <see cref="X509Certificate"/> instance.</returns> /// <exception cref="CryptographicException">An error occurs while creating the self-signed certificate.</exception> public static X509Certificate Create(RSACryptoServiceProvider key, string issuerName, CertificateSettings settings) { if (settings == null) settings = new CertificateSettings(); CspKeyContainerInfo container = key.CspKeyContainerInfo; IntPtr certHandle = IntPtr.Zero; byte[] issuerBlob = null; uint pcbEncoded = 0; if (CertStrToName(X509_ASN_ENCODING, issuerName, CERT_X500_NAME_STR, IntPtr.Zero, null, ref pcbEncoded, IntPtr.Zero)) { issuerBlob = new byte[pcbEncoded]; CertStrToName(X509_ASN_ENCODING, issuerName, CERT_X500_NAME_STR, IntPtr.Zero, issuerBlob, ref pcbEncoded, IntPtr.Zero); } else { throw new CryptographicException("Cannot encode the issuer name! Please check whether the string is in a valid format."); } List<IntPtr> extensionList = new List<IntPtr>(); CERT_NAME_BLOB subject = new CERT_NAME_BLOB(); try { subject.pbData = Marshal.AllocHGlobal(issuerBlob.Length); Marshal.Copy(issuerBlob, 0, subject.pbData, issuerBlob.Length); subject.cbData = issuerBlob.Length; SYSTEMTIME startDate = ConvertDateTime(settings.StartDate); SYSTEMTIME endDate = ConvertDateTime(settings.EndDate); CRYPT_KEY_PROV_INFO providerInfo = new CRYPT_KEY_PROV_INFO(); providerInfo.pwszContainerName = container.KeyContainerName; providerInfo.pwszProvName = container.ProviderName; providerInfo.dwProvType = container.ProviderType; providerInfo.dwFlags = (int)0; providerInfo.cProvParam = 0; providerInfo.rgProvParam = IntPtr.Zero; providerInfo.dwKeySpec = (int)container.KeyNumber; // convert the list of extensions to an unmanaged structure // the .NET marshallers don't handle recursive arrays too well, // so we do it manually :-/ extensionList = ConvertExtensions(settings.Extensions); certHandle = CertCreateSelfSignCertificate(IntPtr.Zero, ref subject, settings.Flags, ref providerInfo, IntPtr.Zero, ref startDate, ref endDate, extensionList[0]); if (certHandle == IntPtr.Zero) { throw new CryptographicException("Couldn't create unsigned certificate"); } return new X509Certificate2(certHandle); } finally { if (subject.pbData != IntPtr.Zero) Marshal.FreeHGlobal(subject.pbData); if (extensionList != null && extensionList.Count > 0) { foreach (IntPtr ptr in extensionList) { if (ptr != IntPtr.Zero) Marshal.FreeHGlobal(ptr); } } } }
/// <summary> /// Create a self-signed x509 certificate. /// </summary> /// <param name="subjectName">The distinguished name.</param> /// <param name="notBefore">The start time.</param> /// <param name="notAfter">the end time.</param> /// <param name="extensions">the extensions.</param> /// <returns>A byte array containing the certificate and private key encoded as PFX.</returns> public static byte[] CreateSelfSignCertificatePfx( string subjectName, DateTime notBefore, DateTime notAfter, params X509Extension[] extensions) { if (subjectName == null) { subjectName = string.Empty; } byte[] pfxData; SYSTEMTIME startSystemTime = ToSystemTime(notBefore); SYSTEMTIME endSystemTime = ToSystemTime(notAfter); string containerName = $"Created by Workstation. {Guid.NewGuid().ToString()}"; GCHandle gcHandle = default(GCHandle); var providerContext = SafeCryptProvHandle.InvalidHandle; var cryptKey = SafeCryptKeyHandle.InvalidHandle; var certContext = SafeCertContextHandle.InvalidHandle; var certStore = SafeCertStoreHandle.InvalidHandle; var storeCertContext = SafeCertContextHandle.InvalidHandle; try { Check(NativeMethods.CryptAcquireContextW( out providerContext, containerName, null, PROV_RSA_FULL, CRYPT_NEWKEYSET)); Check(NativeMethods.CryptGenKey( providerContext, AT_KEYEXCHANGE, CRYPT_EXPORTABLE | (2048 << 16), // 2048bit out cryptKey)); IntPtr pbEncoded = IntPtr.Zero; int cbEncoded = 0; Check(NativeMethods.CertStrToNameW( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, subjectName, CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, IntPtr.Zero, IntPtr.Zero, ref cbEncoded, IntPtr.Zero)); pbEncoded = Marshal.AllocHGlobal(cbEncoded); Check(NativeMethods.CertStrToNameW( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, subjectName, CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, IntPtr.Zero, pbEncoded, ref cbEncoded, IntPtr.Zero)); var nameBlob = new CRYPTOAPI_BLOB { cbData = (uint)cbEncoded, pbData = pbEncoded }; var kpi = new CRYPT_KEY_PROV_INFO { pwszContainerName = containerName, dwProvType = PROV_RSA_FULL, dwKeySpec = AT_KEYEXCHANGE }; var signatureAlgorithm = new CRYPT_ALGORITHM_IDENTIFIER { pszObjId = OID_RSA_SHA256RSA, Parameters = default(CRYPTOAPI_BLOB) }; IntPtr pInfo = IntPtr.Zero; int cbInfo = 0; byte[] keyHash = null; int cbKeyHash = 0; try { Check(NativeMethods.CryptExportPublicKeyInfoEx( providerContext, AT_KEYEXCHANGE, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, OID_RSA_RSA, 0, IntPtr.Zero, IntPtr.Zero, ref cbInfo)); pInfo = Marshal.AllocHGlobal(cbInfo); Check(NativeMethods.CryptExportPublicKeyInfoEx( providerContext, AT_KEYEXCHANGE, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, OID_RSA_RSA, 0, IntPtr.Zero, pInfo, ref cbInfo)); Check(NativeMethods.CryptHashPublicKeyInfo( providerContext, CALG_SHA1, 0, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pInfo, null, ref cbKeyHash)); keyHash = new byte[cbKeyHash]; Check(NativeMethods.CryptHashPublicKeyInfo( providerContext, CALG_SHA1, 0, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pInfo, keyHash, ref cbKeyHash)); } finally { if (pInfo != IntPtr.Zero) { Marshal.FreeHGlobal(pInfo); } } var safeExtensions = new List <SafeX509Extension>(); var blob = IntPtr.Zero; try { foreach (var item in extensions) { safeExtensions.Add(new SafeX509Extension(item)); } // adding SubjectKeyIdentifier TODO: AuthKeyIdentifier? safeExtensions.Add(new SafeX509Extension(new X509SubjectKeyIdentifierExtension(keyHash, false))); var structSize = Marshal.SizeOf <CERT_EXTENSION>(); blob = Marshal.AllocHGlobal(structSize * safeExtensions.Count); for (int index = 0, offset = 0; index < safeExtensions.Count; index++, offset += structSize) { var marshalX509Extension = safeExtensions[index]; Marshal.StructureToPtr(marshalX509Extension.Value, blob + offset, false); } var certExtensions = new CERT_EXTENSIONS { cExtension = (uint)safeExtensions.Count, rgExtension = blob }; certContext = NativeMethods.CertCreateSelfSignCertificate( providerContext, ref nameBlob, 0, ref kpi, ref signatureAlgorithm, ref startSystemTime, ref endSystemTime, ref certExtensions); Check(!certContext.IsInvalid); } finally { foreach (var safeExtension in safeExtensions) { safeExtension.Dispose(); } safeExtensions.Clear(); Marshal.FreeHGlobal(blob); Marshal.FreeHGlobal(pbEncoded); } certStore = NativeMethods.CertOpenStore( sz_CERT_STORE_PROV_MEMORY, 0, IntPtr.Zero, CERT_STORE_CREATE_NEW_FLAG, IntPtr.Zero); Check(!certStore.IsInvalid); Check(NativeMethods.CertAddCertificateContextToStore( certStore, certContext, CERT_STORE_ADD_NEW, out storeCertContext)); NativeMethods.CertSetCertificateContextProperty( storeCertContext, CERT_KEY_PROV_INFO_PROP_ID, 0, ref kpi); CRYPTOAPI_BLOB pfxBlob = default(CRYPTOAPI_BLOB); Check(NativeMethods.PFXExportCertStoreEx( certStore, ref pfxBlob, IntPtr.Zero, IntPtr.Zero, EXPORT_PRIVATE_KEYS | REPORT_NO_PRIVATE_KEY | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY)); pfxData = new byte[pfxBlob.cbData]; gcHandle = GCHandle.Alloc(pfxData, GCHandleType.Pinned); pfxBlob.pbData = gcHandle.AddrOfPinnedObject(); Check(NativeMethods.PFXExportCertStoreEx( certStore, ref pfxBlob, IntPtr.Zero, IntPtr.Zero, EXPORT_PRIVATE_KEYS | REPORT_NO_PRIVATE_KEY | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY)); gcHandle.Free(); } finally { if (gcHandle.IsAllocated) { gcHandle.Free(); } if (!certContext.IsInvalid) { certContext.Dispose(); } if (!storeCertContext.IsInvalid) { storeCertContext.Dispose(); } if (!certStore.IsInvalid) { certStore.Dispose(); } if (!cryptKey.IsInvalid) { cryptKey.Dispose(); } if (!providerContext.IsInvalid) { providerContext.Dispose(); providerContext = SafeCryptProvHandle.InvalidHandle; // Delete generated keyset. Does not return a providerContext NativeMethods.CryptAcquireContextW( out providerContext, containerName, null, PROV_RSA_FULL, CRYPT_DELETEKEYSET); } } return(pfxData); }
public static extern bool CertSetCertificateContextProperty(IntPtr pCertContext, uint dwPropId, uint dwFlags, ref CRYPT_KEY_PROV_INFO pvData);
public unsafe X509Certificate2 GenerateCertificate(X509Certificate2 issuingCertificate, PrivateKey privateKey, X500DistinguishedName dn, string[] dnsNames, IPAddress[] ipAddresses = null, HashAlgorithm?signatureAlgorithm = null, DateTime?notBefore = null, DateTime?notAfter = null) { if (!issuingCertificate.HasPrivateKey) { throw new ArgumentException("Issuing certificate must have a private key.", nameof(issuingCertificate)); } IntPtr basicEncodedDataPtr = IntPtr.Zero, certExtensionPtr = IntPtr.Zero; var serialNumber = new byte[16]; var rng = RandomNumberGenerator.Create(); rng.GetNonZeroBytes(serialNumber); serialNumber[15] &= 0x7F; fixed(byte *dnPtr = dn.RawData, issuerDnPtr = issuingCertificate.SubjectName.RawData, serialNumberPtr = serialNumber) { try { var blob = new NATIVE_CRYPTOAPI_BLOB { cbData = (uint)dn.RawData.Length, pbData = dnPtr }; var signingSignatureAlgorithmIdentifier = new CRYPT_ALGORITHM_IDENTIFIER { pszObjId = issuingCertificate.SignatureAlgorithm.Value }; using (var signingKey = ExtractKey(issuingCertificate)) { var signingAlgorithmIdentifier = new CRYPT_ALGORITHM_IDENTIFIER { pszObjId = signatureAlgorithm != null?HashAlgorithmToSignatureAlgorithm(signingKey, signatureAlgorithm.Value) : issuingCertificate.SignatureAlgorithm.Value }; using (PublicKeyInfo publicKey = privateKey.ToPublicKey(), signingPublicKey = signingKey.ToPublicKey()) { using (var extensions = new MarshalX509ExtensionCollection()) { using (extensions.Freeze()) { var usage = X509KeyUsageFlags.DigitalSignature; if (privateKey.AlgorithmGroup == AlgorithmGroup.RSA) { //Key encipherment is not valid for DSA/ECDSA usage |= X509KeyUsageFlags.KeyEncipherment; } extensions.Add(new X509BasicConstraintsExtension(false, false, 0, true)); extensions.Add(new X509KeyUsageExtension(usage, true)); extensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid(OIDs.EKU_SERVER) }, false)); extensions.Add(new X509SubjectAlternativeNameExtension(DnsAltNamesFromArray(dnsNames, ipAddresses ?? new IPAddress[0]), false)); using (var sha1 = new SHA1CryptoServiceProvider()) { var issuingKeyId = sha1.ComputeHash(signingPublicKey.Key); extensions.Add(new X509SubjectKeyIdentifierExtension(sha1.ComputeHash(publicKey.Key), false)); extensions.Add(new X509AuthorityKeyIdentifierExtension(issuingKeyId, null)); } } var certInfo = new CERT_INFO(); certInfo.Subject = blob; certInfo.SerialNumber = new NATIVE_CRYPTOAPI_BLOB { cbData = (uint)serialNumber.Length, pbData = serialNumberPtr }; certInfo.SubjectPublicKeyInfo = publicKey.PublicKey; certInfo.dwVersion = CertificateVersion.CERT_V3; certInfo.Issuer = new NATIVE_CRYPTOAPI_BLOB { cbData = (uint)issuingCertificate.SubjectName.RawData.Length, pbData = issuerDnPtr }; certInfo.SignatureAlgorithm = signingAlgorithmIdentifier; certInfo.NotAfter = FileTimeHelper.ToFileTimeStructureUtc(notAfter ?? DateTime.Now.AddHours(-1).AddYears(5)); certInfo.NotBefore = FileTimeHelper.ToFileTimeStructureUtc(notBefore ?? DateTime.Now.AddHours(-1)); certInfo.cExtension = extensions.Extensions.cExtension; certInfo.rgExtension = extensions.Extensions.rgExtension; var size = 0u; var CERT_INFO_TYPE = (IntPtr)2; if (!Crypt32.CryptSignAndEncodeCertificate(signingKey.Handle, signingKey.KeySpec, EncodingType.X509_ASN_ENCODING, CERT_INFO_TYPE, ref certInfo, ref signingSignatureAlgorithmIdentifier, IntPtr.Zero, IntPtr.Zero, ref size)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } var buffer = Marshal.AllocHGlobal((int)size); if (!Crypt32.CryptSignAndEncodeCertificate(signingKey.Handle, signingKey.KeySpec, EncodingType.X509_ASN_ENCODING, CERT_INFO_TYPE, ref certInfo, ref signingSignatureAlgorithmIdentifier, IntPtr.Zero, buffer, ref size)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } const int CERT_KEY_PROV_INFO_PROP_ID = 2; var certificate = new X509Certificate2(SerializeCertificate(buffer, size)); var keyProvInfo = new CRYPT_KEY_PROV_INFO { cProvParam = 0, dwKeySpec = privateKey.KeySpec, dwProvType = privateKey.Handle.IsNCryptKey ? ProviderType.CNG : ProviderType.PROV_RSA_AES, pwszProvName = privateKey.ProviderName, dwFlags = 0, pwszContainerName = privateKey.Name }; if (!Crypt32.CertSetCertificateContextProperty(certificate.Handle, CERT_KEY_PROV_INFO_PROP_ID, 0u, ref keyProvInfo)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } return(new X509Certificate2(certificate)); } } } } finally { Marshal.FreeHGlobal(basicEncodedDataPtr); Marshal.FreeHGlobal(certExtensionPtr); } } }
CriticalAllocHandle GetProviderInfo() { CRYPT_KEY_PROV_INFO provInfo = new CRYPT_KEY_PROV_INFO(); provInfo.container = this.keyContainerName; provInfo.providerType = (int)ProviderType.RsaSecureChannel; provInfo.paramsCount = 0; provInfo.keySpec = (int)AlgorithmType.KeyExchange; return CriticalAllocHandleBlob.FromBlob<CRYPT_KEY_PROV_INFO>(provInfo); }
private static SafeCertContextHandle CreateSelfSignedCertificate(CngKey key, bool takeOwnershipOfKey, byte[] subjectName, X509CertificateCreationOptions creationOptions, string signatureAlgorithmOid, DateTime startTime, DateTime endTime) { // Create an algorithm identifier structure for the signature algorithm CRYPT_ALGORITHM_IDENTIFIER nativeSignatureAlgorithm = new CRYPT_ALGORITHM_IDENTIFIER(); nativeSignatureAlgorithm.pszObjId = signatureAlgorithmOid; nativeSignatureAlgorithm.Parameters = new CRYPTOAPI_BLOB(); nativeSignatureAlgorithm.Parameters.cbData = 0; nativeSignatureAlgorithm.Parameters.pbData = IntPtr.Zero; // Convert the begin and expire dates to system time structures SYSTEMTIME nativeStartTime = new SYSTEMTIME(startTime); SYSTEMTIME nativeEndTime = new SYSTEMTIME(endTime); CERT_EXTENSIONS nativeExtensions = new CERT_EXTENSIONS(); nativeExtensions.cExtension = 0; // Setup a CRYPT_KEY_PROV_INFO for the key CRYPT_KEY_PROV_INFO keyProvInfo = new CRYPT_KEY_PROV_INFO(); keyProvInfo.pwszContainerName = key.UniqueName; keyProvInfo.pwszProvName = key.Provider.Provider; keyProvInfo.dwProvType = 0; // NCRYPT keyProvInfo.dwFlags = 0; keyProvInfo.cProvParam = 0; keyProvInfo.rgProvParam = IntPtr.Zero; keyProvInfo.dwKeySpec = 0; // // Now that all of the needed data structures are setup, we can create the certificate // SafeCertContextHandle selfSignedCertHandle = null; unsafe { fixed (byte* pSubjectName = &subjectName[0]) { // Create a CRYPTOAPI_BLOB for the subject of the cert CRYPTOAPI_BLOB nativeSubjectName = new CRYPTOAPI_BLOB(); nativeSubjectName.cbData = subjectName.Length; nativeSubjectName.pbData = new IntPtr(pSubjectName); // Now that we've converted all the inputs to native data structures, we can generate // the self signed certificate for the input key. using (SafeNCryptKeyHandle keyHandle = key.Handle) { selfSignedCertHandle = CertCreateSelfSignCertificate(keyHandle, ref nativeSubjectName, creationOptions, ref keyProvInfo, ref nativeSignatureAlgorithm, ref nativeStartTime, ref nativeEndTime, ref nativeExtensions); if (selfSignedCertHandle.IsInvalid) { throw new CryptographicException(Marshal.GetLastWin32Error()); } } } } Debug.Assert(selfSignedCertHandle != null, "selfSignedCertHandle != null"); // Attach a key context to the certificate which will allow Windows to find the private key // associated with the certificate if the NCRYPT_KEY_HANDLE is ephemeral. // is done. using (SafeNCryptKeyHandle keyHandle = key.Handle) { CERT_KEY_CONTEXT keyContext = new CERT_KEY_CONTEXT(); keyContext.cbSize = Marshal.SizeOf(typeof(CERT_KEY_CONTEXT)); keyContext.hNCryptKey = keyHandle.DangerousGetHandle(); keyContext.dwKeySpec = KeySpec.NCryptKey; bool attachedProperty = false; int setContextError = 0; // Run in a CER to ensure accurate tracking of the transfer of handle ownership RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { CertificatePropertySetFlags flags = CertificatePropertySetFlags.None; if (!takeOwnershipOfKey) { // If the certificate is not taking ownership of the key handle, then it should // not release the handle when the context is released. flags |= CertificatePropertySetFlags.NoCryptRelease; } attachedProperty = CertSetCertificateContextProperty(selfSignedCertHandle, CertificateProperty.KeyContext, flags, ref keyContext); setContextError = Marshal.GetLastWin32Error(); // If we succesfully transferred ownership of the key to the certificate, // then we need to ensure that we no longer release its handle. if (attachedProperty && takeOwnershipOfKey) { keyHandle.SetHandleAsInvalid(); } } if (!attachedProperty) { throw new CryptographicException(setContextError); } } return selfSignedCertHandle; }
/// <summary> /// Set the private key object. The additional "publicKey" argument is used to validate that the private key corresponds to the existing publicKey. /// </summary> public void SetPrivateKey(AsymmetricAlgorithm privateKey, AsymmetricAlgorithm publicKey) { if (privateKey == null) { unsafe { if (!Interop.crypt32.CertSetCertificateContextProperty(_certContext, CertContextPropId.CERT_KEY_PROV_INFO_PROP_ID, CertSetPropertyFlags.None, (CRYPT_KEY_PROV_INFO *)null)) { throw new CryptographicException(Marshal.GetLastWin32Error()); } } } else { // we do not support keys in non-CAPI storage for now. ICspAsymmetricAlgorithm asymmetricAlgorithm = privateKey as ICspAsymmetricAlgorithm; if (asymmetricAlgorithm == null) { throw new NotSupportedException(SR.NotSupported_InvalidKeyImpl); } if (asymmetricAlgorithm.CspKeyContainerInfo == null) { throw new ArgumentException("CspKeyContainerInfo"); } // check that the public key in the certificate corresponds to the private key passed in. // // note that it should be legal to set a key which matches in every aspect but the usage // i.e. to use a CALG_RSA_KEYX private key to match a CALG_RSA_SIGN public key. A // PUBLICKEYBLOB is defined as: // // BLOBHEADER publickeystruc // RSAPUBKEY rsapubkey // BYTE modulus[rsapubkey.bitlen/8] // // To allow keys which differ by key usage only, we skip over the BLOBHEADER of the key, // and start comparing bytes at the RSAPUBKEY structure. unsafe { // This cast is safe because via our contract with our caller, "publicKey" is the Key property of a PublicKey object that this Pal class manufactured in the first place. // Since we manufactured the PublicKey, we know the real types of the object. ICspAsymmetricAlgorithm cspPublicKey = (ICspAsymmetricAlgorithm)publicKey; byte[] array1 = cspPublicKey.ExportCspBlob(false); byte[] array2 = asymmetricAlgorithm.ExportCspBlob(false); if (array1 == null || array2 == null || array1.Length != array2.Length || array1.Length <= sizeof(BLOBHEADER)) { throw new CryptographicUnexpectedOperationException(SR.Cryptography_X509_KeyMismatch); } for (int index = sizeof(BLOBHEADER); index < array1.Length; index++) { if (array1[index] != array2[index]) { throw new CryptographicUnexpectedOperationException(SR.Cryptography_X509_KeyMismatch); } } } unsafe { CspKeyContainerInfo keyContainerInfo = asymmetricAlgorithm.CspKeyContainerInfo; fixed(char *pKeyContainerName = keyContainerInfo.KeyContainerName, pProviderName = keyContainerInfo.ProviderName) { CRYPT_KEY_PROV_INFO keyProvInfo = new CRYPT_KEY_PROV_INFO() { pwszContainerName = pKeyContainerName, pwszProvName = pProviderName, dwProvType = keyContainerInfo.ProviderType, dwFlags = keyContainerInfo.MachineKeyStore ? CryptAcquireContextFlags.CRYPT_MACHINE_KEYSET : CryptAcquireContextFlags.None, cProvParam = 0, rgProvParam = IntPtr.Zero, dwKeySpec = (int)(keyContainerInfo.KeyNumber), }; if (!Interop.crypt32.CertSetCertificateContextProperty(_certContext, CertContextPropId.CERT_KEY_PROV_INFO_PROP_ID, CertSetPropertyFlags.None, &keyProvInfo)) { throw new CryptographicException(Marshal.GetLastWin32Error()); } } } } }
/// <summary> /// Creates the certificate and adds it to the store. /// </summary> private static string CreateSelfSignedCertificate( IntPtr hProvider, IntPtr hStore, bool useMachineStore, string applicationName, string applicationUri, string subjectName, IList<string> hostNames, ushort keySize, ushort lifetimeInMonths, ushort algorithm = 0) { IntPtr hKey = IntPtr.Zero; IntPtr pKpi = IntPtr.Zero; IntPtr pThumbprint = IntPtr.Zero; IntPtr pContext = IntPtr.Zero; IntPtr pAlgorithmId = IntPtr.Zero; IntPtr pNewContext = IntPtr.Zero; CRYPT_DATA_BLOB publicKeyId = new CRYPT_DATA_BLOB(); CERT_NAME_BLOB subjectNameBlob = new CERT_NAME_BLOB(); SYSTEMTIME stValidTo = new SYSTEMTIME(); CERT_EXTENSIONS extensions = new CERT_EXTENSIONS(); CRYPT_DATA_BLOB friendlyName = new CRYPT_DATA_BLOB(); GCHandle hValidTo = new GCHandle(); GCHandle hExtensionList = new GCHandle(); GCHandle hSubjectNameBlob = new GCHandle(); GCHandle hFriendlyName = new GCHandle(); try { // create a new key pair. int bResult = NativeMethods.CryptGenKey( hProvider, AT_KEYEXCHANGE, CRYPT_EXPORTABLE | (keySize << 16), ref hKey); if (bResult == 0) { Throw("Could not generate a new key pair. Error={0:X8}", Marshal.GetLastWin32Error()); } // gey the public key identifier. GetPublicKeyIdentifier(hProvider, ref publicKeyId); // construct the certificate subject name. CreateX500Name(subjectName, ref subjectNameBlob); GCHandle hSubjectName = GCHandle.Alloc(subjectNameBlob, GCHandleType.Pinned); // allocate memory for all possible extensions. extensions.cExtension = 0; extensions.rgExtension = Marshal.AllocHGlobal(6 * Marshal.SizeOf(typeof(CERT_EXTENSION))); // create the subject key info extension. IntPtr pPos = extensions.rgExtension; CERT_EXTENSION extension = new CERT_EXTENSION(); CreateSubjectKeyIdentifierExtension(ref extension, ref publicKeyId); Marshal.StructureToPtr(extension, pPos, false); pPos = new IntPtr(pPos.ToInt64() + Marshal.SizeOf(typeof(CERT_EXTENSION))); extensions.cExtension++; // create the authority key info extension. extension = new CERT_EXTENSION(); CreateAuthorityKeyIdentifierExtension(ref extension, ref publicKeyId); Marshal.StructureToPtr(extension, pPos, false); pPos = new IntPtr(pPos.ToInt64() + Marshal.SizeOf(typeof(CERT_EXTENSION))); extensions.cExtension++; // create the basic constraints extension. extension = new CERT_EXTENSION(); CreateBasicConstraintsExtension(ref extension, false); Marshal.StructureToPtr(extension, pPos, false); pPos = new IntPtr(pPos.ToInt64() + Marshal.SizeOf(typeof(CERT_EXTENSION))); extensions.cExtension++; // create the key usage extension. extension = new CERT_EXTENSION(); CreateKeyUsageExtension(ref extension, false); Marshal.StructureToPtr(extension, pPos, false); pPos = new IntPtr(pPos.ToInt64() + Marshal.SizeOf(typeof(CERT_EXTENSION))); extensions.cExtension++; // create the extended key usage extension. extension = new CERT_EXTENSION(); CreateExtendedKeyUsageExtension(ref extension); Marshal.StructureToPtr(extension, pPos, false); pPos = new IntPtr(pPos.ToInt64() + Marshal.SizeOf(typeof(CERT_EXTENSION))); extensions.cExtension++; // create the subject alternate name extension. extension = new CERT_EXTENSION(); CreateSubjectAltNameExtension(applicationUri, hostNames, ref extension); Marshal.StructureToPtr(extension, pPos, false); pPos = new IntPtr(pPos.ToInt64() + Marshal.SizeOf(typeof(CERT_EXTENSION))); extensions.cExtension++; // set the expiration date. DateTime validTo = DateTime.UtcNow.AddMonths(lifetimeInMonths); System.Runtime.InteropServices.ComTypes.FILETIME ftValidTo = new System.Runtime.InteropServices.ComTypes.FILETIME(); ulong ticks = (ulong)(validTo.Ticks - new DateTime(1601, 1, 1).Ticks); ftValidTo.dwHighDateTime = (int)((0xFFFFFFFF00000000 & (ulong)ticks) >> 32); ftValidTo.dwLowDateTime = (int)((ulong)ticks & 0x00000000FFFFFFFF); NativeMethods.FileTimeToSystemTime(ref ftValidTo, ref stValidTo); // specify what key is being used to sign the certificate. CRYPT_KEY_PROV_INFO kpi = new CRYPT_KEY_PROV_INFO(); kpi.pwszContainerName = KEY_CONTAINER_NAME; // must be the same as the hProvider kpi.pwszProvName = DEFAULT_CRYPTO_PROVIDER; kpi.dwProvType = PROV_RSA_FULL; kpi.dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID; kpi.dwKeySpec = AT_KEYEXCHANGE; if (useMachineStore) { kpi.dwFlags |= CRYPT_MACHINE_KEYSET; } else { kpi.dwFlags |= CRYPT_USER_KEYSET; } pKpi = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CRYPT_KEY_PROV_INFO))); Marshal.StructureToPtr(kpi, pKpi, false); hValidTo = GCHandle.Alloc(stValidTo, GCHandleType.Pinned); hExtensionList = GCHandle.Alloc(extensions, GCHandleType.Pinned); hSubjectNameBlob = GCHandle.Alloc(subjectNameBlob, GCHandleType.Pinned); if (algorithm == 1) { CRYPT_ALGORITHM_IDENTIFIER algorithmID = new CRYPT_ALGORITHM_IDENTIFIER(); algorithmID.pszObjId = "1.2.840.113549.1.1.11"; //SHA256 pAlgorithmId = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CRYPT_ALGORITHM_IDENTIFIER))); Marshal.StructureToPtr(algorithmID, pAlgorithmId, false); //create the certificate pContext = NativeMethods.CertCreateSelfSignCertificate( hProvider, hSubjectNameBlob.AddrOfPinnedObject(), 0, pKpi, pAlgorithmId, IntPtr.Zero, hValidTo.AddrOfPinnedObject(), hExtensionList.AddrOfPinnedObject()); } else { // (default) create the certificate. pContext = NativeMethods.CertCreateSelfSignCertificate( hProvider, hSubjectNameBlob.AddrOfPinnedObject(), 0, pKpi, IntPtr.Zero, IntPtr.Zero, hValidTo.AddrOfPinnedObject(), hExtensionList.AddrOfPinnedObject()); } if (pContext == IntPtr.Zero) { Throw("Could not create self-signed certificate. Error={0:X8}", Marshal.GetLastWin32Error()); } // get the thumbprint. int dwThumbprintSize = 20; pThumbprint = Marshal.AllocHGlobal(dwThumbprintSize); bResult = NativeMethods.CertGetCertificateContextProperty( pContext, CERT_SHA1_HASH_PROP_ID, pThumbprint, ref dwThumbprintSize); if (bResult == 0) { Throw("Could not get the thumbprint of the new certificate. Error={0:X8}", Marshal.GetLastWin32Error()); } byte[] bytes = new byte[dwThumbprintSize]; Marshal.Copy(pThumbprint, bytes, 0, dwThumbprintSize); string thumbprint = Utils.ToHexString(bytes); // set the friendly name. friendlyName.pbData = Marshal.StringToHGlobalUni(applicationName); friendlyName.cbData = (applicationName.Length+1)*Marshal.SizeOf(typeof(ushort)); hFriendlyName = GCHandle.Alloc(friendlyName, GCHandleType.Pinned); bResult = NativeMethods.CertSetCertificateContextProperty( pContext, CERT_FRIENDLY_NAME_PROP_ID, 0, hFriendlyName.AddrOfPinnedObject()); if (bResult == 0) { Throw("Could not set the friendly name for the certificate. Error={0:X8}", Marshal.GetLastWin32Error()); } // add into store. bResult = NativeMethods.CertAddCertificateContextToStore( hStore, pContext, CERT_STORE_ADD_REPLACE_EXISTING, ref pNewContext); if (bResult == 0) { Throw("Could not add the certificate to the store. Error={0:X8}", Marshal.GetLastWin32Error()); } return thumbprint; } finally { if (pContext != IntPtr.Zero) { NativeMethods.CertFreeCertificateContext(pContext); } if (pNewContext != IntPtr.Zero) { NativeMethods.CertFreeCertificateContext(pNewContext); } if (friendlyName.pbData != IntPtr.Zero) { Marshal.FreeHGlobal(friendlyName.pbData); } if (pThumbprint != IntPtr.Zero) { Marshal.FreeHGlobal(pThumbprint); } if (pAlgorithmId != IntPtr.Zero) { Marshal.DestroyStructure(pAlgorithmId, typeof(CRYPT_ALGORITHM_IDENTIFIER)); Marshal.FreeHGlobal(pAlgorithmId); } if (hValidTo.IsAllocated) hValidTo.Free(); if (hExtensionList.IsAllocated) hExtensionList.Free(); if (hSubjectNameBlob.IsAllocated) hSubjectNameBlob.Free(); if (hFriendlyName.IsAllocated) hFriendlyName.Free(); if (pKpi != IntPtr.Zero) { Marshal.DestroyStructure(pKpi, typeof(CRYPT_KEY_PROV_INFO)); Marshal.FreeHGlobal(pKpi); } DeleteExtensions(ref extensions.rgExtension, extensions.cExtension); DeleteX500Name(ref subjectNameBlob); if (publicKeyId.pbData != IntPtr.Zero) { Marshal.FreeHGlobal(publicKeyId.pbData); } if (hKey != IntPtr.Zero) { NativeMethods.CryptDestroyKey(hKey); } } }
private static IntPtr CreateUnsignedCertCntxt(String keycontainer, String provider, uint KEYSPEC, uint cspflags, String DN) { const uint AT_KEYEXCHANGE = 0x00000001; const uint AT_SIGNATURE = 0x00000002; const uint CRYPT_MACHINE_KEYSET = 0x00000020; const uint PROV_RSA_FULL = 0x00000001; const String MS_DEF_PROV = "Microsoft Base Cryptographic Provider v1.0"; const String MS_STRONG_PROV = "Microsoft Strong Cryptographic Provider"; const String MS_ENHANCED_PROV = "Microsoft Enhanced Cryptographic Provider v1.0"; const uint CERT_CREATE_SELFSIGN_NO_SIGN = 1; const uint X509_ASN_ENCODING = 0x00000001; const uint CERT_X500_NAME_STR = 3; IntPtr hCertCntxt = IntPtr.Zero; byte[] encodedName = null; uint cbName = 0; if (provider != MS_DEF_PROV && provider != MS_STRONG_PROV && provider != MS_ENHANCED_PROV) return IntPtr.Zero; if (keycontainer == "") return IntPtr.Zero; if (KEYSPEC != AT_SIGNATURE && KEYSPEC != AT_KEYEXCHANGE) return IntPtr.Zero; if (cspflags != 0 && cspflags != CRYPT_MACHINE_KEYSET) //only 0 (Current User) keyset is currently used. return IntPtr.Zero; if (DN == "") return IntPtr.Zero; if (Win32.CertStrToName(X509_ASN_ENCODING, DN, CERT_X500_NAME_STR, IntPtr.Zero, null, ref cbName, IntPtr.Zero)) { encodedName = new byte[cbName]; Win32.CertStrToName(X509_ASN_ENCODING, DN, CERT_X500_NAME_STR, IntPtr.Zero, encodedName, ref cbName, IntPtr.Zero); } CERT_NAME_BLOB subjectblob = new CERT_NAME_BLOB(); subjectblob.pbData = Marshal.AllocHGlobal(encodedName.Length); Marshal.Copy(encodedName, 0, subjectblob.pbData, encodedName.Length); subjectblob.cbData = encodedName.Length; CRYPT_KEY_PROV_INFO pInfo = new CRYPT_KEY_PROV_INFO(); pInfo.pwszContainerName = keycontainer; pInfo.pwszProvName = provider; pInfo.dwProvType = PROV_RSA_FULL; pInfo.dwFlags = cspflags; pInfo.cProvParam = 0; pInfo.rgProvParam = IntPtr.Zero; pInfo.dwKeySpec = KEYSPEC; hCertCntxt = Win32.CertCreateSelfSignCertificate(IntPtr.Zero, ref subjectblob, CERT_CREATE_SELFSIGN_NO_SIGN, ref pInfo, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); if (hCertCntxt == IntPtr.Zero) showWin32Error(Marshal.GetLastWin32Error()); Marshal.FreeHGlobal(subjectblob.pbData); return hCertCntxt; }
private static extern int CertSetCertificateContextProperty(IntPtr pCertContext, int dwPropId, int dwFlags, ref CRYPT_KEY_PROV_INFO pvData);
/// <summary> /// Associates the certificate with a private key from a PVK file. /// </summary> /// <param name="pvkFile">The path to the PVK file to open.</param> /// <param name="password">The password used to encrypt the private key.</param> /// <param name="exportable"><b>true</b> if the private key should be marked exportable, <b>false</b> otherwise.</param> /// <remarks>The <paramref name="password"/> can be a null reference (<b>Nothing</b> in Visual Basic) if the private key is not encrypted.</remarks> /// <exception cref="ArgumentNullException">The PVK file is encrypted and <paramref name="password"/> is a null reference (<b>Nothing</b> in Visual Basic).</exception> /// <exception cref="FileNotFoundException">The specified file could not be found.</exception> /// <exception cref="CertificateException">An error occurs while importing the private key.</exception> public void AssociateWithPrivateKey(string pvkFile, string password, bool exportable) { try { if (!File.Exists(pvkFile)) throw new FileNotFoundException("The PVK file could not be found."); } catch (FileNotFoundException fnf) { throw fnf; } catch (Exception e) { throw new FileNotFoundException("The PVK file could not be found.", e); } byte[] buffer = new byte[24]; FileStream fs = File.Open(pvkFile, FileMode.Open, FileAccess.Read, FileShare.Read); fs.Read(buffer, 0, buffer.Length); if (BitConverter.ToUInt32(buffer, 0) != 0xB0B5F11E) throw new CertificateException("The specified file is not a valid PVK file."); int keytype = BitConverter.ToInt32(buffer, 8); int isEncrypted = BitConverter.ToInt32(buffer, 12); int saltLength = BitConverter.ToInt32(buffer, 16); int keyLen = BitConverter.ToInt32(buffer, 20); byte[] salt = new byte[saltLength]; byte[] blob = new byte[keyLen]; fs.Read(salt, 0, salt.Length); fs.Read(blob, 0, blob.Length); if (isEncrypted != 0) { // decrypt private key if (password == null) throw new ArgumentNullException(); byte[] pass = Encoding.ASCII.GetBytes(password); byte[] key = new byte[salt.Length + password.Length]; Array.Copy(salt, 0, key, 0, salt.Length); Array.Copy(pass, 0, key, salt.Length, pass.Length); byte[] pkb = TryDecrypt(blob, 8, blob.Length - 8, key, 16); if (pkb == null) { // decryption failed, try with an export key pkb = TryDecrypt(blob, 8, blob.Length - 8, key, 5); if (pkb == null) { throw new CertificateException("The PVK file could not be decrypted. [wrong password?]"); } } Array.Copy(pkb, 0, blob, 8, pkb.Length); Array.Clear(pkb, 0, pkb.Length); Array.Clear(pass, 0, pass.Length); Array.Clear(key, 0, key.Length); } int hKey = 0, flags = 0; if (exportable) flags = SecurityConstants.CRYPT_EXPORTABLE; int provider = CAPIProvider.ContainerHandle; if (SspiProvider.CryptImportKey(provider, blob, blob.Length, 0, flags, ref hKey) == 0) throw new CertificateException("Could not import the private key from the PVK file."); CRYPT_KEY_PROV_INFO kpi = new CRYPT_KEY_PROV_INFO(); kpi.pwszContainerName = SecurityConstants.KEY_CONTAINER; kpi.pwszProvName = null; kpi.dwProvType = SecurityConstants.PROV_RSA_FULL; kpi.dwFlags = 0; kpi.cProvParam = 0; kpi.rgProvParam = IntPtr.Zero; kpi.dwKeySpec = keytype; if (SspiProvider.CertSetCertificateContextProperty(this.Handle, SecurityConstants.CERT_KEY_PROV_INFO_PROP_ID, 0, ref kpi) == 0) throw new CertificateException("Could not associate the private key with the certificate."); SspiProvider.CryptDestroyKey(hKey); Array.Clear(blob, 0, blob.Length); }
internal static extern bool CertSetCertificateContextProperty( SafeCertContextHandle certificateContext, uint propertyId, uint flags, [In] ref CRYPT_KEY_PROV_INFO data);
/// <summary> /// Create a self-signed x509 certificate. /// </summary> /// <param name="subjectName">The distinguished name.</param> /// <param name="notBefore">The start time.</param> /// <param name="notAfter">the end time.</param> /// <param name="extensions">the extensions.</param> /// <returns>A byte array containing the certificate and private key encoded as PFX.</returns> public static byte[] CreateSelfSignCertificatePfx( string subjectName, DateTime notBefore, DateTime notAfter, params X509Extension[] extensions) { if (subjectName == null) { subjectName = string.Empty; } byte[] pfxData; SYSTEMTIME startSystemTime = ToSystemTime(notBefore); SYSTEMTIME endSystemTime = ToSystemTime(notAfter); string containerName = $"Created by Workstation. {Guid.NewGuid().ToString()}"; GCHandle gcHandle = default(GCHandle); var providerContext = SafeCryptProvHandle.InvalidHandle; var cryptKey = SafeCryptKeyHandle.InvalidHandle; var certContext = SafeCertContextHandle.InvalidHandle; var certStore = SafeCertStoreHandle.InvalidHandle; var storeCertContext = SafeCertContextHandle.InvalidHandle; try { Check(NativeMethods.CryptAcquireContextW( out providerContext, containerName, null, PROV_RSA_FULL, CRYPT_NEWKEYSET)); Check(NativeMethods.CryptGenKey( providerContext, AT_KEYEXCHANGE, CRYPT_EXPORTABLE | (2048 << 16), // 2048bit out cryptKey)); IntPtr pbEncoded = IntPtr.Zero; int cbEncoded = 0; Check(NativeMethods.CertStrToNameW( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, subjectName, CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, IntPtr.Zero, IntPtr.Zero, ref cbEncoded, IntPtr.Zero)); pbEncoded = Marshal.AllocHGlobal(cbEncoded); Check(NativeMethods.CertStrToNameW( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, subjectName, CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, IntPtr.Zero, pbEncoded, ref cbEncoded, IntPtr.Zero)); var nameBlob = new CRYPTOAPI_BLOB { cbData = (uint)cbEncoded, pbData = pbEncoded }; var kpi = new CRYPT_KEY_PROV_INFO { pwszContainerName = containerName, dwProvType = PROV_RSA_FULL, dwKeySpec = AT_KEYEXCHANGE }; var signatureAlgorithm = new CRYPT_ALGORITHM_IDENTIFIER { pszObjId = OID_RSA_SHA256RSA, Parameters = default(CRYPTOAPI_BLOB) }; IntPtr pInfo = IntPtr.Zero; int cbInfo = 0; byte[] keyHash = null; int cbKeyHash = 0; try { Check(NativeMethods.CryptExportPublicKeyInfoEx( providerContext, AT_KEYEXCHANGE, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, OID_RSA_RSA, 0, IntPtr.Zero, IntPtr.Zero, ref cbInfo)); pInfo = Marshal.AllocHGlobal(cbInfo); Check(NativeMethods.CryptExportPublicKeyInfoEx( providerContext, AT_KEYEXCHANGE, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, OID_RSA_RSA, 0, IntPtr.Zero, pInfo, ref cbInfo)); Check(NativeMethods.CryptHashPublicKeyInfo( providerContext, CALG_SHA1, 0, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pInfo, null, ref cbKeyHash)); keyHash = new byte[cbKeyHash]; Check(NativeMethods.CryptHashPublicKeyInfo( providerContext, CALG_SHA1, 0, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pInfo, keyHash, ref cbKeyHash)); } finally { if (pInfo != IntPtr.Zero) { Marshal.FreeHGlobal(pInfo); } } var safeExtensions = new List<SafeX509Extension>(); var blob = IntPtr.Zero; try { foreach (var item in extensions) { safeExtensions.Add(new SafeX509Extension(item)); } // adding SubjectKeyIdentifier TODO: AuthKeyIdentifier? safeExtensions.Add(new SafeX509Extension(new X509SubjectKeyIdentifierExtension(keyHash, false))); var structSize = Marshal.SizeOf<CERT_EXTENSION>(); blob = Marshal.AllocHGlobal(structSize * safeExtensions.Count); for (int index = 0, offset = 0; index < safeExtensions.Count; index++, offset += structSize) { var marshalX509Extension = safeExtensions[index]; Marshal.StructureToPtr(marshalX509Extension.Value, blob + offset, false); } var certExtensions = new CERT_EXTENSIONS { cExtension = (uint)safeExtensions.Count, rgExtension = blob }; certContext = NativeMethods.CertCreateSelfSignCertificate( providerContext, ref nameBlob, 0, ref kpi, ref signatureAlgorithm, ref startSystemTime, ref endSystemTime, ref certExtensions); Check(!certContext.IsInvalid); } finally { foreach (var safeExtension in safeExtensions) { safeExtension.Dispose(); } safeExtensions.Clear(); Marshal.FreeHGlobal(blob); Marshal.FreeHGlobal(pbEncoded); } certStore = NativeMethods.CertOpenStore( sz_CERT_STORE_PROV_MEMORY, 0, IntPtr.Zero, CERT_STORE_CREATE_NEW_FLAG, IntPtr.Zero); Check(!certStore.IsInvalid); Check(NativeMethods.CertAddCertificateContextToStore( certStore, certContext, CERT_STORE_ADD_NEW, out storeCertContext)); NativeMethods.CertSetCertificateContextProperty( storeCertContext, CERT_KEY_PROV_INFO_PROP_ID, 0, ref kpi); CRYPTOAPI_BLOB pfxBlob = default(CRYPTOAPI_BLOB); Check(NativeMethods.PFXExportCertStoreEx( certStore, ref pfxBlob, IntPtr.Zero, IntPtr.Zero, EXPORT_PRIVATE_KEYS | REPORT_NO_PRIVATE_KEY | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY)); pfxData = new byte[pfxBlob.cbData]; gcHandle = GCHandle.Alloc(pfxData, GCHandleType.Pinned); pfxBlob.pbData = gcHandle.AddrOfPinnedObject(); Check(NativeMethods.PFXExportCertStoreEx( certStore, ref pfxBlob, IntPtr.Zero, IntPtr.Zero, EXPORT_PRIVATE_KEYS | REPORT_NO_PRIVATE_KEY | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY)); gcHandle.Free(); } finally { if (gcHandle.IsAllocated) { gcHandle.Free(); } if (!certContext.IsInvalid) { certContext.Dispose(); } if (!storeCertContext.IsInvalid) { storeCertContext.Dispose(); } if (!certStore.IsInvalid) { certStore.Dispose(); } if (!cryptKey.IsInvalid) { cryptKey.Dispose(); } if (!providerContext.IsInvalid) { providerContext.Dispose(); providerContext = SafeCryptProvHandle.InvalidHandle; // Delete generated keyset. Does not return a providerContext NativeMethods.CryptAcquireContextW( out providerContext, containerName, null, PROV_RSA_FULL, CRYPT_DELETEKEYSET); } } return pfxData; }
public IntPtr Import(string password) { var containerName = "CERT"; var context = Context.AcquireContext(containerName); try { var hHash = IntPtr.Zero; // get a symmetric key to decrypt the private key if (NativeMethods.CryptCreateHash(context, Const.CALG_SHA, IntPtr.Zero, 0, out hHash)) { try { if (SaltData.Length > 0) { if (!NativeMethods.CryptHashData(hHash, SaltData, SaltData.Length, 0)) { throw new CryptographicException(Marshal.GetLastWin32Error()); } } var passwordBytes = Encoding.ASCII.GetBytes(password); if (!NativeMethods.CryptHashData(hHash, passwordBytes, passwordBytes.Length, 0)) { throw new CryptographicException(Marshal.GetLastWin32Error()); } var decryptKey = IntPtr.Zero; uint enc = EncryptionType == PVKEncryptionType.RC4Password ? Const.CALG_RC4 : Const.CALG_RC2; if (!NativeMethods.CryptDeriveKey(context, enc, hHash, 0, out decryptKey)) { throw new CryptographicException(Marshal.GetLastWin32Error()); } try { // decrypt and import the private key var importKey = IntPtr.Zero; if (!NativeMethods.CryptImportKey(context, KeyData, (uint)KeyData.Length, decryptKey, Const.CRYPT_EXPORTABLE, out importKey)) { throw new CryptographicException(Marshal.GetLastWin32Error()); } // get the info length int length = 0; if (!NativeMethods.CryptExportPublicKeyInfo(context, KeySpec, Const.CRYPT_ASN_ENCODING, IntPtr.Zero, ref length)) { throw new CryptographicException(Marshal.GetLastWin32Error()); } var pInfo = Marshal.AllocHGlobal(length); try { // get the public key info if (!NativeMethods.CryptExportPublicKeyInfo(context, KeySpec, Const.CRYPT_ASN_ENCODING, pInfo, ref length)) { throw new CryptographicException(Marshal.GetLastWin32Error()); } // find a certificate in the store that matches this private key var pCert = NativeMethods.CertFindCertificateInStore(Store.Handle, Const.CRYPT_ASN_ENCODING, 0, Const.CERT_FIND_PUBLIC_KEY, pInfo, IntPtr.Zero); if (pCert == IntPtr.Zero) { throw new CryptographicException("No Certificate found in store for this Private Key"); } var keyProvInfo = new CRYPT_KEY_PROV_INFO(); keyProvInfo.pwszContainerName = Context.KeyContainer; keyProvInfo.pwszProvName = Context.ProviderName == null ? null : Const.MS_ENHANCED_PROV; keyProvInfo.dwProvType = (int)Const.PROV_RSA_FULL; keyProvInfo.dwFlags = 0; keyProvInfo.cProvParam = 0; keyProvInfo.rgProvParam = IntPtr.Zero; keyProvInfo.dwKeySpec = KeySpec; if (!NativeMethods.CertSetCertificateContextProperty( pCert, Const.CERT_KEY_PROV_INFO_PROP_ID, 0, keyProvInfo)) { throw new CryptographicException(Marshal.GetLastWin32Error()); } return(pCert); } finally { Marshal.FreeHGlobal(pInfo); } } finally { NativeMethods.CryptDestroyKey(decryptKey); } } finally { NativeMethods.CryptDestroyHash(hHash); } } } finally { Context.ReleaseContext(context); } return(IntPtr.Zero); }
internal static SafeCertContextHandle CreateSelfSignedCertificate(CngKey key, bool takeOwnershipOfKey, byte[] subjectName, X509CertificateCreationOptions creationOptions, string signatureAlgorithmOid, DateTime startTime, DateTime endTime, X509ExtensionCollection extensions) { Debug.Assert(key != null, "key != null"); Debug.Assert(subjectName != null, "subjectName != null"); Debug.Assert(!String.IsNullOrEmpty(signatureAlgorithmOid), "!String.IsNullOrEmpty(signatureAlgorithmOid)"); Debug.Assert(extensions != null, "extensions != null"); // Create an algorithm identifier structure for the signature algorithm CapiNative.CRYPT_ALGORITHM_IDENTIFIER nativeSignatureAlgorithm = new CapiNative.CRYPT_ALGORITHM_IDENTIFIER(); nativeSignatureAlgorithm.pszObjId = signatureAlgorithmOid; nativeSignatureAlgorithm.Parameters = new CapiNative.CRYPTOAPI_BLOB(); nativeSignatureAlgorithm.Parameters.cbData = 0; nativeSignatureAlgorithm.Parameters.pbData = IntPtr.Zero; // Convert the begin and expire dates to system time structures Win32Native.SYSTEMTIME nativeStartTime = new Win32Native.SYSTEMTIME(startTime); Win32Native.SYSTEMTIME nativeEndTime = new Win32Native.SYSTEMTIME(endTime); // Map the extensions into CERT_EXTENSIONS. This involves several steps to get the // CERT_EXTENSIONS ready for interop with the native APIs. // 1. Build up the CERT_EXTENSIONS structure in managed code // 2. For each extension, create a managed CERT_EXTENSION structure; this requires allocating // native memory for the blob pointer in the CERT_EXTENSION. These extensions are stored in // the nativeExtensionArray variable. // 3. Get a block of native memory that can hold a native array of CERT_EXTENSION structures. // This is the block referenced by the CERT_EXTENSIONS structure. // 4. For each of the extension structures created in step 2, marshal the extension into the // native buffer allocated in step 3. CERT_EXTENSIONS nativeExtensions = new CERT_EXTENSIONS(); nativeExtensions.cExtension = extensions.Count; CERT_EXTENSION[] nativeExtensionArray = new CERT_EXTENSION[extensions.Count]; // Run this in a CER to ensure that we release any native memory allocated for the certificate // extensions. RuntimeHelpers.PrepareConstrainedRegions(); try { // Copy over each extension into a native extension structure, including allocating native // memory for its blob if necessary. for (int i = 0; i < extensions.Count; ++i) { nativeExtensionArray[i] = new CERT_EXTENSION(); nativeExtensionArray[i].pszObjId = extensions[i].Oid.Value; nativeExtensionArray[i].fCritical = extensions[i].Critical; nativeExtensionArray[i].Value = new CapiNative.CRYPTOAPI_BLOB(); nativeExtensionArray[i].Value.cbData = extensions[i].RawData.Length; if (nativeExtensionArray[i].Value.cbData > 0) { nativeExtensionArray[i].Value.pbData = Marshal.AllocCoTaskMem(nativeExtensionArray[i].Value.cbData); Marshal.Copy(extensions[i].RawData, 0, nativeExtensionArray[i].Value.pbData, nativeExtensionArray[i].Value.cbData); } } // Now that we've built up the extension array, create a block of native memory to marshal // them into. if (nativeExtensionArray.Length > 0) { checked { // CERT_EXTENSION structures end with a pointer field, which means on all supported // platforms they won't require any padding between elements of the array. nativeExtensions.rgExtension = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(CERT_EXTENSION)) * nativeExtensionArray.Length); for (int i = 0; i < nativeExtensionArray.Length; ++i) { ulong offset = (uint)i * (uint)Marshal.SizeOf(typeof(CERT_EXTENSION)); ulong next = offset + (ulong)nativeExtensions.rgExtension.ToInt64(); IntPtr nextExtensionAddr = new IntPtr((long)next); Marshal.StructureToPtr(nativeExtensionArray[i], nextExtensionAddr, false); } } } // Setup a CRYPT_KEY_PROV_INFO for the key CRYPT_KEY_PROV_INFO keyProvInfo = new CRYPT_KEY_PROV_INFO(); keyProvInfo.pwszContainerName = key.UniqueName; keyProvInfo.pwszProvName = key.Provider.Provider; keyProvInfo.dwProvType = 0; // NCRYPT keyProvInfo.dwFlags = 0; keyProvInfo.cProvParam = 0; keyProvInfo.rgProvParam = IntPtr.Zero; keyProvInfo.dwKeySpec = 0; // // Now that all of the needed data structures are setup, we can create the certificate // SafeCertContextHandle selfSignedCertHandle = null; unsafe { fixed(byte *pSubjectName = &subjectName[0]) { // Create a CRYPTOAPI_BLOB for the subject of the cert CapiNative.CRYPTOAPI_BLOB nativeSubjectName = new CapiNative.CRYPTOAPI_BLOB(); nativeSubjectName.cbData = subjectName.Length; nativeSubjectName.pbData = new IntPtr(pSubjectName); // Now that we've converted all the inputs to native data structures, we can generate // the self signed certificate for the input key. using (SafeNCryptKeyHandle keyHandle = key.Handle) { selfSignedCertHandle = UnsafeNativeMethods.CertCreateSelfSignCertificate(keyHandle, ref nativeSubjectName, creationOptions, ref keyProvInfo, ref nativeSignatureAlgorithm, ref nativeStartTime, ref nativeEndTime, ref nativeExtensions); if (selfSignedCertHandle.IsInvalid) { throw new CryptographicException(Marshal.GetLastWin32Error()); } } } } Debug.Assert(selfSignedCertHandle != null, "selfSignedCertHandle != null"); // Attach a key context to the certificate which will allow Windows to find the private key // associated with the certificate if the NCRYPT_KEY_HANDLE is ephemeral. // is done. using (SafeNCryptKeyHandle keyHandle = key.Handle) { CERT_KEY_CONTEXT keyContext = new CERT_KEY_CONTEXT(); keyContext.cbSize = Marshal.SizeOf(typeof(CERT_KEY_CONTEXT)); keyContext.hNCryptKey = keyHandle.DangerousGetHandle(); keyContext.dwKeySpec = KeySpec.NCryptKey; bool attachedProperty = false; int setContextError = 0; // Run in a CER to ensure accurate tracking of the transfer of handle ownership RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { CertificatePropertySetFlags flags = CertificatePropertySetFlags.None; if (!takeOwnershipOfKey) { // If the certificate is not taking ownership of the key handle, then it should // not release the handle when the context is released. flags |= CertificatePropertySetFlags.NoCryptRelease; } attachedProperty = UnsafeNativeMethods.CertSetCertificateContextProperty(selfSignedCertHandle, CertificateProperty.KeyContext, flags, ref keyContext); setContextError = Marshal.GetLastWin32Error(); // If we succesfully transferred ownership of the key to the certificate, // then we need to ensure that we no longer release its handle. if (attachedProperty && takeOwnershipOfKey) { keyHandle.SetHandleAsInvalid(); } } if (!attachedProperty) { throw new CryptographicException(setContextError); } } return(selfSignedCertHandle); } finally { // // In order to release all resources held by the CERT_EXTENSIONS we need to do three things // 1. Destroy each structure marshaled into the native CERT_EXTENSION array // 2. Release the memory used for the CERT_EXTENSION array // 3. Release the memory used in each individual CERT_EXTENSION // // Release each extension marshaled into the native buffer as well if (nativeExtensions.rgExtension != IntPtr.Zero) { for (int i = 0; i < nativeExtensionArray.Length; ++i) { ulong offset = (uint)i * (uint)Marshal.SizeOf(typeof(CERT_EXTENSION)); ulong next = offset + (ulong)nativeExtensions.rgExtension.ToInt64(); IntPtr nextExtensionAddr = new IntPtr((long)next); Marshal.DestroyStructure(nextExtensionAddr, typeof(CERT_EXTENSION)); } Marshal.FreeCoTaskMem(nativeExtensions.rgExtension); } // If we allocated memory for any extensions, make sure to free it now for (int i = 0; i < nativeExtensionArray.Length; ++i) { if (nativeExtensionArray[i].Value.pbData != IntPtr.Zero) { Marshal.FreeCoTaskMem(nativeExtensionArray[i].Value.pbData); } } } }
public static void Register(X509Certificate2 cert, CertificateContext context) { IntPtr hCertContext = new IntPtr(); hCertContext = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, cert.RawData,(uint) cert.RawData.Length); CRYPT_KEY_PROV_INFO ProvInfo = new CRYPT_KEY_PROV_INFO(); ProvInfo.ContainerName = context.containerName; ProvInfo.ProvName = context.providerName; ProvInfo.ProvType = PROV_RSA_FULL; ProvInfo.Flags = 0; ProvInfo.KeySpec = context.keySpec; ProvInfo.ProvParam = 0; ProvInfo.rgProvParam = new IntPtr(); if (!CertSetCertificateContextProperty(hCertContext, CERT_KEY_PROV_INFO_PROP_ID, 0, ref ProvInfo)) throw new Exception("Could set certificate's context."); Debug.WriteLine("Context set!"); IntPtr hCertStore = CertOpenStoreStringPara(CERT_STORE_PROV_SYSTEM_W, 0, new IntPtr(), CERT_STORE_OPEN_EXISTING_FLAG | CERT_SYSTEM_STORE_CURRENT_USER, "My"); if (!CertAddCertificateContextToStore(hCertStore, hCertContext, CERT_STORE_ADD_REPLACE_EXISTING, new IntPtr())) throw new Exception("Could not add certificate to store."); }
internal static extern bool CertSetCertificateContextProperty(SafeCertContextHandle pCertContext, CertificateProperty dwPropId, CertSetPropertyFlags dwFlags, [In] ref CRYPT_KEY_PROV_INFO pvData);
public static void RegisterKeyContainer() { uint[] keySpecs = { AT_SIGNATURE, AT_KEYEXCHANGE }; string providerName = "Microsoft Base Smart Card Crypto Provider"; IntPtr hCryptProvider = new IntPtr(); if (!CryptAcquireContext(ref hCryptProvider, null, providerName, PROV_RSA_FULL, (uint)0)) throw new RegisterException(new RegisterExceptionEventArgs(RegisterException.ACQUIRE_CONTEXT)); uint bufferSize = 1024; StringBuilder containerName = new StringBuilder((int)bufferSize); // enumerate all containers while (CryptGetProvParam(hCryptProvider, PP_ENUMCONTAINERS, containerName, ref bufferSize, CRYPT_FIRST)) { Debug.WriteLine("Container Name: " + containerName.ToString()); if (!CryptAcquireContext(ref hCryptProvider, containerName.ToString(), providerName, PROV_RSA_FULL, 0)) continue; // loop over the key specs foreach (uint keySpec in keySpecs) { IntPtr hUserKey = new IntPtr(); uint certLen = 0; if (!CryptGetUserKey(hCryptProvider, keySpec, ref hUserKey)) continue; if (!CryptGetKeyParam(hUserKey, KP_CERTIFICATE, 0, ref certLen, 0)) // get certificate length throw new Exception("Could not retrieve certificate length."); Debug.WriteLine("got certificate length"); byte[] rawCert = new byte[certLen]; if (!CryptGetKeyParam(hUserKey, KP_CERTIFICATE, rawCert, ref certLen, 0)) // get certificate throw new Exception("Could not retrieve certificate."); Debug.WriteLine("got certificate"); IntPtr hCertContext = new IntPtr(); hCertContext = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, rawCert, certLen); CRYPT_KEY_PROV_INFO ProvInfo = new CRYPT_KEY_PROV_INFO(); ProvInfo.ContainerName = containerName.ToString(); ProvInfo.ProvName = providerName; ProvInfo.ProvType = PROV_RSA_FULL; ProvInfo.Flags = 0; ProvInfo.KeySpec = AT_SIGNATURE; ProvInfo.ProvParam = 0; ProvInfo.rgProvParam = new IntPtr(); X509Certificate2 c = new X509Certificate2(rawCert); string name = c.SubjectName.Name; if (!CertSetCertificateContextProperty(hCertContext, CERT_KEY_PROV_INFO_PROP_ID, 0, ref ProvInfo)) throw new Exception("Could set certificate's context."); Debug.WriteLine("Context set!"); IntPtr hCertStore = CertOpenStoreStringPara(CERT_STORE_PROV_SYSTEM_W, 0, new IntPtr(), CERT_STORE_OPEN_EXISTING_FLAG | CERT_SYSTEM_STORE_CURRENT_USER, "My"); if (!CertAddCertificateContextToStore(hCertStore, hCertContext, CERT_STORE_ADD_REPLACE_EXISTING, new IntPtr())) throw new Exception("Could not add certificate to store."); } } }
private CriticalAllocHandle GetProviderInfo() { CRYPT_KEY_PROV_INFO id = new CRYPT_KEY_PROV_INFO { container = this.keyContainerName, providerType = 12, paramsCount = 0, keySpec = 1 }; return CriticalAllocHandleBlob.FromBlob<CRYPT_KEY_PROV_INFO>(id); }
private static SafeCertContextHandle CreateSelfSignedCertificate(CngKey key, bool takeOwnershipOfKey, byte[] subjectName, X509CertificateCreationOptions creationOptions, string signatureAlgorithmOid, DateTime startTime, DateTime endTime) { // Create an algorithm identifier structure for the signature algorithm CRYPT_ALGORITHM_IDENTIFIER nativeSignatureAlgorithm = new CRYPT_ALGORITHM_IDENTIFIER(); nativeSignatureAlgorithm.pszObjId = signatureAlgorithmOid; nativeSignatureAlgorithm.Parameters = new CRYPTOAPI_BLOB(); nativeSignatureAlgorithm.Parameters.cbData = 0; nativeSignatureAlgorithm.Parameters.pbData = IntPtr.Zero; // Convert the begin and expire dates to system time structures SYSTEMTIME nativeStartTime = new SYSTEMTIME(startTime); SYSTEMTIME nativeEndTime = new SYSTEMTIME(endTime); CERT_EXTENSIONS nativeExtensions = new CERT_EXTENSIONS(); nativeExtensions.cExtension = 0; // Setup a CRYPT_KEY_PROV_INFO for the key CRYPT_KEY_PROV_INFO keyProvInfo = new CRYPT_KEY_PROV_INFO(); keyProvInfo.pwszContainerName = key.UniqueName; keyProvInfo.pwszProvName = key.Provider.Provider; keyProvInfo.dwProvType = 0; // NCRYPT keyProvInfo.dwFlags = 0; keyProvInfo.cProvParam = 0; keyProvInfo.rgProvParam = IntPtr.Zero; keyProvInfo.dwKeySpec = 0; // // Now that all of the needed data structures are setup, we can create the certificate // SafeCertContextHandle selfSignedCertHandle = null; unsafe { fixed(byte *pSubjectName = &subjectName[0]) { // Create a CRYPTOAPI_BLOB for the subject of the cert CRYPTOAPI_BLOB nativeSubjectName = new CRYPTOAPI_BLOB(); nativeSubjectName.cbData = subjectName.Length; nativeSubjectName.pbData = new IntPtr(pSubjectName); // Now that we've converted all the inputs to native data structures, we can generate // the self signed certificate for the input key. using (SafeNCryptKeyHandle keyHandle = key.Handle) { selfSignedCertHandle = CertCreateSelfSignCertificate(keyHandle, ref nativeSubjectName, creationOptions, ref keyProvInfo, ref nativeSignatureAlgorithm, ref nativeStartTime, ref nativeEndTime, ref nativeExtensions); if (selfSignedCertHandle.IsInvalid) { throw new CryptographicException(Marshal.GetLastWin32Error()); } } } } Debug.Assert(selfSignedCertHandle != null, "selfSignedCertHandle != null"); // Attach a key context to the certificate which will allow Windows to find the private key // associated with the certificate if the NCRYPT_KEY_HANDLE is ephemeral. // is done. using (SafeNCryptKeyHandle keyHandle = key.Handle) { CERT_KEY_CONTEXT keyContext = new CERT_KEY_CONTEXT(); keyContext.cbSize = Marshal.SizeOf(typeof(CERT_KEY_CONTEXT)); keyContext.hNCryptKey = keyHandle.DangerousGetHandle(); keyContext.dwKeySpec = KeySpec.NCryptKey; bool attachedProperty = false; int setContextError = 0; // Run in a CER to ensure accurate tracking of the transfer of handle ownership RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { CertificatePropertySetFlags flags = CertificatePropertySetFlags.None; if (!takeOwnershipOfKey) { // If the certificate is not taking ownership of the key handle, then it should // not release the handle when the context is released. flags |= CertificatePropertySetFlags.NoCryptRelease; } attachedProperty = CertSetCertificateContextProperty(selfSignedCertHandle, CertificateProperty.KeyContext, flags, ref keyContext); setContextError = Marshal.GetLastWin32Error(); // If we succesfully transferred ownership of the key to the certificate, // then we need to ensure that we no longer release its handle. if (attachedProperty && takeOwnershipOfKey) { keyHandle.SetHandleAsInvalid(); } } if (!attachedProperty) { throw new CryptographicException(setContextError); } } return(selfSignedCertHandle); }
// begin: gost public unsafe void SetCspPrivateKey(AsymmetricAlgorithm key) { if (key == null) { return; } CspKeyContainerInfo keyContainerInfo; switch (key.SignatureAlgorithm) { case GostConstants.XmlSignatureAlgorithm2001: { Gost3410CryptoServiceProvider asymmetricAlgorithm = key as Gost3410CryptoServiceProvider; keyContainerInfo = asymmetricAlgorithm.CspKeyContainerInfo; break; } case GostConstants.XmlSignatureAlgorithm2012_256: { Gost3410_2012_256CryptoServiceProvider asymmetricAlgorithm = key as Gost3410_2012_256CryptoServiceProvider; keyContainerInfo = asymmetricAlgorithm.CspKeyContainerInfo; break; } case GostConstants.XmlSignatureAlgorithm2012_512: { Gost3410_2012_512CryptoServiceProvider asymmetricAlgorithm = key as Gost3410_2012_512CryptoServiceProvider; keyContainerInfo = asymmetricAlgorithm.CspKeyContainerInfo; break; } case "RSA": { RSACryptoServiceProvider asymmetricAlgorithm = key as RSACryptoServiceProvider; keyContainerInfo = asymmetricAlgorithm.CspKeyContainerInfo; break; } case "DSA": { DSACryptoServiceProvider asymmetricAlgorithm = key as DSACryptoServiceProvider; keyContainerInfo = asymmetricAlgorithm.CspKeyContainerInfo; break; } default: { throw new PlatformNotSupportedException(); } } SafeLocalAllocHandle ptr = SafeLocalAllocHandle.InvalidHandle; fixed(char *keyContainerName = keyContainerInfo.KeyContainerName) fixed(char *providerName = keyContainerInfo.ProviderName) { CRYPT_KEY_PROV_INFO keyProvInfo = new CRYPT_KEY_PROV_INFO(); keyProvInfo.pwszContainerName = keyContainerName; keyProvInfo.pwszProvName = providerName; keyProvInfo.dwProvType = keyContainerInfo.ProviderType; keyProvInfo.dwFlags = keyContainerInfo.MachineKeyStore ? CryptAcquireContextFlags.CRYPT_MACHINE_KEYSET : CryptAcquireContextFlags.None; keyProvInfo.cProvParam = 0; keyProvInfo.rgProvParam = IntPtr.Zero; keyProvInfo.dwKeySpec = (int)keyContainerInfo.KeyNumber; if (!Interop.crypt32.CertSetCertificateContextProperty( _certContext, CertContextPropId.CERT_KEY_PROV_INFO_PROP_ID, CertSetPropertyFlags.None, &keyProvInfo)) { throw Marshal.GetLastWin32Error().ToCryptographicException(); } } }
/// <summary> /// Associates this private key instance with the corresponding X509 certificate. /// </summary> /// <param name="certificate">An <see cref="X509Certificate"/> instance to associate the key with.</param> /// <param name="exportable"><b>true</b> if the key should be marked as exportable, <b>false</b> otherwise.</param> /// <exception cref="CryptographicException">An error occurs while associating the private key with the certificate.</exception> public void AssociateWith(X509Certificate certificate, bool exportable) { if (certificate == null) throw new ArgumentNullException("certificate"); IntPtr hKey = IntPtr.Zero; int flags = 0; if (exportable) flags = CRYPT_EXPORTABLE; using (CryptoProviderHandle provider = new CryptoProviderHandle()) { if (CryptImportKey(provider.Handle, _blob, _blob.Length, IntPtr.Zero, flags, ref hKey) == 0) throw new CryptographicException("Could not import the private key from the PVK file."); CRYPT_KEY_PROV_INFO kpi = new CRYPT_KEY_PROV_INFO(); kpi.pwszContainerName = provider.Container; kpi.pwszProvName = null; kpi.dwProvType = provider.ProviderType; kpi.dwFlags = 0; kpi.cProvParam = 0; kpi.rgProvParam = IntPtr.Zero; kpi.dwKeySpec = _keyType; if (CertSetCertificateContextProperty(certificate.Handle, CERT_KEY_PROV_INFO_PROP_ID, 0, ref kpi) == 0) throw new CryptographicException("Could not associate the private key with the certificate."); CryptDestroyKey(hKey); } }