将证书导出为具有适当签名链的 PFX
Export Certificate as PFX with proper chain of signing
我阅读了一些 post(它们不再存在)并提出了以下生成 PFX 证书的代码。它对创建此 self-signed 证书的部分工作正常。
我正在尝试扩展它以创建一个 self-signed 证书,并从那个证书创建它的“孩子”。我尝试了很多东西,但 none 结果实际上导出了带有证书链的证书。
当前代码已达到导出具有包含 CA 的 PFX 并导入它的程度,这将包括两个证书,但不会相互关联。
这是一段很长的代码,但该操作应该在它的最后一个“创建”功能上起作用。
using Sys = global::System;
using SysInterop = global::System.Runtime.InteropServices;
using SysCry509 = global::System.Security.Cryptography.X509Certificates;
using MdPFX = global::PFX;
public static class PFX
{
#region NativeCode
private const string DLL_Kernel = "kernel32.dll";
private const string DLL_AdvApi = "AdvApi32.dll";
private const string DLL_Crypt = "Crypt32.dll";
[SysInterop.StructLayout(SysInterop.LayoutKind.Sequential)] private 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;
}
[SysInterop.StructLayout(SysInterop.LayoutKind.Sequential)] private struct CryptoApiBlob
{
public int DataLength;
public Sys.IntPtr Data;
public CryptoApiBlob(int dataLength, Sys.IntPtr data)
{
this.DataLength = dataLength;
this.Data = data;
}
}
[SysInterop.StructLayout(SysInterop.LayoutKind.Sequential)] private struct CryptKeyProviderInformation
{
[SysInterop.MarshalAs(SysInterop.UnmanagedType.LPWStr)] public string ContainerName;
[SysInterop.MarshalAs(SysInterop.UnmanagedType.LPWStr)] public string ProviderName;
public int ProviderType;
public int Flags;
public int ProviderParameterCount;
public Sys.IntPtr ProviderParameters;
public int KeySpec;
}
[SysInterop.DllImport(MdPFX.DLL_Kernel, SetLastError = true, ExactSpelling = true)][return: SysInterop.MarshalAs(SysInterop.UnmanagedType.Bool)] private static extern bool FileTimeToSystemTime([SysInterop.In] ref long fileTime, out MdPFX.SystemTime systemTime);
[SysInterop.DllImport(MdPFX.DLL_AdvApi, SetLastError = true, ExactSpelling = true)][return: SysInterop.MarshalAs(SysInterop.UnmanagedType.Bool)] private static extern bool CryptAcquireContextW(out Sys.IntPtr providerContext, [SysInterop.MarshalAs(SysInterop.UnmanagedType.LPWStr)] string container, [SysInterop.MarshalAs(SysInterop.UnmanagedType.LPWStr)] string provider, int providerType, int flags);
[SysInterop.DllImport(MdPFX.DLL_AdvApi, SetLastError = true, ExactSpelling = true)][return: SysInterop.MarshalAs(SysInterop.UnmanagedType.Bool)] private static extern bool CryptReleaseContext(Sys.IntPtr providerContext, int flags);
[SysInterop.DllImport(MdPFX.DLL_AdvApi, SetLastError = true, ExactSpelling = true)][return: SysInterop.MarshalAs(SysInterop.UnmanagedType.Bool)] private static extern bool CryptGenKey(Sys.IntPtr providerContext, int algorithmId, int flags, out Sys.IntPtr cryptKeyHandle);
[SysInterop.DllImport(MdPFX.DLL_AdvApi, SetLastError = true, ExactSpelling = true)][return: SysInterop.MarshalAs(SysInterop.UnmanagedType.Bool)] private static extern bool CryptDestroyKey(Sys.IntPtr cryptKeyHandle);
[SysInterop.DllImport(MdPFX.DLL_Crypt, SetLastError = true, ExactSpelling = true)][return: SysInterop.MarshalAs(SysInterop.UnmanagedType.Bool)] private static extern bool CertStrToNameW(int certificateEncodingType, Sys.IntPtr x500, int strType, Sys.IntPtr reserved, [SysInterop.MarshalAs(SysInterop.UnmanagedType.LPArray)] [SysInterop.Out] byte[] encoded, ref int encodedLength, out Sys.IntPtr errorString);
[SysInterop.DllImport(MdPFX.DLL_Crypt, SetLastError = true, ExactSpelling = true)] private static extern Sys.IntPtr CertCreateSelfSignCertificate(Sys.IntPtr providerHandle, [SysInterop.In] ref MdPFX.CryptoApiBlob subjectIssuerBlob, int flags, [SysInterop.In] ref MdPFX.CryptKeyProviderInformation keyProviderInformation, Sys.IntPtr signatureAlgorithm, [SysInterop.In] ref MdPFX.SystemTime startTime, [SysInterop.In] ref MdPFX.SystemTime endTime, Sys.IntPtr extensions);
[SysInterop.DllImport(MdPFX.DLL_Crypt, SetLastError = true, ExactSpelling = true)][return: SysInterop.MarshalAs(SysInterop.UnmanagedType.Bool)] private static extern bool CertFreeCertificateContext(Sys.IntPtr certificateContext);
[SysInterop.DllImport(MdPFX.DLL_Crypt, SetLastError = true, ExactSpelling = true)] private static extern Sys.IntPtr CertOpenStore([SysInterop.MarshalAs(SysInterop.UnmanagedType.LPStr)] string storeProvider, int messageAndCertificateEncodingType, Sys.IntPtr cryptProvHandle, int flags, Sys.IntPtr parameters);
[SysInterop.DllImport(MdPFX.DLL_Crypt, SetLastError = true, ExactSpelling = true)][return: SysInterop.MarshalAs(SysInterop.UnmanagedType.Bool)] private static extern bool CertCloseStore(Sys.IntPtr certificateStoreHandle, int flags);
[SysInterop.DllImport(MdPFX.DLL_Crypt, SetLastError = true, ExactSpelling = true)][return: SysInterop.MarshalAs(SysInterop.UnmanagedType.Bool)] private static extern bool CertAddCertificateContextToStore(Sys.IntPtr certificateStoreHandle, Sys.IntPtr certificateContext, int addDisposition, out Sys.IntPtr storeContextPtr);
[SysInterop.DllImport(MdPFX.DLL_Crypt, SetLastError = true, ExactSpelling = true)][return: SysInterop.MarshalAs(SysInterop.UnmanagedType.Bool)] private static extern bool CertSetCertificateContextProperty(Sys.IntPtr certificateContext, int propertyId, int flags, [SysInterop.In] ref MdPFX.CryptKeyProviderInformation data);
[SysInterop.DllImport(MdPFX.DLL_Crypt, SetLastError = true, ExactSpelling = true)][return: SysInterop.MarshalAs(SysInterop.UnmanagedType.Bool)] private static extern bool PFXExportCertStoreEx(Sys.IntPtr certificateStoreHandle, ref MdPFX.CryptoApiBlob pfxBlob, Sys.IntPtr password, Sys.IntPtr reserved, int flags);
private static void Check(bool nativeCallSucceeded) { if (!nativeCallSucceeded) { SysInterop.Marshal.ThrowExceptionForHR(SysInterop.Marshal.GetHRForLastWin32Error()); } }
private static MdPFX.SystemTime ToSystemTime(Sys.DateTime dateTime)
{
long fileTime = dateTime.ToFileTime();
MdPFX.SystemTime systemTime = default(MdPFX.SystemTime);
MdPFX.Check(MdPFX.FileTimeToSystemTime(ref fileTime, out systemTime));
return systemTime;
}
#endregion
public static byte[] Create(string commonName, Sys.DateTime startTime, Sys.DateTime endTime, Sys.Security.SecureString password)
{
byte[] pfxData;
if (commonName == null) { commonName = string.Empty; }
MdPFX.SystemTime startSystemTime = MdPFX.ToSystemTime(startTime);
MdPFX.SystemTime endSystemTime = MdPFX.ToSystemTime(endTime);
string containerName = Sys.Guid.NewGuid().ToString();
SysInterop.GCHandle dataHandle = default(SysInterop.GCHandle);
Sys.IntPtr providerContext = Sys.IntPtr.Zero;
Sys.IntPtr cryptKey = Sys.IntPtr.Zero;
Sys.IntPtr certContext = Sys.IntPtr.Zero;
Sys.IntPtr certStore = Sys.IntPtr.Zero;
Sys.IntPtr storeCertContext = Sys.IntPtr.Zero;
Sys.IntPtr passwordPtr = Sys.IntPtr.Zero;
Sys.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions();
try
{
MdPFX.Check(MdPFX.CryptAcquireContextW(out providerContext, containerName, null, 1, 8));
MdPFX.Check(MdPFX.CryptGenKey(providerContext, 1, 1, out cryptKey));
Sys.IntPtr errorStringPtr = Sys.IntPtr.Zero;
int nameDataLength = 0;
byte[] nameData = null;
dataHandle = SysInterop.GCHandle.Alloc(commonName, SysInterop.GCHandleType.Pinned);
if (!MdPFX.CertStrToNameW(0x00010001, dataHandle.AddrOfPinnedObject(), 3, Sys.IntPtr.Zero, null, ref nameDataLength, out errorStringPtr)) { throw new Sys.ArgumentException(SysInterop.Marshal.PtrToStringUni(errorStringPtr)); }
nameData = new byte[nameDataLength];
if (!MdPFX.CertStrToNameW(0x00010001, dataHandle.AddrOfPinnedObject(), 3, Sys.IntPtr.Zero, nameData, ref nameDataLength, out errorStringPtr)) { throw new Sys.ArgumentException(SysInterop.Marshal.PtrToStringUni(errorStringPtr)); }
dataHandle.Free();
dataHandle = SysInterop.GCHandle.Alloc(nameData, SysInterop.GCHandleType.Pinned);
MdPFX.CryptoApiBlob nameBlob = new MdPFX.CryptoApiBlob(nameData.Length, dataHandle.AddrOfPinnedObject());
MdPFX.CryptKeyProviderInformation kpi = new MdPFX.CryptKeyProviderInformation();
kpi.ContainerName = containerName;
kpi.ProviderType = 1;
kpi.KeySpec = 1;
certContext = MdPFX.CertCreateSelfSignCertificate(providerContext, ref nameBlob, 0, ref kpi, Sys.IntPtr.Zero, ref startSystemTime, ref endSystemTime, Sys.IntPtr.Zero);
MdPFX.Check(certContext != Sys.IntPtr.Zero);
dataHandle.Free();
certStore = MdPFX.CertOpenStore("Memory", 0, Sys.IntPtr.Zero, 0x2000, Sys.IntPtr.Zero);
MdPFX.Check(certStore != Sys.IntPtr.Zero);
MdPFX.Check(MdPFX.CertAddCertificateContextToStore(certStore, certContext, 1, out storeCertContext));
MdPFX.CertSetCertificateContextProperty(storeCertContext, 2, 0, ref kpi);
if (password != null) { passwordPtr = SysInterop.Marshal.SecureStringToCoTaskMemUnicode(password); }
MdPFX.CryptoApiBlob pfxBlob = new MdPFX.CryptoApiBlob();
MdPFX.Check(MdPFX.PFXExportCertStoreEx(certStore, ref pfxBlob, passwordPtr, Sys.IntPtr.Zero, 7));
pfxData = new byte[pfxBlob.DataLength];
dataHandle = SysInterop.GCHandle.Alloc(pfxData, SysInterop.GCHandleType.Pinned);
pfxBlob.Data = dataHandle.AddrOfPinnedObject();
MdPFX.Check(MdPFX.PFXExportCertStoreEx(certStore, ref pfxBlob, passwordPtr, Sys.IntPtr.Zero, 7));
dataHandle.Free();
}
finally
{
if (passwordPtr != Sys.IntPtr.Zero) { SysInterop.Marshal.ZeroFreeCoTaskMemUnicode(passwordPtr); }
if (dataHandle.IsAllocated) { dataHandle.Free(); }
if (certContext != Sys.IntPtr.Zero) { MdPFX.CertFreeCertificateContext(certContext); }
if (storeCertContext != Sys.IntPtr.Zero) { MdPFX.CertFreeCertificateContext(storeCertContext); }
if (certStore != Sys.IntPtr.Zero) { MdPFX.CertCloseStore(certStore, 0); }
if (cryptKey != Sys.IntPtr.Zero) { MdPFX.CryptDestroyKey(cryptKey); }
if (providerContext != Sys.IntPtr.Zero)
{
MdPFX.CryptReleaseContext(providerContext, 0);
MdPFX.CryptAcquireContextW(out providerContext, containerName, null, 1, 0x10);
}
}
return pfxData;
}
public static Sys.Security.SecureString CreateSecurePassword(string insecurePassword)
{
if (!string.IsNullOrEmpty(insecurePassword))
{
Sys.Security.SecureString password = new Sys.Security.SecureString();
foreach (char ch in insecurePassword) { password.AppendChar(ch); }
password.MakeReadOnly();
return password;
} else { return null; }
}
public static byte[] Create(string commonName, Sys.DateTime startTime, Sys.DateTime endTime, string insecurePassword)
{
byte[] pfxData;
Sys.Security.SecureString password = null;
try
{
password = MdPFX.CreateSecurePassword(insecurePassword);
pfxData = MdPFX.Create(commonName, startTime, endTime, password);
} finally { if (password != null) { password.Dispose(); } }
return pfxData;
}
public static byte[] Create(string commonName, int YearsValid, Sys.Security.SecureString password)
{
if (!commonName.StartsWith("CN=", Sys.StringComparison.OrdinalIgnoreCase)) { commonName = "CN=" + commonName; }
return MdPFX.Create(commonName, Sys.DateTime.Now, Sys.DateTime.Now.AddYears(YearsValid), password);
}
public static void Create(Sys.IO.Stream save, string commonName, int YearsValid, Sys.Security.SecureString password)
{
byte[] certificateData = MdPFX.Create(commonName, 5, password);
using (Sys.IO.BinaryWriter binWriter = new Sys.IO.BinaryWriter(save))
{
binWriter.Write(certificateData);
binWriter.Flush();
}
}
public static byte[] Create(string commonName, Sys.DateTime startTime, Sys.DateTime endTime) { return MdPFX.Create(commonName, startTime, endTime, (Sys.Security.SecureString)null); }
public static byte[] Create(string commonName, int YearsValid, string insecurePassword) { using (Sys.Security.SecureString password = MdPFX.CreateSecurePassword(insecurePassword)) { return MdPFX.Create(commonName, YearsValid, password); } }
public static void Create(Sys.IO.Stream save, string commonName, int YearsValid, string insecurePassword) { using (Sys.Security.SecureString password = MdPFX.CreateSecurePassword(insecurePassword)) { MdPFX.Create(save, commonName, YearsValid, password); } }
public static void Create(string savePath, string commonName, int YearsValid, Sys.Security.SecureString password) { using (Sys.IO.FileStream fStream = Sys.IO.File.Open(savePath, Sys.IO.FileMode.Create)) { MdPFX.Create(fStream, commonName, YearsValid, password); } }
public static void Create(string savePath, string commonName, int YearsValid, string insecurePassword) { using (Sys.Security.SecureString password = MdPFX.CreateSecurePassword(insecurePassword)) { using (Sys.IO.FileStream fStream = Sys.IO.File.Open(savePath, Sys.IO.FileMode.Create)) { MdPFX.Create(fStream, commonName, YearsValid, password); } } }
public static byte[] Create(SysCry509.X509Certificate2 certificate, string insecurePassword, SysCry509.X509Certificate2 signingCert, SysCry509.X509Certificate2Collection chain = null)
{
SysCry509.X509Certificate2Collection col = new SysCry509.X509Certificate2Collection(certificate);
if (chain != null) { col.AddRange(chain); }
if (signingCert != null)
{
SysCry509.X509Certificate2 sigCertNoPK = new SysCry509.X509Certificate2(signingCert.Export(SysCry509.X509ContentType.Cert));
col.Add(sigCertNoPK);
}
return col.Export(SysCry509.X509ContentType.Pfx, insecurePassword);
}
public static byte[] Create(string commonName, string insecurePassword, int YearsValid, SysCry509.X509Certificate2 signingCert, SysCry509.X509Certificate2Collection chain = null)
{
SysCry509.X509Certificate2 certificate = new SysCry509.X509Certificate2();
certificate.Import(MdPFX.Create(commonName, YearsValid, insecurePassword), insecurePassword, (SysCry509.X509KeyStorageFlags.PersistKeySet | SysCry509.X509KeyStorageFlags.Exportable));
return MdPFX.Create(certificate, insecurePassword, signingCert, chain: chain);
}
public static SysCry509.X509Certificate2 Load(byte[] rawData, string insecurePassword)
{
try
{
SysCry509.X509Certificate2 rCert = new SysCry509.X509Certificate2();
rCert.Import(rawData, insecurePassword, (SysCry509.X509KeyStorageFlags.PersistKeySet | SysCry509.X509KeyStorageFlags.Exportable));
return rCert;
} catch { return null; }
}
public static byte[] Create(string commonName, string insecurePassword, int YearsValid, bool Signed, SysCry509.X509Certificate2Collection chain = null)
{
if (Signed)
{
SysCry509.X509Store store = new SysCry509.X509Store(typeof(MdPFX).FullName, SysCry509.StoreLocation.LocalMachine);
store.Open(SysCry509.OpenFlags.ReadWrite);
const string rCertN = "A.Root.Cert.Name";
SysCry509.X509Certificate2 rCert = null;
if (store.Certificates.Count > 0) { foreach (SysCry509.X509Certificate2 c in store.Certificates) { if (c.SubjectName.Name == rCertN) { rCert = c; break; } } }
if (rCert == null)
{
rCert = MdPFX.Load(MdPFX.Create(rCertN, 10, "A.Root.Cert.Pass"), "A.Root.Cert.Pass");
store.Add(rCert);
}
store.Close();
return MdPFX.Create(commonName, insecurePassword, YearsValid, rCert, chain: chain);
} else { return MdPFX.Create(commonName, YearsValid, insecurePassword); }
}
}
然后,如果我 运行 喜欢这样,它不会将带有链的证书提供给创建的“CA”。
internal static class Program
{
internal static void Main()
{
Sys.IO.File.WriteAllBytes("C:\Users\User\Desktop\cert.pfx", MdPFX.Create("My.Name", "Pass.123", 10, true, chain: null));
}
}
更新问题:2022-01-10
有一件事,@bartonjs 建议我检查这个 link:
我相信我以前见过这个,但仍然继续它并将 soition 上的代码(按原样)复制到一个新的空白项目和 运行(使用密码将“导出”添加到 PFX)。
File.WriteAllBytes("C:\signed.pfx", cert.Export(X509ContentType.Pfx, "pwdpwdpwd"));
然后我导入了证书,但仍然不准确:我将 post 生成的证书(图像)和下面“正确”的证书 - windows 屏幕是在葡萄牙语中(因为它是我的 OS 语言),但是是每个证书的“证书路径”选项卡。
从示例代码创建的证书表明:
但应该是这样的:
我会说在开发证书中以这些品质为目标:
- 根证书颁发机构文件,例如
myRoot.ca
- 密码保护的PKCS12文件(包含私钥+证书),root为上述CA,eg
mySslCert p12
.
- 后者也可以是通配符证书,例如可用于
*.mycompany.com
下的多个子域,这在简单管理方面很有用。
创作
我个人更喜欢使用 OpenSSL 来创建证书,因为这是保护互联网安全的技术,而且我确信没有任何特定于颁发的证书的技术。
查看我的 certificates repository 和 makeCerts.sh
文件,了解一些 OpenSSL 命令:
- 创建根 CA 密钥对
- 创建根证书
- 创建 SSL 密钥对
- 创建 SSL 证书签名请求(可以是通配符证书)
- 创建 SSL 证书
- 创建密码保护的 PKCS12 文件
如果您想使用C# 创建证书,那么您需要遵循相同的6 个步骤并生成相同的文件。希望这能让您的要求更清楚。
部署
如今在真实环境中,您可能最终会部署根 CA 文件(在我的示例中为 mycompany.ca.pem)和 PKCS12 文件(在我的示例中为mycompany.ssl.p12)。
这在专用网络中的专用 PKI 设置中很常见,因此在开发人员 PC 上进行模拟非常有用。我的 .NET Example API uses the certs issued, though in some cases I use tools such as cert-manager 自动发行。
PFX 文件不包含证书链,它们只包含证书(可能碰巧形成一个链)。
cert.Export(X509ContentType.Pfx, "pwdpwdpwd")
将一个证书导出为 PFX。 shorthand
X509Certificate2Collection coll = new X509Certificate2Collection();
coll.Add(cert);
coll.Export(X509ContentType.Pfx, "pwdpwdpwd");
鉴于此,将两个证书导出到一个 PFX 的方法应该是显而易见的:
X509Certificate2Collection coll = new X509Certificate2Collection();
coll.Add(issuer);
coll.Add(cert);
coll.Export(X509ContentType.Pfx, "pwdpwdpwd");
如果您将其导出为“链”,那么您可能不希望出现发行者的私钥,因此如果您还没有 public-only 版本证书,用new X509Certificate2(issuer.RawData)
做一个
我阅读了一些 post(它们不再存在)并提出了以下生成 PFX 证书的代码。它对创建此 self-signed 证书的部分工作正常。
我正在尝试扩展它以创建一个 self-signed 证书,并从那个证书创建它的“孩子”。我尝试了很多东西,但 none 结果实际上导出了带有证书链的证书。
当前代码已达到导出具有包含 CA 的 PFX 并导入它的程度,这将包括两个证书,但不会相互关联。
这是一段很长的代码,但该操作应该在它的最后一个“创建”功能上起作用。
using Sys = global::System;
using SysInterop = global::System.Runtime.InteropServices;
using SysCry509 = global::System.Security.Cryptography.X509Certificates;
using MdPFX = global::PFX;
public static class PFX
{
#region NativeCode
private const string DLL_Kernel = "kernel32.dll";
private const string DLL_AdvApi = "AdvApi32.dll";
private const string DLL_Crypt = "Crypt32.dll";
[SysInterop.StructLayout(SysInterop.LayoutKind.Sequential)] private 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;
}
[SysInterop.StructLayout(SysInterop.LayoutKind.Sequential)] private struct CryptoApiBlob
{
public int DataLength;
public Sys.IntPtr Data;
public CryptoApiBlob(int dataLength, Sys.IntPtr data)
{
this.DataLength = dataLength;
this.Data = data;
}
}
[SysInterop.StructLayout(SysInterop.LayoutKind.Sequential)] private struct CryptKeyProviderInformation
{
[SysInterop.MarshalAs(SysInterop.UnmanagedType.LPWStr)] public string ContainerName;
[SysInterop.MarshalAs(SysInterop.UnmanagedType.LPWStr)] public string ProviderName;
public int ProviderType;
public int Flags;
public int ProviderParameterCount;
public Sys.IntPtr ProviderParameters;
public int KeySpec;
}
[SysInterop.DllImport(MdPFX.DLL_Kernel, SetLastError = true, ExactSpelling = true)][return: SysInterop.MarshalAs(SysInterop.UnmanagedType.Bool)] private static extern bool FileTimeToSystemTime([SysInterop.In] ref long fileTime, out MdPFX.SystemTime systemTime);
[SysInterop.DllImport(MdPFX.DLL_AdvApi, SetLastError = true, ExactSpelling = true)][return: SysInterop.MarshalAs(SysInterop.UnmanagedType.Bool)] private static extern bool CryptAcquireContextW(out Sys.IntPtr providerContext, [SysInterop.MarshalAs(SysInterop.UnmanagedType.LPWStr)] string container, [SysInterop.MarshalAs(SysInterop.UnmanagedType.LPWStr)] string provider, int providerType, int flags);
[SysInterop.DllImport(MdPFX.DLL_AdvApi, SetLastError = true, ExactSpelling = true)][return: SysInterop.MarshalAs(SysInterop.UnmanagedType.Bool)] private static extern bool CryptReleaseContext(Sys.IntPtr providerContext, int flags);
[SysInterop.DllImport(MdPFX.DLL_AdvApi, SetLastError = true, ExactSpelling = true)][return: SysInterop.MarshalAs(SysInterop.UnmanagedType.Bool)] private static extern bool CryptGenKey(Sys.IntPtr providerContext, int algorithmId, int flags, out Sys.IntPtr cryptKeyHandle);
[SysInterop.DllImport(MdPFX.DLL_AdvApi, SetLastError = true, ExactSpelling = true)][return: SysInterop.MarshalAs(SysInterop.UnmanagedType.Bool)] private static extern bool CryptDestroyKey(Sys.IntPtr cryptKeyHandle);
[SysInterop.DllImport(MdPFX.DLL_Crypt, SetLastError = true, ExactSpelling = true)][return: SysInterop.MarshalAs(SysInterop.UnmanagedType.Bool)] private static extern bool CertStrToNameW(int certificateEncodingType, Sys.IntPtr x500, int strType, Sys.IntPtr reserved, [SysInterop.MarshalAs(SysInterop.UnmanagedType.LPArray)] [SysInterop.Out] byte[] encoded, ref int encodedLength, out Sys.IntPtr errorString);
[SysInterop.DllImport(MdPFX.DLL_Crypt, SetLastError = true, ExactSpelling = true)] private static extern Sys.IntPtr CertCreateSelfSignCertificate(Sys.IntPtr providerHandle, [SysInterop.In] ref MdPFX.CryptoApiBlob subjectIssuerBlob, int flags, [SysInterop.In] ref MdPFX.CryptKeyProviderInformation keyProviderInformation, Sys.IntPtr signatureAlgorithm, [SysInterop.In] ref MdPFX.SystemTime startTime, [SysInterop.In] ref MdPFX.SystemTime endTime, Sys.IntPtr extensions);
[SysInterop.DllImport(MdPFX.DLL_Crypt, SetLastError = true, ExactSpelling = true)][return: SysInterop.MarshalAs(SysInterop.UnmanagedType.Bool)] private static extern bool CertFreeCertificateContext(Sys.IntPtr certificateContext);
[SysInterop.DllImport(MdPFX.DLL_Crypt, SetLastError = true, ExactSpelling = true)] private static extern Sys.IntPtr CertOpenStore([SysInterop.MarshalAs(SysInterop.UnmanagedType.LPStr)] string storeProvider, int messageAndCertificateEncodingType, Sys.IntPtr cryptProvHandle, int flags, Sys.IntPtr parameters);
[SysInterop.DllImport(MdPFX.DLL_Crypt, SetLastError = true, ExactSpelling = true)][return: SysInterop.MarshalAs(SysInterop.UnmanagedType.Bool)] private static extern bool CertCloseStore(Sys.IntPtr certificateStoreHandle, int flags);
[SysInterop.DllImport(MdPFX.DLL_Crypt, SetLastError = true, ExactSpelling = true)][return: SysInterop.MarshalAs(SysInterop.UnmanagedType.Bool)] private static extern bool CertAddCertificateContextToStore(Sys.IntPtr certificateStoreHandle, Sys.IntPtr certificateContext, int addDisposition, out Sys.IntPtr storeContextPtr);
[SysInterop.DllImport(MdPFX.DLL_Crypt, SetLastError = true, ExactSpelling = true)][return: SysInterop.MarshalAs(SysInterop.UnmanagedType.Bool)] private static extern bool CertSetCertificateContextProperty(Sys.IntPtr certificateContext, int propertyId, int flags, [SysInterop.In] ref MdPFX.CryptKeyProviderInformation data);
[SysInterop.DllImport(MdPFX.DLL_Crypt, SetLastError = true, ExactSpelling = true)][return: SysInterop.MarshalAs(SysInterop.UnmanagedType.Bool)] private static extern bool PFXExportCertStoreEx(Sys.IntPtr certificateStoreHandle, ref MdPFX.CryptoApiBlob pfxBlob, Sys.IntPtr password, Sys.IntPtr reserved, int flags);
private static void Check(bool nativeCallSucceeded) { if (!nativeCallSucceeded) { SysInterop.Marshal.ThrowExceptionForHR(SysInterop.Marshal.GetHRForLastWin32Error()); } }
private static MdPFX.SystemTime ToSystemTime(Sys.DateTime dateTime)
{
long fileTime = dateTime.ToFileTime();
MdPFX.SystemTime systemTime = default(MdPFX.SystemTime);
MdPFX.Check(MdPFX.FileTimeToSystemTime(ref fileTime, out systemTime));
return systemTime;
}
#endregion
public static byte[] Create(string commonName, Sys.DateTime startTime, Sys.DateTime endTime, Sys.Security.SecureString password)
{
byte[] pfxData;
if (commonName == null) { commonName = string.Empty; }
MdPFX.SystemTime startSystemTime = MdPFX.ToSystemTime(startTime);
MdPFX.SystemTime endSystemTime = MdPFX.ToSystemTime(endTime);
string containerName = Sys.Guid.NewGuid().ToString();
SysInterop.GCHandle dataHandle = default(SysInterop.GCHandle);
Sys.IntPtr providerContext = Sys.IntPtr.Zero;
Sys.IntPtr cryptKey = Sys.IntPtr.Zero;
Sys.IntPtr certContext = Sys.IntPtr.Zero;
Sys.IntPtr certStore = Sys.IntPtr.Zero;
Sys.IntPtr storeCertContext = Sys.IntPtr.Zero;
Sys.IntPtr passwordPtr = Sys.IntPtr.Zero;
Sys.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions();
try
{
MdPFX.Check(MdPFX.CryptAcquireContextW(out providerContext, containerName, null, 1, 8));
MdPFX.Check(MdPFX.CryptGenKey(providerContext, 1, 1, out cryptKey));
Sys.IntPtr errorStringPtr = Sys.IntPtr.Zero;
int nameDataLength = 0;
byte[] nameData = null;
dataHandle = SysInterop.GCHandle.Alloc(commonName, SysInterop.GCHandleType.Pinned);
if (!MdPFX.CertStrToNameW(0x00010001, dataHandle.AddrOfPinnedObject(), 3, Sys.IntPtr.Zero, null, ref nameDataLength, out errorStringPtr)) { throw new Sys.ArgumentException(SysInterop.Marshal.PtrToStringUni(errorStringPtr)); }
nameData = new byte[nameDataLength];
if (!MdPFX.CertStrToNameW(0x00010001, dataHandle.AddrOfPinnedObject(), 3, Sys.IntPtr.Zero, nameData, ref nameDataLength, out errorStringPtr)) { throw new Sys.ArgumentException(SysInterop.Marshal.PtrToStringUni(errorStringPtr)); }
dataHandle.Free();
dataHandle = SysInterop.GCHandle.Alloc(nameData, SysInterop.GCHandleType.Pinned);
MdPFX.CryptoApiBlob nameBlob = new MdPFX.CryptoApiBlob(nameData.Length, dataHandle.AddrOfPinnedObject());
MdPFX.CryptKeyProviderInformation kpi = new MdPFX.CryptKeyProviderInformation();
kpi.ContainerName = containerName;
kpi.ProviderType = 1;
kpi.KeySpec = 1;
certContext = MdPFX.CertCreateSelfSignCertificate(providerContext, ref nameBlob, 0, ref kpi, Sys.IntPtr.Zero, ref startSystemTime, ref endSystemTime, Sys.IntPtr.Zero);
MdPFX.Check(certContext != Sys.IntPtr.Zero);
dataHandle.Free();
certStore = MdPFX.CertOpenStore("Memory", 0, Sys.IntPtr.Zero, 0x2000, Sys.IntPtr.Zero);
MdPFX.Check(certStore != Sys.IntPtr.Zero);
MdPFX.Check(MdPFX.CertAddCertificateContextToStore(certStore, certContext, 1, out storeCertContext));
MdPFX.CertSetCertificateContextProperty(storeCertContext, 2, 0, ref kpi);
if (password != null) { passwordPtr = SysInterop.Marshal.SecureStringToCoTaskMemUnicode(password); }
MdPFX.CryptoApiBlob pfxBlob = new MdPFX.CryptoApiBlob();
MdPFX.Check(MdPFX.PFXExportCertStoreEx(certStore, ref pfxBlob, passwordPtr, Sys.IntPtr.Zero, 7));
pfxData = new byte[pfxBlob.DataLength];
dataHandle = SysInterop.GCHandle.Alloc(pfxData, SysInterop.GCHandleType.Pinned);
pfxBlob.Data = dataHandle.AddrOfPinnedObject();
MdPFX.Check(MdPFX.PFXExportCertStoreEx(certStore, ref pfxBlob, passwordPtr, Sys.IntPtr.Zero, 7));
dataHandle.Free();
}
finally
{
if (passwordPtr != Sys.IntPtr.Zero) { SysInterop.Marshal.ZeroFreeCoTaskMemUnicode(passwordPtr); }
if (dataHandle.IsAllocated) { dataHandle.Free(); }
if (certContext != Sys.IntPtr.Zero) { MdPFX.CertFreeCertificateContext(certContext); }
if (storeCertContext != Sys.IntPtr.Zero) { MdPFX.CertFreeCertificateContext(storeCertContext); }
if (certStore != Sys.IntPtr.Zero) { MdPFX.CertCloseStore(certStore, 0); }
if (cryptKey != Sys.IntPtr.Zero) { MdPFX.CryptDestroyKey(cryptKey); }
if (providerContext != Sys.IntPtr.Zero)
{
MdPFX.CryptReleaseContext(providerContext, 0);
MdPFX.CryptAcquireContextW(out providerContext, containerName, null, 1, 0x10);
}
}
return pfxData;
}
public static Sys.Security.SecureString CreateSecurePassword(string insecurePassword)
{
if (!string.IsNullOrEmpty(insecurePassword))
{
Sys.Security.SecureString password = new Sys.Security.SecureString();
foreach (char ch in insecurePassword) { password.AppendChar(ch); }
password.MakeReadOnly();
return password;
} else { return null; }
}
public static byte[] Create(string commonName, Sys.DateTime startTime, Sys.DateTime endTime, string insecurePassword)
{
byte[] pfxData;
Sys.Security.SecureString password = null;
try
{
password = MdPFX.CreateSecurePassword(insecurePassword);
pfxData = MdPFX.Create(commonName, startTime, endTime, password);
} finally { if (password != null) { password.Dispose(); } }
return pfxData;
}
public static byte[] Create(string commonName, int YearsValid, Sys.Security.SecureString password)
{
if (!commonName.StartsWith("CN=", Sys.StringComparison.OrdinalIgnoreCase)) { commonName = "CN=" + commonName; }
return MdPFX.Create(commonName, Sys.DateTime.Now, Sys.DateTime.Now.AddYears(YearsValid), password);
}
public static void Create(Sys.IO.Stream save, string commonName, int YearsValid, Sys.Security.SecureString password)
{
byte[] certificateData = MdPFX.Create(commonName, 5, password);
using (Sys.IO.BinaryWriter binWriter = new Sys.IO.BinaryWriter(save))
{
binWriter.Write(certificateData);
binWriter.Flush();
}
}
public static byte[] Create(string commonName, Sys.DateTime startTime, Sys.DateTime endTime) { return MdPFX.Create(commonName, startTime, endTime, (Sys.Security.SecureString)null); }
public static byte[] Create(string commonName, int YearsValid, string insecurePassword) { using (Sys.Security.SecureString password = MdPFX.CreateSecurePassword(insecurePassword)) { return MdPFX.Create(commonName, YearsValid, password); } }
public static void Create(Sys.IO.Stream save, string commonName, int YearsValid, string insecurePassword) { using (Sys.Security.SecureString password = MdPFX.CreateSecurePassword(insecurePassword)) { MdPFX.Create(save, commonName, YearsValid, password); } }
public static void Create(string savePath, string commonName, int YearsValid, Sys.Security.SecureString password) { using (Sys.IO.FileStream fStream = Sys.IO.File.Open(savePath, Sys.IO.FileMode.Create)) { MdPFX.Create(fStream, commonName, YearsValid, password); } }
public static void Create(string savePath, string commonName, int YearsValid, string insecurePassword) { using (Sys.Security.SecureString password = MdPFX.CreateSecurePassword(insecurePassword)) { using (Sys.IO.FileStream fStream = Sys.IO.File.Open(savePath, Sys.IO.FileMode.Create)) { MdPFX.Create(fStream, commonName, YearsValid, password); } } }
public static byte[] Create(SysCry509.X509Certificate2 certificate, string insecurePassword, SysCry509.X509Certificate2 signingCert, SysCry509.X509Certificate2Collection chain = null)
{
SysCry509.X509Certificate2Collection col = new SysCry509.X509Certificate2Collection(certificate);
if (chain != null) { col.AddRange(chain); }
if (signingCert != null)
{
SysCry509.X509Certificate2 sigCertNoPK = new SysCry509.X509Certificate2(signingCert.Export(SysCry509.X509ContentType.Cert));
col.Add(sigCertNoPK);
}
return col.Export(SysCry509.X509ContentType.Pfx, insecurePassword);
}
public static byte[] Create(string commonName, string insecurePassword, int YearsValid, SysCry509.X509Certificate2 signingCert, SysCry509.X509Certificate2Collection chain = null)
{
SysCry509.X509Certificate2 certificate = new SysCry509.X509Certificate2();
certificate.Import(MdPFX.Create(commonName, YearsValid, insecurePassword), insecurePassword, (SysCry509.X509KeyStorageFlags.PersistKeySet | SysCry509.X509KeyStorageFlags.Exportable));
return MdPFX.Create(certificate, insecurePassword, signingCert, chain: chain);
}
public static SysCry509.X509Certificate2 Load(byte[] rawData, string insecurePassword)
{
try
{
SysCry509.X509Certificate2 rCert = new SysCry509.X509Certificate2();
rCert.Import(rawData, insecurePassword, (SysCry509.X509KeyStorageFlags.PersistKeySet | SysCry509.X509KeyStorageFlags.Exportable));
return rCert;
} catch { return null; }
}
public static byte[] Create(string commonName, string insecurePassword, int YearsValid, bool Signed, SysCry509.X509Certificate2Collection chain = null)
{
if (Signed)
{
SysCry509.X509Store store = new SysCry509.X509Store(typeof(MdPFX).FullName, SysCry509.StoreLocation.LocalMachine);
store.Open(SysCry509.OpenFlags.ReadWrite);
const string rCertN = "A.Root.Cert.Name";
SysCry509.X509Certificate2 rCert = null;
if (store.Certificates.Count > 0) { foreach (SysCry509.X509Certificate2 c in store.Certificates) { if (c.SubjectName.Name == rCertN) { rCert = c; break; } } }
if (rCert == null)
{
rCert = MdPFX.Load(MdPFX.Create(rCertN, 10, "A.Root.Cert.Pass"), "A.Root.Cert.Pass");
store.Add(rCert);
}
store.Close();
return MdPFX.Create(commonName, insecurePassword, YearsValid, rCert, chain: chain);
} else { return MdPFX.Create(commonName, YearsValid, insecurePassword); }
}
}
然后,如果我 运行 喜欢这样,它不会将带有链的证书提供给创建的“CA”。
internal static class Program
{
internal static void Main()
{
Sys.IO.File.WriteAllBytes("C:\Users\User\Desktop\cert.pfx", MdPFX.Create("My.Name", "Pass.123", 10, true, chain: null));
}
}
更新问题:2022-01-10
有一件事,@bartonjs 建议我检查这个 link:
File.WriteAllBytes("C:\signed.pfx", cert.Export(X509ContentType.Pfx, "pwdpwdpwd"));
然后我导入了证书,但仍然不准确:我将 post 生成的证书(图像)和下面“正确”的证书 - windows 屏幕是在葡萄牙语中(因为它是我的 OS 语言),但是是每个证书的“证书路径”选项卡。
从示例代码创建的证书表明:
但应该是这样的:
我会说在开发证书中以这些品质为目标:
- 根证书颁发机构文件,例如
myRoot.ca
- 密码保护的PKCS12文件(包含私钥+证书),root为上述CA,eg
mySslCert p12
.
- 后者也可以是通配符证书,例如可用于
*.mycompany.com
下的多个子域,这在简单管理方面很有用。
创作
我个人更喜欢使用 OpenSSL 来创建证书,因为这是保护互联网安全的技术,而且我确信没有任何特定于颁发的证书的技术。
查看我的 certificates repository 和 makeCerts.sh
文件,了解一些 OpenSSL 命令:
- 创建根 CA 密钥对
- 创建根证书
- 创建 SSL 密钥对
- 创建 SSL 证书签名请求(可以是通配符证书)
- 创建 SSL 证书
- 创建密码保护的 PKCS12 文件
如果您想使用C# 创建证书,那么您需要遵循相同的6 个步骤并生成相同的文件。希望这能让您的要求更清楚。
部署
如今在真实环境中,您可能最终会部署根 CA 文件(在我的示例中为 mycompany.ca.pem)和 PKCS12 文件(在我的示例中为mycompany.ssl.p12)。
这在专用网络中的专用 PKI 设置中很常见,因此在开发人员 PC 上进行模拟非常有用。我的 .NET Example API uses the certs issued, though in some cases I use tools such as cert-manager 自动发行。
PFX 文件不包含证书链,它们只包含证书(可能碰巧形成一个链)。
cert.Export(X509ContentType.Pfx, "pwdpwdpwd")
将一个证书导出为 PFX。 shorthand
X509Certificate2Collection coll = new X509Certificate2Collection();
coll.Add(cert);
coll.Export(X509ContentType.Pfx, "pwdpwdpwd");
鉴于此,将两个证书导出到一个 PFX 的方法应该是显而易见的:
X509Certificate2Collection coll = new X509Certificate2Collection();
coll.Add(issuer);
coll.Add(cert);
coll.Export(X509ContentType.Pfx, "pwdpwdpwd");
如果您将其导出为“链”,那么您可能不希望出现发行者的私钥,因此如果您还没有 public-only 版本证书,用new X509Certificate2(issuer.RawData)
做一个