使用RSA AES提供程序生成自签名1024位X509Certificate2时出现问题

我正在尝试使用Microsoft AES加密提供程序生成X509Certificate2对象:

CALG_AES_256(0x00006610)256位AES。 Microsoft AES加密提供程序支持此算法。

我的问题是我对CryptGenKey(providerContext, 0x6610, 0x4000001, out cryptKey)失败,出现以下错误:

mscorlib.dll中发生未处理的“System.Runtime.InteropServices.COMException”类型exception

附加信息:指定了无效标志。 (HRESULTexception:0x80090009)

我正在使用的标志是(1024 << 16) | 1) (1024 << 16) | 1) 。 除非我弄错了, 根据MSDN上的文档 ,这不应该产生1024位可导出密钥吗? 我的方法有什么问题?

我的代码如下:

 using System; using System.Security; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using System.Security.Cryptography.X509Certificates; namespace WebSockets { public struct SystemTime { public short Year; public short Month; public short DayOfWeek; public short Day; public short Hour; public short Minute; public short Second; public short Milliseconds; } public static class MarshalHelper { public static void CheckReturnValue(bool nativeCallSucceeded) { if (!nativeCallSucceeded) Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); } } public static class DateTimeExtensions { [DllImport("kernel32.dll", SetLastError = true)] static extern bool FileTimeToSystemTime(ref long fileTime, out SystemTime systemTime); public static SystemTime ToSystemTime(this DateTime dateTime) { long fileTime = dateTime.ToFileTime(); SystemTime systemTime; MarshalHelper.CheckReturnValue(FileTimeToSystemTime(ref fileTime, out systemTime)); return systemTime; } } class X509Certificate2Helper { [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] static extern bool CryptAcquireContextW(out IntPtr providerContext, string container, string provider, uint providerType, uint flags); [DllImport("advapi32.dll", SetLastError = true)] static extern bool CryptReleaseContext(IntPtr providerContext, int flags); [DllImport("advapi32.dll", SetLastError = true)] static extern bool CryptGenKey(IntPtr providerContext, int algorithmId, int flags, out IntPtr cryptKeyHandle); [DllImport("advapi32.dll", SetLastError = true)] static extern bool CryptDestroyKey(IntPtr cryptKeyHandle); [DllImport("crypt32.dll", SetLastError = true)] static extern bool CertStrToNameW(int certificateEncodingType, IntPtr x500, int strType, IntPtr reserved, byte[] encoded, ref int encodedLength, out IntPtr errorString); [DllImport("crypt32.dll", SetLastError = true)] static extern IntPtr CertCreateSelfSignCertificate(IntPtr providerHandle, ref CryptoApiBlob subjectIssuerBlob, int flags, ref CryptKeyProviderInformation keyProviderInformation, IntPtr signatureAlgorithm, ref SystemTime startTime, ref SystemTime endTime, IntPtr extensions); [DllImport("crypt32.dll", SetLastError = true)] static extern bool CertFreeCertificateContext(IntPtr certificateContext); [DllImport("crypt32.dll", SetLastError = true)] static extern bool CertSetCertificateContextProperty(IntPtr certificateContext, int propertyId, int flags, ref CryptKeyProviderInformation data); public static X509Certificate2 GenerateSelfSignedCertificate(String name = "", DateTime? startTime = null, DateTime? endTime = null) { if (startTime == null || (DateTime)startTime < DateTime.FromFileTimeUtc(0)) startTime = DateTime.FromFileTimeUtc(0); var startSystemTime = ((DateTime)startTime).ToSystemTime(); if (endTime == null) endTime = DateTime.MaxValue; var endSystemTime = ((DateTime)endTime).ToSystemTime(); string containerName = Guid.NewGuid().ToString(); GCHandle dataHandle = new GCHandle(); IntPtr providerContext = IntPtr.Zero; IntPtr cryptKey = IntPtr.Zero; IntPtr certificateContext = IntPtr.Zero; IntPtr algorithmPointer = IntPtr.Zero; RuntimeHelpers.PrepareConstrainedRegions(); try { MarshalHelper.CheckReturnValue(CryptAcquireContextW(out providerContext, containerName, null, 0x18, 0x8)); MarshalHelper.CheckReturnValue(CryptGenKey(providerContext, 0x6610, 0x4000001, out cryptKey)); IntPtr errorStringPtr; int nameDataLength = 0; byte[] nameData; dataHandle = GCHandle.Alloc(name, GCHandleType.Pinned); if (!CertStrToNameW(0x10001, dataHandle.AddrOfPinnedObject(), 3, IntPtr.Zero, null, ref nameDataLength, out errorStringPtr)) { string error = Marshal.PtrToStringUni(errorStringPtr); throw new ArgumentException(error); } nameData = new byte[nameDataLength]; if (!CertStrToNameW(0x10001, dataHandle.AddrOfPinnedObject(), 3, IntPtr.Zero, nameData, ref nameDataLength, out errorStringPtr)) { string error = Marshal.PtrToStringUni(errorStringPtr); throw new ArgumentException(error); } dataHandle.Free(); dataHandle = GCHandle.Alloc(nameData, GCHandleType.Pinned); CryptoApiBlob nameBlob = new CryptoApiBlob { cbData = (uint)nameData.Length, pbData = dataHandle.AddrOfPinnedObject() }; dataHandle.Free(); CryptKeyProviderInformation keyProvider = new CryptKeyProviderInformation { pwszContainerName = containerName, dwProvType = 1, dwKeySpec = 1 }; CryptAlgorithmIdentifier algorithm = new CryptAlgorithmIdentifier { pszObjId = "1.2.840.113549.1.1.13", Parameters = new CryptoApiBlob() }; algorithmPointer = Marshal.AllocHGlobal(Marshal.SizeOf(algorithm)); Marshal.StructureToPtr(algorithm, algorithmPointer, false); certificateContext = CertCreateSelfSignCertificate(providerContext, ref nameBlob, 0, ref keyProvider, algorithmPointer, ref startSystemTime, ref endSystemTime, IntPtr.Zero); MarshalHelper.CheckReturnValue(certificateContext != IntPtr.Zero); return new X509Certificate2(certificateContext); } finally { if (dataHandle.IsAllocated) dataHandle.Free(); if (certificateContext != IntPtr.Zero) CertFreeCertificateContext(certificateContext); if (cryptKey != IntPtr.Zero) CryptDestroyKey(cryptKey); if (providerContext != IntPtr.Zero) CryptReleaseContext(providerContext, 0); if (algorithmPointer != IntPtr.Zero) { Marshal.DestroyStructure(algorithmPointer, typeof(CryptAlgorithmIdentifier)); Marshal.FreeHGlobal(algorithmPointer); } } } struct CryptoApiBlob { public uint cbData; public IntPtr pbData; } struct CryptAlgorithmIdentifier { [MarshalAs(UnmanagedType.LPStr)] public String pszObjId; public CryptoApiBlob Parameters; } struct CryptKeyProviderInformation { [MarshalAs(UnmanagedType.LPWStr)] public String pwszContainerName; [MarshalAs(UnmanagedType.LPWStr)] public String pwszProvName; public uint dwProvType; public uint dwFlags; public uint cProvParam; public IntPtr rgProvParam; public uint dwKeySpec; } } } 

