C# 使用私钥生成自签名X509Certificate2证书
我不是最熟悉的,但我正在尝试生成一个自签名的X509Certificate2证书 以下是完整的代码:C# 使用私钥生成自签名X509Certificate2证书,c#,.net,winapi,pki,x509certificate2,C#,.net,Winapi,Pki,X509certificate2,我不是最熟悉的,但我正在尝试生成一个自签名的X509Certificate2证书 以下是完整的代码: using System; using System.Security; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using System.Security.Cryptography.X509Certificates; namespace Example { [Struc
using System;
using System.Security;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Security.Cryptography.X509Certificates;
namespace Example
{
[StructLayout(LayoutKind.Sequential)]
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 ErrorCheck(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.ErrorCheck(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, int providerType, int 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 = "CN = Example", DateTime? startTime = null, DateTime? endTime = null)
{
if (name == null)
name = String.Empty;
var startSystemTime = default(SystemTime);
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.ErrorCheck(CryptAcquireContextW(out providerContext, containerName, null, 1, 0x8));
MarshalHelper.ErrorCheck(CryptGenKey(providerContext, 1, 0x8000001, out cryptKey));
IntPtr errorStringPtr;
int nameDataLength = 0;
byte[] nameData;
dataHandle = GCHandle.Alloc(name, GCHandleType.Pinned);
if (!CertStrToNameW(0x00010001, 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(0x00010001, 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 = 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.ErrorCheck(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 Int32 cbData;
public IntPtr pbData;
}
struct CryptAlgorithmIdentifier {
public String pszObjId;
public CryptoApiBlob Parameters;
}
struct CryptKeyProviderInformation
{
public String pwszContainerName;
public String pwszProvName;
public Int32 dwProvType;
public Int32 dwFlags;
public Int32 cProvParam;
public IntPtr rgProvParam;
public Int32 dwKeySpec;
}
}
}
但是,您可以看到,试图通过
证书获取私钥。PrivateKey
抛出的密钥集不存在。我曾试图查阅文档,但我不明白为什么证书上下文作为X509Certificate2加载时没有设置其私钥。有人有什么想法吗?实现中是否存在导致未设置密钥的问题?我的意思是,我在这里有点困惑,因为我希望自签名证书总是携带它的私钥,因为它自己使用私钥签名,或者不是这样?问题在于CryptKeyProviderInformation
结构签名。它缺少字符集
(具有Auto或Unicode)属性,因为容器和提供程序名称应为Unicode(编组后)。更新结构定义,如下所示:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct CryptKeyProviderInformation
{
public String pwszContainerName;
public String pwszProvName;
public Int32 dwProvType;
public Int32 dwFlags;
public Int32 cProvParam;
public IntPtr rgProvParam;
public Int32 dwKeySpec;
}
然后钥匙应该是accessbile。谢谢,我真不敢相信我错过了。这对我来说是非常棘手的。出于好奇,如果将来会发生这种情况,您是否知道找到此类封送问题的最佳方法是什么?如果没有本地源,这是不可能的。但是,参数名称和/或文档中可能有指示。由于CryptoAPI在其名称中使用匈牙利符号,因此它确实提供了一些线索。例如,LPSTR类型是ANSI字符串,而LPWSTR(注意“W”字符)表示Unicode。参数名称包含以下信息:“psz”或“pwsz”。第一个是ANSI,第二个是Unicode。并且文档(在本例中)说它需要Unicode字符串。hths有时,结构可能具有冲突字符串类型。一个字段是ANSI,另一个是Unicode。可以通过对字段进行统一的属性设置来解决此问题:[Marshallas(UnmanagedType.LPStr)]
用于ANSI和[Marshallas(UnmanagedType.LPWStr)]
对于Unicode字符串,我想问的是,除了检查是否存在返回值外,是否还有其他方法可以检测封送是否无法将值封送回来?事实上,这并没有失败,问题是关于输入数据和封送规则。如果函数没有严格检查输入数据,那么就没有通用的方法来确定issue来源。
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct CryptKeyProviderInformation
{
public String pwszContainerName;
public String pwszProvName;
public Int32 dwProvType;
public Int32 dwFlags;
public Int32 cProvParam;
public IntPtr rgProvParam;
public Int32 dwKeySpec;
}