// // This returns an allocated native memory block. Its lifetime (and that of any allocated subblocks it may point to) is that of "hb". // private static unsafe CMSG_KEY_TRANS_RECIPIENT_ENCODE_INFO *EncodeKeyTransRecipientInfo(CmsRecipient recipient, HeapBlockRetainer hb) { // "recipient" is a deep-cloned CmsRecipient object whose lifetime this class controls. Because of this, we can pull out the CERT_CONTEXT* and CERT_INFO* pointers // and embed pointers to them in the memory block we return. Yes, this code is scary. // // (The use of SafeCertContextHandle here is about using a consistent pattern to get the CERT_CONTEXT (rather than the ugly (CERT_CONTEXT*)(recipient.Certificate.Handle) pattern.) // It's not about keeping the context alive.) using (SafeCertContextHandle hCertContext = recipient.Certificate.CreateCertContextHandle()) { CERT_CONTEXT *pCertContext = hCertContext.DangerousGetCertContext(); CERT_INFO * pCertInfo = pCertContext->pCertInfo; CMSG_KEY_TRANS_RECIPIENT_ENCODE_INFO *pEncodeInfo = (CMSG_KEY_TRANS_RECIPIENT_ENCODE_INFO *)(hb.Alloc(sizeof(CMSG_KEY_TRANS_RECIPIENT_ENCODE_INFO))); pEncodeInfo->cbSize = sizeof(CMSG_KEY_TRANS_RECIPIENT_ENCODE_INFO); CRYPT_ALGORITHM_IDENTIFIER algId = pCertInfo->SubjectPublicKeyInfo.Algorithm; pEncodeInfo->KeyEncryptionAlgorithm = algId; pEncodeInfo->pvKeyEncryptionAuxInfo = IntPtr.Zero; pEncodeInfo->hCryptProv = IntPtr.Zero; pEncodeInfo->RecipientPublicKey = pCertInfo->SubjectPublicKeyInfo.PublicKey; pEncodeInfo->RecipientId = EncodeRecipientId(recipient, hCertContext, pCertContext, pCertInfo, hb); return(pEncodeInfo); } }
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);
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 static extern bool CryptSignAndEncodeCertificate(IntPtr hCryptProvOrNCryptKey, uint dwKeySpec, uint dwCertEncodingType, ulong lpszStructType, IntPtr pvStructInfo, ref CRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm, IntPtr pvHashAuxInfo, byte[] pbEncoded, ref uint pcbEncoded);
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 );
public static extern bool CryptSignAndEncodeCertificate( SafeCryptProviderHandle hCryptProv, CALG dwKeySpec, CertEncoding dwCertEncodingType, IntPtr lpszStructType, IntPtr pvStructInfo, CRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm, IntPtr pvHashAuxInfo, byte[] pbEncoded, ref uint pcbEncoded );
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); } } }
/// <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 AlgorithmIdentifier ToAlgorithmIdentifier(this CRYPT_ALGORITHM_IDENTIFIER cryptAlgorithmIdentifer) { string oidValue = cryptAlgorithmIdentifer.pszObjId.ToStringAnsi(); AlgId algId = oidValue.ToAlgId(); int keyLength; switch (algId) { case AlgId.CALG_RC2: { if (cryptAlgorithmIdentifer.Parameters.cbData == 0) { keyLength = 0; } else { CRYPT_RC2_CBC_PARAMETERS rc2Parameters; unsafe { int cbSize = sizeof(CRYPT_RC2_CBC_PARAMETERS); if (!Interop.Crypt32.CryptDecodeObject(CryptDecodeObjectStructType.PKCS_RC2_CBC_PARAMETERS, cryptAlgorithmIdentifer.Parameters.pbData, (int)(cryptAlgorithmIdentifer.Parameters.cbData), &rc2Parameters, ref cbSize)) { throw Interop.CPError.GetLastWin32Error().ToCryptographicException(); } } switch (rc2Parameters.dwVersion) { case CryptRc2Version.CRYPT_RC2_40BIT_VERSION: keyLength = KeyLengths.Rc2_40Bit; break; case CryptRc2Version.CRYPT_RC2_56BIT_VERSION: keyLength = KeyLengths.Rc2_56Bit; break; case CryptRc2Version.CRYPT_RC2_64BIT_VERSION: keyLength = KeyLengths.Rc2_64Bit; break; case CryptRc2Version.CRYPT_RC2_128BIT_VERSION: keyLength = KeyLengths.Rc2_128Bit; break; default: keyLength = 0; break; } } break; } case AlgId.CALG_RC4: { int saltLength = 0; if (cryptAlgorithmIdentifer.Parameters.cbData != 0) { using (SafeHandle sh = Interop.Crypt32.CryptDecodeObjectToMemory(CryptDecodeObjectStructType.X509_OCTET_STRING, cryptAlgorithmIdentifer.Parameters.pbData, (int)cryptAlgorithmIdentifer.Parameters.cbData)) { unsafe { DATA_BLOB *pDataBlob = (DATA_BLOB *)(sh.DangerousGetHandle()); saltLength = (int)(pDataBlob->cbData); } } } // For RC4, keyLength = 128 - (salt length * 8). keyLength = KeyLengths.Rc4Max_128Bit - saltLength * 8; break; } case AlgId.CALG_DES: // DES key length is fixed at 64 (or 56 without the parity bits). keyLength = KeyLengths.Des_64Bit; break; case AlgId.CALG_3DES: // 3DES key length is fixed at 192 (or 168 without the parity bits). keyLength = KeyLengths.TripleDes_192Bit; break; default: // We've exhausted all the algorithm types that the desktop used to set the KeyLength for. Key lengths are not a viable way of // identifying algorithms in the long run so we will not extend this list any further. keyLength = 0; break; } AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Oid.FromOidValue(oidValue, OidGroup.All), keyLength); switch (oidValue) { case Oids.RsaOaep: algorithmIdentifier.Parameters = cryptAlgorithmIdentifer.Parameters.ToByteArray(); break; } return(algorithmIdentifier); }
/// <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 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); }
internal CMSG_ENVELOPED_ENCODE_INFO(int size) { cbSize = (uint) size; hCryptProv = IntPtr.Zero; ContentEncryptionAlgorithm = new CRYPT_ALGORITHM_IDENTIFIER(); pvEncryptionAuxInfo = IntPtr.Zero; cRecipients = 0; rgpRecipients = IntPtr.Zero; rgCmsRecipients = IntPtr.Zero; cCertEncoded = 0; rgCertEncoded = IntPtr.Zero; cCrlEncoded = 0; rgCrlEncoded = IntPtr.Zero; cAttrCertEncoded = 0; rgAttrCertEncoded = IntPtr.Zero; cUnprotectedAttr = 0; rgUnprotectedAttr = IntPtr.Zero; }
internal CMSG_SIGNER_ENCODE_INFO(int size) { cbSize = (uint) size; pCertInfo = IntPtr.Zero; hCryptProv = IntPtr.Zero; dwKeySpec = 0; HashAlgorithm = new CRYPT_ALGORITHM_IDENTIFIER(); pvHashAuxInfo = IntPtr.Zero; cAuthAttr = 0; rgAuthAttr = IntPtr.Zero; cUnauthAttr = 0; rgUnauthAttr = IntPtr.Zero; SignerId = new CERT_ID(); HashEncryptionAlgorithm = new CRYPT_ALGORITHM_IDENTIFIER(); pvHashEncryptionAuxInfo = IntPtr.Zero; }
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; }
// // This returns an allocated native memory block. Its lifetime (and that of any allocated subblocks it may point to) is that of "hb". // private static unsafe CMSG_KEY_TRANS_RECIPIENT_ENCODE_INFO *EncodeKeyTransRecipientInfo(CmsRecipient recipient, HeapBlockRetainer hb) { // "recipient" is a deep-cloned CmsRecipient object whose lifetime this class controls. Because of this, we can pull out the CERT_CONTEXT* and CERT_INFO* pointers // and embed pointers to them in the memory block we return. Yes, this code is scary. // // (The use of SafeCertContextHandle here is about using a consistent pattern to get the CERT_CONTEXT (rather than the ugly (CERT_CONTEXT*)(recipient.Certificate.Handle) pattern.) // It's not about keeping the context alive.) using (SafeCertContextHandle hCertContext = recipient.Certificate.CreateCertContextHandle()) { CERT_CONTEXT *pCertContext = hCertContext.DangerousGetCertContext(); CERT_INFO * pCertInfo = pCertContext->pCertInfo; CMSG_KEY_TRANS_RECIPIENT_ENCODE_INFO *pEncodeInfo = (CMSG_KEY_TRANS_RECIPIENT_ENCODE_INFO *)(hb.Alloc(sizeof(CMSG_KEY_TRANS_RECIPIENT_ENCODE_INFO))); pEncodeInfo->cbSize = sizeof(CMSG_KEY_TRANS_RECIPIENT_ENCODE_INFO); if (recipient.RSAEncryptionPadding is null) { CRYPT_ALGORITHM_IDENTIFIER algId = pCertInfo->SubjectPublicKeyInfo.Algorithm; pEncodeInfo->KeyEncryptionAlgorithm = algId; } else if (recipient.RSAEncryptionPadding == RSAEncryptionPadding.Pkcs1) { pEncodeInfo->KeyEncryptionAlgorithm.pszObjId = hb.AllocAsciiString(Oids.Rsa); pEncodeInfo->KeyEncryptionAlgorithm.Parameters.cbData = (uint)s_rsaPkcsParameters.Length; pEncodeInfo->KeyEncryptionAlgorithm.Parameters.pbData = hb.AllocBytes(s_rsaPkcsParameters); } else if (recipient.RSAEncryptionPadding == RSAEncryptionPadding.OaepSHA1) { pEncodeInfo->KeyEncryptionAlgorithm.pszObjId = hb.AllocAsciiString(Oids.RsaOaep); pEncodeInfo->KeyEncryptionAlgorithm.Parameters.cbData = (uint)s_rsaOaepSha1Parameters.Length; pEncodeInfo->KeyEncryptionAlgorithm.Parameters.pbData = hb.AllocBytes(s_rsaOaepSha1Parameters); } else if (recipient.RSAEncryptionPadding == RSAEncryptionPadding.OaepSHA256) { pEncodeInfo->KeyEncryptionAlgorithm.pszObjId = hb.AllocAsciiString(Oids.RsaOaep); pEncodeInfo->KeyEncryptionAlgorithm.Parameters.cbData = (uint)s_rsaOaepSha256Parameters.Length; pEncodeInfo->KeyEncryptionAlgorithm.Parameters.pbData = hb.AllocBytes(s_rsaOaepSha256Parameters); } else if (recipient.RSAEncryptionPadding == RSAEncryptionPadding.OaepSHA384) { pEncodeInfo->KeyEncryptionAlgorithm.pszObjId = hb.AllocAsciiString(Oids.RsaOaep); pEncodeInfo->KeyEncryptionAlgorithm.Parameters.cbData = (uint)s_rsaOaepSha384Parameters.Length; pEncodeInfo->KeyEncryptionAlgorithm.Parameters.pbData = hb.AllocBytes(s_rsaOaepSha384Parameters); } else if (recipient.RSAEncryptionPadding == RSAEncryptionPadding.OaepSHA512) { pEncodeInfo->KeyEncryptionAlgorithm.pszObjId = hb.AllocAsciiString(Oids.RsaOaep); pEncodeInfo->KeyEncryptionAlgorithm.Parameters.cbData = (uint)s_rsaOaepSha512Parameters.Length; pEncodeInfo->KeyEncryptionAlgorithm.Parameters.pbData = hb.AllocBytes(s_rsaOaepSha512Parameters); } else { throw ErrorCode.CRYPT_E_UNKNOWN_ALGO.ToCryptographicException(); } pEncodeInfo->pvKeyEncryptionAuxInfo = IntPtr.Zero; pEncodeInfo->hCryptProv = IntPtr.Zero; pEncodeInfo->RecipientPublicKey = pCertInfo->SubjectPublicKeyInfo.PublicKey; pEncodeInfo->RecipientId = EncodeRecipientId(recipient, hCertContext, pCertContext, pCertInfo, hb); return(pEncodeInfo); } }
/***************************************************************************** * wmain * *****************************************************************************/ static int Main(string[] args) { HRESULT hr = HRESULT.S_OK; SafeHCERTSTORE hStoreHandle = default; string wszStoreName = "MY"; // by default, MY string wszContainerName = "SAMPLE"; uint dwBits = 0; string wszKeyAlgName = "RSA"; // string[] rgwszCNGAlgs = new string[] { "SHA1", "RSA" }; SafeNCRYPT_KEY_HANDLE hCNGKey = default; SafePCCERT_CONTEXT pCertContext = default; CRYPTOAPI_BLOB SubjectName = default; int i; // // options // for (i = 0; i < args.Length; i++) { if (string.Compare(args[i], "/?") == 0 || string.Compare(args[i], "-?") == 0) { Usage("CreateCert.exe"); goto CleanUp; } if (args[i][0] != '-') { break; } if (string.Compare(args[i], "-s") == 0) { if (i + 1 >= args.Length) { hr = HRESULT.E_INVALIDARG; goto CleanUp; } wszStoreName = args[++i]; } else if (string.Compare(args[i], "-c") == 0) { if (i + 1 >= args.Length) { hr = HRESULT.E_INVALIDARG; goto CleanUp; } wszContainerName = args[++i]; } else if (string.Compare(args[i], "-k") == 0) { if (i + 1 >= args.Length) { hr = HRESULT.E_INVALIDARG; goto CleanUp; } wszKeyAlgName = args[++i]; } else if (string.Compare(args[i], "-h") == 0) { if (i + 1 >= args.Length) { hr = HRESULT.E_INVALIDARG; goto CleanUp; } rgwszCNGAlgs[0] = args[++i]; } else if (string.Compare(args[i], "-l") == 0) { if (i + 1 >= args.Length) { hr = HRESULT.E_INVALIDARG; goto CleanUp; } dwBits = uint.Parse(args[++i]); } } if (i >= args.Length) { hr = HRESULT.E_INVALIDARG; goto CleanUp; } var wszSubject = args[i]; // // Find the Signature algorithm // var pOidInfo = CryptFindOIDInfo(CryptOIDInfoFlags.CRYPT_OID_INFO_NAME_KEY, wszKeyAlgName, OIDGroupId.CRYPT_PUBKEY_ALG_OID_GROUP_ID); if (default == pOidInfo) { Console.Write("FAILED: Unable to find Public Key algorithm: '{0}'.\n", wszKeyAlgName); hr = HRESULT.CRYPT_E_UNKNOWN_ALGO; goto CleanUp; } var oidInfo = (CRYPT_OID_INFO)pOidInfo; if (!string.IsNullOrEmpty(oidInfo.pwszCNGExtraAlgid)) { rgwszCNGAlgs[1] = oidInfo.pwszCNGExtraAlgid; } else { rgwszCNGAlgs[1] = oidInfo.pwszCNGAlgid; } using (var pAlgs = SafeLocalHandle.CreateFromStringList(rgwszCNGAlgs, StringListPackMethod.Packed, CharSet.Unicode)) pOidInfo = CryptFindOIDInfo(CryptOIDInfoFlags.CRYPT_OID_INFO_CNG_SIGN_KEY, pAlgs, OIDGroupId.CRYPT_SIGN_ALG_OID_GROUP_ID); if (default == pOidInfo) { Console.Write("FAILED: Unable to find signature algorithm: '{0}:{1}'\n", rgwszCNGAlgs[0], rgwszCNGAlgs[1]); hr = HRESULT.CRYPT_E_UNKNOWN_ALGO; goto CleanUp; } var SignatureAlgorithm = new CRYPT_ALGORITHM_IDENTIFIER { pszObjId = ((CRYPT_OID_INFO)pOidInfo).pszOID }; //------------------------------------------------------------------- // Open a system store, in this case, the My store. hStoreHandle = CertOpenStore(CertStoreProvider.CERT_STORE_PROV_SYSTEM, 0, default, CertStoreFlags.CERT_SYSTEM_STORE_CURRENT_USER, wszStoreName);
/// <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); }