.net 如何从 PEM 文件中获取私钥?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7400500/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
how to get private key from PEM file?
提问by losingsleeep
i have a .PEM file that includes public key and a private key for SSL data transfer like this:
我有一个 .PEM 文件,其中包含用于 SSL 数据传输的公钥和私钥,如下所示:
-----BEGIN RSA PRIVATE KEY-----
private key data
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
public key data
-----END CERTIFICATE-----
when i want to load the .PEM file by the following code:
当我想通过以下代码加载 .PEM 文件时:
X509Certificate2 xx = new X509Certificate2("c:\myKey.pem");
i get an exception that says: "Cannot find the requested object." , with full stack:
我收到一条异常消息:“找不到请求的对象。” ,全栈:
System.Security.Cryptography.CryptographicException was unhandled
Message=Cannot find the requested object.
Source=mscorlib
StackTrace:
at System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)
at System.Security.Cryptography.X509Certificates.X509Utils._QueryCertFileType(String fileName)
at System.Security.Cryptography.X509Certificates.X509Certificate.LoadCertificateFromFile(String fileName, Object password, X509KeyStorageFlags keyStorageFlags)
at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(String fileName)
at System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(String fileName)
at DLLTest.SSL_Test.test() in E:\Projects\DLLTest\DLLTest\SSL_Test.cs:line 165
at DLLTest.SSL_Test.Run() in E:\Projects\DLLTest\DLLTest\SSL_Test.cs:line 21
at DLLTest.Program.Main(String[] args) in E:\Projects\DLLTest\DLLTest\Program.cs:line 21
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException:
if i swap place of private key section and public key section, the code works and load data, and i can get just public key info from the object, eg. IssuerName, and its HasPrivateKey is false. why? am i misunderstood and doing wrong something?
如果我交换私钥部分和公钥部分的位置,则代码工作并加载数据,并且我可以从对象中获取公钥信息,例如。IssuerName,其 HasPrivateKey 为 false。为什么?我是不是被误解了,做错了什么?
回答by Marnix van Valen
There's an article on the Code Projectthat has all the code you need to do this. It's just a couple of classes so it's a light-weight solution.
这里有一个在代码项目的文章有所有你需要做的这个代码。它只是几个类,所以它是一个轻量级的解决方案。
To get the bytes for either a certificate or a key from the PEM file the following method will work, regardless of the order of the key and certificate in the file.
要从 PEM 文件中获取证书或密钥的字节,无论文件中密钥和证书的顺序如何,以下方法都可以使用。
byte[] GetBytesFromPEM( string pemString, string section )
{
var header = String.Format("-----BEGIN {0}-----", section);
var footer = String.Format("-----END {0}-----", section);
var start= pemString.IndexOf(header, StringComparison.Ordinal);
if( start < 0 )
return null;
start += header.Length;
var end = pemString.IndexOf(footer, start, StringComparison.Ordinal) - start;
if( end < 0 )
return null;
return Convert.FromBase64String( pemString.Substring( start, end ) );
}
Load the PEM file into a string and call the method above to get the bytes that represent the certificate. Next you pass the obtained bytes to the constructor of an X509Certificate2 :
将 PEM 文件加载到字符串中并调用上述方法以获取表示证书的字节。接下来,您将获得的字节传递给 X509Certificate2 的构造函数:
var pem = System.IO.File.ReadAllText( "c:\myKey.pem" );
byte[] certBuffer = GetBytesFromPEM( pem, "CERTIFICATE" );
var certificate = new X509Certificate2( certBuffer );
Loading the (RSA) private key from the PEM file is a bit more complicated but you'll find support for that in the above mentioned article as well using the Crypto.DecodeRsaPrivateKeymethod.
从 PEM 文件加载 (RSA) 私钥有点复杂,但您会在上述文章中找到支持,也可以使用该Crypto.DecodeRsaPrivateKey方法。
回答by poupou
AFAIK the .NET framework does not support PEM anywhere.
AFAIK .NET 框架在任何地方都不支持 PEM。
You can hack around this easily for the X509Certificatepart since you can extract the base64 string between the -----BEGIN CERTIFICATE-----and -----END CERTIFICATE-----lines, convert it into a byte[]and create the X509Certificatefrom it.
您可以轻松地解决这个问题,X509Certificate因为您可以提取-----BEGIN CERTIFICATE-----和-----END CERTIFICATE-----行之间的 base64 字符串,将其转换为 abyte[]和X509Certificate从中创建。
An easy solution is to copy-paste code from Mono.Security's X509Certificate.csto do this.
一个简单的解决方案是从 Mono.Security 的X509Certificate.cs复制粘贴代码来执行此操作。
Getting the private key is a bit tricky since getting the byte[]won't be of much help to reconstruct the RSA instance (which we can assume since the PEM header states it's RSA).
获取私钥有点棘手,因为获取私钥byte[]对重建 RSA 实例没有太大帮助(我们可以假设,因为 PEM 标头声明它是 RSA)。
This time you better copy-paste from Mono.Security's PKCS8.csfile and sioply call the decode method.
这次你最好从 Mono.Security 的PKCS8.cs文件中复制粘贴,然后简单地调用 decode 方法。
Disclaimer:I'm the main author of the Mono code discussed above and it is all available under the MIT.X11 license
免责声明:我是上面讨论的 Mono 代码的主要作者,它在 MIT.X11 许可下全部可用
回答by andrew.fox
I had the same problem and - for the record - I post here a complete, working code sample (the key is cut for known reasons). It's mostly a compilation of stuff found on the Internet and my home project requirements.
我遇到了同样的问题 - 为了记录 - 我在这里发布了一个完整的,有效的代码示例(由于已知原因,密钥被切断)。它主要是在互联网上找到的东西和我的家庭项目要求的汇编。
Following code's features
以下代码的特点
- Loads a PEM certificate ("-----BEGIN CERTIFICATE-----") from openssl that may contain "-----BEGIN RSA PRIVATE KEY-----"
- returns X509Certificate2
- private key for x509 is stored in the machine store (windows feature), with access rule for everyone
- private key cannot be exported from the store
- 从可能包含“-----BEGIN RSA PRIVATE KEY-----”的openssl加载PEM证书(“-----BEGIN CERTIFICATE-----”)
- 返回 X509Certificate2
- x509 的私钥存储在机器存储中(Windows 功能),每个人都有访问规则
- 无法从商店导出私钥
The code:
编码:
using System;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Security.Principal;
using System.Security.AccessControl;
namespace Test1
{
public static class Test
{
public static int Main()
{
string pemCertWithPrivateKeyText = @"-----BEGIN CERTIFICATE-----
...
bjEdMBsGA1UEChQUVGV4YXMgQSZNIFV5jZTESMBAGA1UEAxMJVXNlciBOYW1lMSA
...
YXMgQSZNIFV5jZTESMBAGA1e2yX28ERsgBD6xx7mJDrPxkqWyV/a9tCF8W6jGSs=
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIIEow..................
jZMxBWg+imTpbGb+TpR2kxBWctnzFOWRuVYdSQIDAQABAoIBAFSKz/RLtkmZKE1d
....
BWctnzFOWRuVYdSdsf+WDqNxEzrL08SU1w5WuSxIsbxchUvG4
-----END RSA PRIVATE KEY-----
"; // just an example
X509Certificate2 cert = PEMToX509.Convert(pemCertWithPrivateKeyText);
return (cert.HasPrivateKey ? 1 : -1);
}
}
internal static class PEMToX509
{
const string KEY_HEADER = "-----BEGIN RSA PRIVATE KEY-----";
const string KEY_FOOTER = "-----END RSA PRIVATE KEY-----";
internal static X509Certificate2 Convert(string pem)
{
try
{
byte[] pemCertWithPrivateKey = System.Text.Encoding.ASCII.GetBytes(pem);
RSACryptoServiceProvider rsaPK = GetRSA(pem);
X509Certificate2 cert = new X509Certificate2();
cert.Import(pemCertWithPrivateKey, "", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
if (rsaPK != null)
{
cert.PrivateKey = rsaPK;
}
return cert;
}
catch
{
return null;
}
}
private static RSACryptoServiceProvider GetRSA(string pem)
{
RSACryptoServiceProvider rsa = null;
if (IsPrivateKeyAvailable(pem))
{
RSAParameters privateKey = DecodeRSAPrivateKey(pem);
SecurityIdentifier everyoneSI = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
CryptoKeyAccessRule rule = new CryptoKeyAccessRule(everyoneSI, CryptoKeyRights.FullControl, AccessControlType.Allow);
CspParameters cspParameters = new CspParameters();
cspParameters.KeyContainerName = "MY_C_NAME";
cspParameters.ProviderName = "Microsoft Strong Cryptographic Provider";
cspParameters.ProviderType = 1;
cspParameters.Flags = CspProviderFlags.UseNonExportableKey | CspProviderFlags.UseMachineKeyStore;
cspParameters.CryptoKeySecurity = new CryptoKeySecurity();
cspParameters.CryptoKeySecurity.SetAccessRule(rule);
rsa = new RSACryptoServiceProvider(cspParameters);
rsa.PersistKeyInCsp = true;
rsa.ImportParameters(privateKey);
}
return rsa;
}
private static bool IsPrivateKeyAvailable(string privateKeyInPEM)
{
return (privateKeyInPEM != null && privateKeyInPEM.Contains(KEY_HEADER)
&& privateKeyInPEM.Contains(KEY_FOOTER));
}
private static RSAParameters DecodeRSAPrivateKey(string privateKeyInPEM)
{
if (IsPrivateKeyAvailable(privateKeyInPEM) == false)
throw new ArgumentException("bad format");
string keyFormatted = privateKeyInPEM;
int cutIndex = keyFormatted.IndexOf(KEY_HEADER);
keyFormatted = keyFormatted.Substring(cutIndex, keyFormatted.Length - cutIndex);
cutIndex = keyFormatted.IndexOf(KEY_FOOTER);
keyFormatted = keyFormatted.Substring(0, cutIndex + KEY_FOOTER.Length);
keyFormatted = keyFormatted.Replace(KEY_HEADER, "");
keyFormatted = keyFormatted.Replace(KEY_FOOTER, "");
keyFormatted = keyFormatted.Replace("\r", "");
keyFormatted = keyFormatted.Replace("\n", "");
keyFormatted = keyFormatted.Trim();
byte[] privateKeyInDER = System.Convert.FromBase64String(keyFormatted);
byte[] paramModulus;
byte[] paramDP;
byte[] paramDQ;
byte[] paramIQ;
byte[] paramE;
byte[] paramD;
byte[] paramP;
byte[] paramQ;
MemoryStream memoryStream = new MemoryStream(privateKeyInDER);
BinaryReader binaryReader = new BinaryReader(memoryStream);
ushort twobytes = 0;
int elements = 0;
byte bt = 0;
try
{
twobytes = binaryReader.ReadUInt16();
if (twobytes == 0x8130)
binaryReader.ReadByte();
else if (twobytes == 0x8230)
binaryReader.ReadInt16();
else
throw new CryptographicException("Wrong data");
twobytes = binaryReader.ReadUInt16();
if (twobytes != 0x0102)
throw new CryptographicException("Wrong data");
bt = binaryReader.ReadByte();
if (bt != 0x00)
throw new CryptographicException("Wrong data");
elements = GetIntegerSize(binaryReader);
paramModulus = binaryReader.ReadBytes(elements);
elements = GetIntegerSize(binaryReader);
paramE = binaryReader.ReadBytes(elements);
elements = GetIntegerSize(binaryReader);
paramD = binaryReader.ReadBytes(elements);
elements = GetIntegerSize(binaryReader);
paramP = binaryReader.ReadBytes(elements);
elements = GetIntegerSize(binaryReader);
paramQ = binaryReader.ReadBytes(elements);
elements = GetIntegerSize(binaryReader);
paramDP = binaryReader.ReadBytes(elements);
elements = GetIntegerSize(binaryReader);
paramDQ = binaryReader.ReadBytes(elements);
elements = GetIntegerSize(binaryReader);
paramIQ = binaryReader.ReadBytes(elements);
EnsureLength(ref paramD, 256);
EnsureLength(ref paramDP, 128);
EnsureLength(ref paramDQ, 128);
EnsureLength(ref paramE, 3);
EnsureLength(ref paramIQ, 128);
EnsureLength(ref paramModulus, 256);
EnsureLength(ref paramP, 128);
EnsureLength(ref paramQ, 128);
RSAParameters rsaParameters = new RSAParameters();
rsaParameters.Modulus = paramModulus;
rsaParameters.Exponent = paramE;
rsaParameters.D = paramD;
rsaParameters.P = paramP;
rsaParameters.Q = paramQ;
rsaParameters.DP = paramDP;
rsaParameters.DQ = paramDQ;
rsaParameters.InverseQ = paramIQ;
return rsaParameters;
}
finally
{
binaryReader.Close();
}
}
private static int GetIntegerSize(BinaryReader binary)
{
byte bt = 0;
byte lowbyte = 0x00;
byte highbyte = 0x00;
int count = 0;
bt = binary.ReadByte();
if (bt != 0x02)
return 0;
bt = binary.ReadByte();
if (bt == 0x81)
count = binary.ReadByte();
else if (bt == 0x82)
{
highbyte = binary.ReadByte();
lowbyte = binary.ReadByte();
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
count = BitConverter.ToInt32(modint, 0);
}
else
count = bt;
while (binary.ReadByte() == 0x00)
count -= 1;
binary.BaseStream.Seek(-1, SeekOrigin.Current);
return count;
}
private static void EnsureLength(ref byte[] data, int desiredLength)
{
if (data == null || data.Length >= desiredLength)
return;
int zeros = desiredLength - data.Length;
byte[] newData = new byte[desiredLength];
Array.Copy(data, 0, newData, zeros, data.Length);
data = newData;
}
}
}
回答by YaronK
A different approach is to convert the client PEM certificate to the PFX format supported by Windows. This can be done using, for example, openssl, by running:
另一种方法是将客户端 PEM 证书转换为 Windows 支持的 PFX 格式。例如,这可以通过运行 openssl 来完成:
openssl pkcs12 -export -out cert.pfx -inkey cert.key -in cert.pem -certfile ca.pem
(where "cert.pfx" is the output file, "cert.key" contains the private key, "cert.pem" contains the input certificate, and "ca.pem" contains the certificate of the signer).
(其中“cert.pfx”是输出文件,“cert.key”包含私钥,“cert.pem”包含输入证书,“ca.pem”包含签名者的证书)。
回答by huse.ckr
I encountered the same issue and found a solution as below:
我遇到了同样的问题,并找到了如下解决方案:
First convert prkey.pem into prkey.xml with this tool //https://superdry.apphb.com/tools/online-rsa-key-converter
首先用这个工具将prkey.pem转换成prkey.xml // https://superdry.apphb.com/tools/online-rsa-key-converter
var dataString = "test";
byte[] dataToEncrypt = Encoding.UTF8.GetBytes(dataString);
RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
provider.FromXmlString(File.ReadAllText("C:\prkey.xml"));
byte[] signedBytes = provider.SignData(dataToEncrypt, new SHA256CryptoServiceProvider());
textBox3.Text = BitConverter.ToString(signedBytes);
回答by Cratylus
I don't know .NET (but Java) but the answer should be the same.
Your pem file contains both the certificate and private key.
This is a usual export in OpenSSL.
To instantiate an object of X509Certificatein Java you would use only the part of the file that says:
我不知道.NET(但Java)但答案应该是一样的。
您的 pem 文件包含证书和私钥。
这是 OpenSSL 中的常见导出。
要X509Certificate在 Java 中实例化一个对象,您将只使用文件的一部分:
-----BEGIN CERTIFICATE-----
certificate data
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
证书数据
-----END CERTIFICATE-----
It should be the same in .NET.
Just load the file and load that part of PEM.
在 .NET 中应该是一样的。
只需加载文件并加载 PEM 的那部分。
Do the same for private key.
In java you would use the corresponding object i.e. PrivateKey to load it.
Use the appropriate for .NET
对私钥做同样的事情。
在 java 中,您将使用相应的对象,即 PrivateKey 来加载它。
使用适用于 .NET 的

