如何从PEM文件中获取私钥?

los*_*eep 31 .net ssl cryptography pem private-key

我有一个.PEM文件,其中包含用于SSL数据传输的公钥和私钥,如下所示:

-----BEGIN RSA PRIVATE KEY-----
      private key data
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
      public key data
-----END CERTIFICATE-----
Run Code Online (Sandbox Code Playgroud)

当我想通过以下代码加载.PEM文件时:

X509Certificate2 xx = new X509Certificate2("c:\\myKey.pem");
Run Code Online (Sandbox Code Playgroud)

我得到一个例外,说:"找不到请求的对象." ,满堆栈:

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: 
Run Code Online (Sandbox Code Playgroud)

如果我交换私钥部分和公钥部分的位置,代码工作和加载数据,我可以从对象获得公钥信息,例如.IssuerName及其HasPrivateKey为false.为什么?我误解了,做错了什么?

Mar*_*len 33

这里有一个在代码项目的文章有所有你需要做的这个代码.它只是几个类,所以它是一个轻量级的解决方案.

要从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 ) );
 }
Run Code Online (Sandbox Code Playgroud)

将PEM文件加载到字符串中并调用上面的方法以获取表示证书的字节.接下来,将获取的字节传递给X509Certificate2的构造函数:

 var pem = System.IO.File.ReadAllText( "c:\\myKey.pem" )
 byte[] certBuffer = GetBytesFromPEM( pem, "CERTIFICATE" );
 var certificate = new X509Certificate2( certBuffer );
Run Code Online (Sandbox Code Playgroud)

从PEM文件加载(RSA)私钥有点复杂,但在上面提到的文章中你也可以使用该Crypto.DecodeRsaPrivateKey方法获得对它的支持.


pou*_*pou 17

AFAIK .NET框架在任何地方都不支持PEM.

你可以轻松地解决这个问题,X509Certificate因为你可以在----- BEGIN CERTIFICATE ---------- END CERTIFICATE -----行之间提取base64字符串,将它转换为byte[]X509Certificate从中创造出来.

一个简单的解决方案是从Mono.Security的X509Certificate.cs复制粘贴代码来执行此操作.

获取私钥有点棘手,因为获得byte[]重建RSA实例没有太大帮助(我们可以假设,因为PEM头表明它是RSA).

这次你最好从Mono.Security的PKCS8.cs文件中复制粘贴,然后调用解码方法.

免责声明: 我是上面讨论的Mono代码的主要作者,它都是在MIT.X11许可下提供的


and*_*fox 11

我遇到了同样的问题 - 为了记录 - 我在这里发布了一个完整的,有效的代码示例(由于已知原因,密钥被删除).它主要是在互联网和我的家庭项目要求中找到的东西的汇编.

遵循代码的功能

  • 从openssl加载PEM证书("----- BEGIN CERTIFICATE -----"),其中可能包含"----- BEGIN RSA PRIVATE KEY -----"
  • 返回X509Certificate2
  • x509的私钥存储在机器商店(Windows功能)中,每个人都有访问规则
  • 私钥无法从商店导出

代码:

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Yar*_*onK 6

另一种方法是将客户端PEM证书转换为Windows支持的PFX格式.这可以使用例如openssl,通过运行来完成:

openssl pkcs12 -export -out cert.pfx -inkey cert.key -in cert.pem -certfile ca.pem
Run Code Online (Sandbox Code Playgroud)

(其中"cert.pfx"是输出文件,"cert.key"包含私钥,"cert.pem"包含输入证书,"ca.pem"包含签名者的证书).


Cra*_*lus -1

我不知道.NET(但是Java),但答案应该是相同的。
您的 pem 文件包含证书和私钥。
这是 OpenSSL 中的常见导出。
要实例化 Java 中的对象,X509Certificate您只需使用文件中的以下部分:

-----BEGIN CERTIFICATE-----
证书数据
-----END CERTIFICATE-----

在.NET 中应该是一样的。
只需加载文件并加载 PEM 的该部分即可。

对私钥执行相同的操作。
在java中,您将使用相应的对象(即PrivateKey)来加载它。
使用适合 .NET 的