要调用它,您可以使用: X509Certificate2Helper.GenerateSelfSignedCertificate("CN = Example");

更新

如果我使用0x1作为标志:

 CryptAcquireContextW(out providerContext, containerName, "Microsoft Enhanced RSA and AES Cryptographic Provider", 0x18, 0x8); CryptGenKey(providerContext, 0x6610, 0x1, out cryptKey); 

它越过CryptGenKey ,但随后在CertCreateSelfSignCertificate上失败:

mscorlib.dll中发生未处理的“System.Runtime.InteropServices.COMException”类型exception

附加信息:密钥不存在。 (HRESULTexception:0x8009000D)

这个键集必须以不同方式传递吗? 我在上面创建它的方式有什么问题?

问题出在CryptGenKey函数调用上。 在Algid参数中,您应该传递0x1(用于RSA密钥交换)或0x2(RSA数字签名)。 您不需要其他值。 密钥长度值应为0x4000001(带可导出密钥)。 另外,我注意到在实例化CryptKeyProviderInformation对象时传递了错误的提供程序类型。 替换此行:

 CryptKeyProviderInformation keyProvider = new CryptKeyProviderInformation { pwszContainerName = containerName, dwProvType = 1, dwKeySpec = 1 }; 

用这一行:

 CryptKeyProviderInformation keyProvider = new CryptKeyProviderInformation { pwszContainerName = containerName, dwProvType = 0x18, dwKeySpec = 1 }; 

使用此公式

 (keySize * 65536) | 1; 

对于2048位密钥,它是0x08000001。 根据CryptGenKey方法的文档,您可以使用RSA1024BIT_KEY生成1024位密钥。 我试过寻找定义并找到了这个 (虽然它是在Adobe网站上:))

 #define RSA1024BIT_KEY 0x04000000