comment obtenir une clé privée à partir du fichier PEM?

j'ai un .Fichier PEM qui comprend une clé publique et une clé privée pour le transfert de données SSL comme ceci:

-----BEGIN RSA PRIVATE KEY-----
      private key data
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
      public key data
-----END CERTIFICATE-----

quand je veux charger le .Fichier PEM par le code suivant:

X509Certificate2 xx = new X509Certificate2("c:myKey.pem");

j'obtiens une exception qui dit: "Impossible de trouver l'objet demandé.", avec plein de pile:

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:ProjectsDLLTestDLLTestSSL_Test.cs:line 165
       at DLLTest.SSL_Test.Run() in E:ProjectsDLLTestDLLTestSSL_Test.cs:line 21
       at DLLTest.Program.Main(String[] args) in E:ProjectsDLLTestDLLTestProgram.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: 

si je change la place de la section de la clé privée et de la section de la clé publique, le code fonctionne et charge les données, et je peux juste obtenir des informations de la clé publique de l'objet, par exemple. IssuerName, et son HasPrivateKey est faux. pourquoi? suis-je mal compris et mal faire quelque chose?

25
demandé sur Mormegil 2011-09-13 14:39:47

6 réponses

Il y a un l'article sur le Projet de Code qui a tout le code dont vous avez besoin pour ce faire. C'est juste quelques cours, donc c'est une solution légère.

pour obtenir les octets d'un certificat ou d'une clé à partir du fichier PEM, la méthode suivante fonctionnera, quel que soit l'ordre de la clé et du certificat dans le fichier.

 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 ) );
 }

chargez le fichier PEM dans une chaîne et appelez la méthode ci-dessus pour obtenir les octets qui représentent le certificat. Ensuite, vous passez les octets obtenus au constructeur D'un X509Certificate2:

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

charger la clé privée (RSA) à partir du fichier PEM est un peu plus compliqué mais vous trouverez un support pour cela dans l'article ci-dessus ainsi qu'en utilisant le Crypto.DecodeRsaPrivateKey méthode.

27
répondu Marnix van Valen 2016-07-26 19:30:47

AFAIK le .net framework ne supporte PEM nulle part.

vous pouvez Hacker cela facilement pour le X509Certificate partie puisque vous pouvez extraire la chaîne base64 entre les -----CERTIFICAT DE DÉBUT - - - - - et -----END CERTIFICATE - - - - - lignes, convertissez - le en un byte[] et de créer l' X509Certificate.

une solution facile est de copier-coller le code à partir du Mono.Sécurité X509Certificate.cs le faire ce.

obtenir la clé privée est un peu délicat puisque obtenir le byte[] ne sera pas d'une grande aide pour reconstruire l'instance RSA (ce que nous pouvons supposer puisque L'en-tête PEM indique QU'il s'agit de RSA).

cette fois, vous feriez mieux de copier-coller à partir de Mono.Sécurité PKCS8.cs le fichier et sioply appellent la méthode de décodage.

Avertissement: je suis l'auteur principal du code Mono discuté ci-dessus et il est tout disponible sous le MIT.X11 licence

15
répondu poupou 2011-10-13 01:39:13

j'ai eu le même problème et - pour l'enregistrement - je poste ici un échantillon complet de code de travail (la clé est coupée pour des raisons connues). C'est surtout une compilation de choses trouvées sur Internet et les exigences de mon projet à la maison.

caractéristiques du code suivant

  • Charge un certificat PEM ("-----BEGIN CERTIFICATE-----") à partir d'openssl qui peut contenir "-----BEGIN RSA PRIVATE KEY-----"
  • retourne X509Certificate2
  • clé privée pour x509 est stocké dans le magasin de machine (Fonction windows), avec la règle d'accès pour tout le monde
  • les clés privées ne peuvent pas être exportées du magasin

le 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;
        }
    }
}
8
répondu andrew.fox 2014-11-17 17:39:59

une autre approche consiste à convertir le certificat client PEM au format PFX pris en charge par Windows. Ceci peut être fait en utilisant, par exemple, openssl, en lançant:

openssl pkcs12 -export -out cert.pfx -inkey cert.key -in cert.pem -certfile ca.pem

(où " cert.pfx" est le fichier de sortie, "cert.la touche" contient la clé privée, "cert.pem "contient le certificat d'entrée et" ca.pem" contient le certificat du signataire).

3
répondu YaronK 2015-10-01 09:29:50

je ne sais pas .NET (mais Java), mais la réponse doit être la même.

Votre fichier pem contient à la fois le certificat et la clé privée.

Il s'agit d'une exportation habituelle dans OpenSSL.

Pour instancier un objet de X509Certificate en Java, vous n'utiliserez que la partie du fichier qui dit:

-----CERTIFICAT DE DÉBUT - - - - -

données du certificat

----CERTIFICAT DE FIN D'-----

Il faut pareil .NET.

Charge le fichier et cette partie de PEM.

faites la même chose pour les clés privées.

En java, vous utiliserez L'objet correspondant, C'est-à-dire PrivateKey, pour le charger.

Utilisez l'option appropriée pour .NET

0
répondu Cratylus 2011-09-14 08:13:30

il y a un exemple de code que vous pouvez trouver à http://pages.infinit.net/ctech/20040812-0816.html et ça marche pour moi.

0
répondu hardywang 2012-07-31 15:32:13