这是我的第一个问题。我已经解决了这个问题好几天了,所以我希望你能帮助我。
我在C#中编写了一个应用程序,可以创建带扩展名的证书(alternativ域名,签名扩展等),并将其导入Windows证书库。大多数函数都是从Crypt32.dll编组的,就像CertCreateSelfSignCertificate一样,一切正常。
我的下一个任务是使用自己创建的证书颁发机构签署证书。所以我使用CryptSignAndEncodeCertificate来创建签名证书。它可以创建一个签名证书,但它没有扩展名,也没有私钥。
这是应该成为魔术的功能。
public static X509Certificate2 SignCertificate(X509Certificate2 certificateToSign, int KeySpecification, X509Certificate2 CACert)
{
IntPtr hCAProv = IntPtr.Zero;
IntPtr hProvAllocPtr = IntPtr.Zero;
IntPtr subordinateCertInfoAllocPtr = IntPtr.Zero;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
// Get CA cert into CERT_CONTEXT
// Get CA cert into CERT_INFO from context.pCertInfo
NativeMethods.CERT_CONTEXT CAContext = (NativeMethods.CERT_CONTEXT)Marshal.PtrToStructure(CACert.Handle, typeof(NativeMethods.CERT_CONTEXT));
NativeMethods.CERT_INFO CACertInfo = (NativeMethods.CERT_INFO)Marshal.PtrToStructure(CAContext.pCertInfo, typeof(NativeMethods.CERT_INFO));
uint pcbData = 0;
// get the context property handle of the CA Cert
if (!NativeMethods.CertGetCertificateContextProperty(CACert.Handle, 2, hProvAllocPtr, ref pcbData))
throw new CryptographicException(Marshal.GetLastWin32Error());
hProvAllocPtr = NativeMethods.LocalAlloc(0, new IntPtr((long)pcbData));
if (!NativeMethods.CertGetCertificateContextProperty(CACert.Handle, 2, hProvAllocPtr, ref pcbData))
throw new CryptographicException(Marshal.GetLastWin32Error());
CRYPT_ALGORITHM_IDENTIFIER signatureAlgo = new CRYPT_ALGORITHM_IDENTIFIER()
{
pszObjId = NativeMethods.OID_RSA_SHA256RSA
};
// get the key handle of the CA Cert
CRYPT_KEY_PROV_INFO pKeyInfo = (CRYPT_KEY_PROV_INFO)Marshal.PtrToStructure(hProvAllocPtr, typeof(CRYPT_KEY_PROV_INFO));
// Acquire a context to the provider for crypto
if (!NativeMethods.CryptAcquireContext(ref hCAProv, pKeyInfo.ContainerName, pKeyInfo.ProviderName, pKeyInfo.ProviderType, pKeyInfo.Flags))
throw new Win32Exception(Marshal.GetLastWin32Error());
// Get subordinate cert into CERT_CONTEXT
// Get subordinate cert into CERT_INFO from context.pCertInfo
NativeMethods.CERT_CONTEXT subordinateCertContext = (NativeMethods.CERT_CONTEXT)Marshal.PtrToStructure(certificateToSign.Handle, typeof(NativeMethods.CERT_CONTEXT));
NativeMethods.CERT_INFO subordinateCertInfo = (NativeMethods.CERT_INFO)Marshal.PtrToStructure(subordinateCertContext.pCertInfo, typeof(NativeMethods.CERT_INFO));
IntPtr extensions = CreateExtensionsStructure(certificateToSign.Extensions);
subordinateCertInfo.cExtension = certificateToSign.Extensions == null ? 0 : (uint)certificateToSign.Extensions.Count;
subordinateCertInfo.rgExtension = extensions;
subordinateCertInfo.SignatureAlgorithm = signatureAlgo;
subordinateCertInfo.Issuer = CACertInfo.Subject;
subordinateCertInfoAllocPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NativeMethods.CERT_INFO)));
Marshal.StructureToPtr(subordinateCertInfo, subordinateCertInfoAllocPtr, false);
byte[] pbEncodedCert = null;
UInt32 pbEncodedCertLength = 0;
if (!NativeMethods.CryptSignAndEncodeCertificate(hCAProv,
(uint)KeySpecification,
NativeMethods.X509_ASN_ENCODING,
NativeMethods.X509_CERT_TO_BE_SIGNED,
subordinateCertInfoAllocPtr,
ref signatureAlgo,
IntPtr.Zero,
pbEncodedCert,
ref pbEncodedCertLength))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
pbEncodedCert = new byte[pbEncodedCertLength];
if (!NativeMethods.CryptSignAndEncodeCertificate(hCAProv,
(uint)KeySpecification,
NativeMethods.X509_ASN_ENCODING,
NativeMethods.X509_CERT_TO_BE_SIGNED,
subordinateCertInfoAllocPtr,
ref signatureAlgo,
IntPtr.Zero,
pbEncodedCert,
ref pbEncodedCertLength))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
X509Certificate2 signedCertificate = new X509Certificate2(pbEncodedCert, "", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet);
return signedCertificate;
}
另一个问题是为什么我不能从CERT_INFO
获得给定证书的扩展名NativeMethods.CERT_CONTEXT subordinateCertContext = (NativeMethods.CERT_CONTEXT)Marshal.PtrToStructure(certificateToSign.Handle, typeof(NativeMethods.CERT_CONTEXT));
NativeMethods.CERT_INFO subordinateCertInfo = (NativeMethods.CERT_INFO)Marshal.PtrToStructure(subordinateCertContext.pCertInfo, typeof(NativeMethods.CERT_INFO));
顺便说一下,如果我在CryptSignAndEncodeCertificate中直接使用subordinateCertInfo作为pvStructInfo,则新证书具有扩展名,但它不会与CA建立连接。因为未设置subordinateCertInfo.Issuer。
[DllImport(CRYPT32, SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
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);
顺便说一下,我不想使用Bouncy Castle或者用C ++编写应用程序。所以我希望你有一个没有一些极端变化的解决方案。