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

本文关键字:X509Certificate2 1024 问题 AES RSA 程序生成 使用 | 更新日期: 2023-09-27 17:55:24

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

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

我的问题是我对CryptGenKey(providerContext, 0x6610, 0x4000001, out cryptKey)的调用失败并显示以下错误:

类型的未处理异常 'System.Runtime.InteropServices.COMException'发生在mscorlib中.dll

其他信息:指定的标志无效。(例外情况 结果:0x80090009)

我正在使用的标志是(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失败,并带有:

类型的未处理异常 'System.Runtime.InteropServices.COMException'发生在mscorlib中.dll

其他信息:密钥不存在。(HRESULT的例外情况: 0x8009000D)

此密钥集必须以不同的方式传入吗?我在上面创建它的方式有什么问题?

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

问题出在函数调用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