none
Hot to get the Private Key from a X509Certifiacte2 into a PKCS8.PrivateKeyInfo? RRS feed

  • General discussion

  • Hi,

    I have a certificate with a Private Key loaded from a .pfx-file into a X509Certificate2. Now I want to get the Private Key from that X590Certificate2 into a PKCS8.PrivateKeyInfo structure. How?

    Regards
    CSN22


    Viele Grüße / Best regards CSN22

    • Changed type Mike FengModerator Thursday, August 16, 2012 9:18 AM Everybody can give different answe
    Thursday, August 2, 2012 9:33 AM

All replies

  • Hi Csn22,

    Welcome to the MSDN Forum.

    What is the PKCS8.PrivateKeyInfo?

    Best regards,


    Mike Feng
    MSDN Community Support | Feedback to us
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Friday, August 3, 2012 8:43 AM
    Moderator
  • Hi Mike Feng,

    i took a look at the code, that was not written by me orginally, and found out, that a programmer that is not longer in our company, has written a class some years ago under .NET 1.1 (.NET 2.0 wasn't available at that time I believe). I will post the code, and maybe you can understand it. If you need more information, just ask and I'll try my best.

    using System;
    using System.Collections;
    using System.Security.Cryptography;
    using System.Text;
    
    using MyCompany.Tools.Asn1;
    using MyCompany.Tools.Security.X509;
    
    namespace MyCompany.Tools.Security.Cryptography {
    
    	public sealed class PKCS8 {
    
    		public enum KeyInfo {
    			PrivateKey,
    			EncryptedPrivateKey,
    			Unknown
    		}
    
    		private PKCS8 () 
    		{
    		}
    
    		static public KeyInfo GetType (byte[] data) 
    		{
    			if (data == null)
    				throw new ArgumentNullException ("data");
    
    			KeyInfo ki = KeyInfo.Unknown;
    			try {
    				Asn1Parser parser = new Asn1Parser();
    				parser.LoadData( data );
    				Asn1Node top = parser.RootNode;
    				if ((top.Tag == 0x30) && (top.Count > 0)) {
    					Asn1Node firstLevel = top [0];
    					switch (firstLevel.Tag) {
    						case 0x02:
    							ki = KeyInfo.PrivateKey;
    							break;
    						case 0x30:
    							ki = KeyInfo.EncryptedPrivateKey;
    							break;
    					}
    				}
    			}
    			catch {
    				throw new CryptographicException ("invalid ASN.1 data");
    			}
    			return ki;
    		}
    
    		/*
    		 * PrivateKeyInfo ::= SEQUENCE {
    		 *	version Version,
    		 *	privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
    		 *	privateKey PrivateKey,
    		 *	attributes [0] IMPLICIT Attributes OPTIONAL 
    		 * }
    		 * 
    		 * Version ::= INTEGER
    		 * 
    		 * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
    		 * 
    		 * PrivateKey ::= OCTET STRING
    		 * 
    		 * Attributes ::= SET OF Attribute
    		 */
    		public class PrivateKeyInfo {
    
    			private int _version;
    			private string _algorithm;
    			private byte[] _key;
    			private ArrayList _list;
    
    			public PrivateKeyInfo () 
    			{
    				_version = 0;
    				_list = new ArrayList ();
    			}
    
    			public PrivateKeyInfo (byte[] data) : this () 
    			{
    				Decode (data);
    			}
    
    			// properties
    
    			public string Algorithm {
    				get { return _algorithm; }
    				set { _algorithm = value; }
    			}
    
    			public ArrayList Attributes {
    				get { return _list; }
    			}
    
    			public byte[] PrivateKey {
    				get {
    					if (_key == null)
    						return null;
    					return (byte[]) _key.Clone (); 
    				}
    				set { 
    					if (value == null)
    						throw new ArgumentNullException ("PrivateKey");
    					_key = (byte[]) value.Clone (); 
    				}
    			}
    
    			public int Version {
    				get { return _version; }
    				set { 
    					if (value < 0)
    						throw new ArgumentOutOfRangeException ("negative version");
    					_version = value; 
    				}
    			}
    
    			// methods
    
    			private void Decode (byte[] data) 
    			{
    				Asn1Parser parser = new Asn1Parser();
    				parser.LoadData( data );
    				Asn1Node privateKeyInfo = parser.RootNode;
    				if (privateKeyInfo.Tag != 0x30)
    					throw new CryptographicException ("invalid PrivateKeyInfo");
    
    				Asn1Node version = privateKeyInfo [0];
    				if (version.Tag != 0x02)
    					throw new CryptographicException ("invalid version");
    				_version = version.Data [0];
    
    				Asn1Node privateKeyAlgorithm = privateKeyInfo [1];
    				if (privateKeyAlgorithm.Tag != 0x30)
    					throw new CryptographicException ("invalid algorithm");
    				
    				Asn1Node algorithm = privateKeyAlgorithm [0];
    				if (algorithm.Tag != 0x06)
    					throw new CryptographicException ("missing algorithm OID");
    				_algorithm = Asn1Convert.ToOid (algorithm);
    
    				Asn1Node privateKey = privateKeyInfo [2];
    				_key = privateKey.Data;
    
    				// attributes [0] IMPLICIT Attributes OPTIONAL
    				if (privateKeyInfo.Count > 3) {
    					Asn1Node attributes = privateKeyInfo [3];
    					for (int i=0; i < attributes.Count; i++) {
    						_list.Add (attributes [i]);
    					}
    				}
    			}
    
    			public byte[] GetBytes () 
    			{
    				Asn1Node privateKeyAlgorithm = new Asn1Node (0x30);
    				privateKeyAlgorithm.Add (Asn1Convert.FromOid (_algorithm));
    				privateKeyAlgorithm.Add (new Asn1Node (0x05)); // ASN.1 NULL
    
    				Asn1Node pki = new Asn1Node (0x30);
    				pki.Add (new Asn1Node (0x02, new byte [1] { (byte) _version }));
    				pki.Add (privateKeyAlgorithm);
    				pki.Add (new Asn1Node (0x04, _key));
    
    				if (_list.Count > 0) {
    					Asn1Node attributes = new Asn1Node (0xA0);
    					foreach (Asn1Node attribute in _list) {
    						attributes.Add (attribute);
    					}
    					pki.Add (attributes);
    				}
    
    				return pki.GetBytes ();
    			}
    
    			// static methods
    
    			static private byte[] RemoveLeadingZero (byte[] bigInt) 
    			{
    				int start = 0;
    				int length = bigInt.Length;
    				if (bigInt [0] == 0x00) {
    					start = 1;
    					length--;
    				}
    				byte[] bi = new byte [length];
    				Buffer.BlockCopy (bigInt, start, bi, 0, length);
    				return bi;
    			}
    
    			static private byte[] Normalize (byte[] bigInt, int length) 
    			{
    				if (bigInt.Length == length)
    					return bigInt;
    				else if (bigInt.Length > length)
    					return RemoveLeadingZero (bigInt);
    				else {
    					// pad with 0
    					byte[] bi = new byte [length];
    					Buffer.BlockCopy (bigInt, 0, bi, (length - bigInt.Length), bigInt.Length);
    					return bi;
    				}
    			}
    			
    			/*
    			 * RSAPrivateKey ::= SEQUENCE {
    			 *	version           Version, 
    			 *	modulus           INTEGER,  -- n
    			 *	publicExponent    INTEGER,  -- e
    			 *	privateExponent   INTEGER,  -- d
    			 *	prime1            INTEGER,  -- p
    			 *	prime2            INTEGER,  -- q
    			 *	exponent1         INTEGER,  -- d mod (p-1)
    			 *	exponent2         INTEGER,  -- d mod (q-1) 
    			 *	coefficient       INTEGER,  -- (inverse of q) mod p
    			 *	otherPrimeInfos   OtherPrimeInfos OPTIONAL 
    			 * }
    			 */
    			static public RSA DecodeRSA (byte[] keypair) 
    			{
    				Asn1Parser parser = new Asn1Parser();
    				parser.LoadData( keypair );
    				Asn1Node privateKey = parser.RootNode;
    				if (privateKey.Tag != 0x30)
    					throw new CryptographicException ("invalid private key format");
    
    				Asn1Node version = privateKey [0];
    				if (version.Tag != 0x02)
    					throw new CryptographicException ("missing version");
    
    				if (privateKey.Count < 9)
    					throw new CryptographicException ("not enough key parameters");
    
    				RSAParameters param = new RSAParameters ();
    				// note: MUST remove leading 0 - else MS wont import the key
    				param.Modulus = RemoveLeadingZero (privateKey [1].Data);
    				int keysize = param.Modulus.Length;
    				int keysize2 = (keysize >> 1); // half-size
    				// size must be normalized - else MS wont import the key
    				param.D = Normalize (privateKey [3].Data, keysize);
    				param.DP = Normalize (privateKey [6].Data, keysize2);
    				param.DQ = Normalize (privateKey [7].Data, keysize2);
    				param.Exponent = RemoveLeadingZero (privateKey [2].Data);
    				param.InverseQ = Normalize (privateKey [8].Data, keysize2);
    				param.P = Normalize (privateKey [4].Data, keysize2);
    				param.Q = Normalize (privateKey [5].Data, keysize2);
    
    				RSA rsa = RSA.Create ();
    				rsa.ImportParameters (param);
    				return rsa;
    			}
    
    			/*
    			 * RSAPrivateKey ::= SEQUENCE {
    			 *	version           Version, 
    			 *	modulus           INTEGER,  -- n
    			 *	publicExponent    INTEGER,  -- e
    			 *	privateExponent   INTEGER,  -- d
    			 *	prime1            INTEGER,  -- p
    			 *	prime2            INTEGER,  -- q
    			 *	exponent1         INTEGER,  -- d mod (p-1)
    			 *	exponent2         INTEGER,  -- d mod (q-1) 
    			 *	coefficient       INTEGER,  -- (inverse of q) mod p
    			 *	otherPrimeInfos   OtherPrimeInfos OPTIONAL 
    			 * }
    			 */
    			static public byte[] Encode (RSA rsa) 
    			{
    				RSAParameters param = rsa.ExportParameters (true);
    
    				Asn1Node rsaPrivateKey = new Asn1Node (0x30);
    				rsaPrivateKey.Add (new Asn1Node (0x02, new byte [1] { 0x00 }));
    				rsaPrivateKey.Add (Asn1Convert.FromUnsignedBigInteger (param.Modulus));
    				rsaPrivateKey.Add (Asn1Convert.FromUnsignedBigInteger (param.Exponent));
    				rsaPrivateKey.Add (Asn1Convert.FromUnsignedBigInteger (param.D));
    				rsaPrivateKey.Add (Asn1Convert.FromUnsignedBigInteger (param.P));
    				rsaPrivateKey.Add (Asn1Convert.FromUnsignedBigInteger (param.Q));
    				rsaPrivateKey.Add (Asn1Convert.FromUnsignedBigInteger (param.DP));
    				rsaPrivateKey.Add (Asn1Convert.FromUnsignedBigInteger (param.DQ));
    				rsaPrivateKey.Add (Asn1Convert.FromUnsignedBigInteger (param.InverseQ));
    
    				return rsaPrivateKey.GetBytes ();
    			}
    
    			// DSA only encode it's X private key inside an ASN.1 INTEGER (Hint: Tag == 0x02)
    			// which isn't enough for rebuilding the keypair. The other parameters
    			// can be found (98% of the time) in the X.509 certificate associated
    			// with the private key or (2% of the time) the parameters are in it's
    			// issuer X.509 certificate (not supported in the .NET framework).
    			static public DSA DecodeDSA (byte[] privateKey, DSAParameters dsaParameters) 
    			{
    				Asn1Parser parser = new Asn1Parser();
    				parser.LoadData( privateKey );
    				Asn1Node  pvk = parser.RootNode;
    				if (pvk.Tag != 0x02)
    					throw new CryptographicException ("invalid private key format");
    
    				// X is ALWAYS 20 bytes (no matter if the key length is 512 or 1024 bits)
    				dsaParameters.X = Normalize (privateKey, 20);
    				DSA dsa = DSA.Create ();
    				dsa.ImportParameters (dsaParameters);
    				return dsa;
    			}
    
    			static public byte[] Encode (DSA dsa) 
    			{
    				DSAParameters param = dsa.ExportParameters (true);
    				return Asn1Convert.FromUnsignedBigInteger (param.X).GetBytes ();
    			}
    
    			static public byte[] Encode (AsymmetricAlgorithm aa) 
    			{
    				if (aa is RSA)
    					return Encode ((RSA)aa);
    				else if (aa is DSA)
    					return Encode ((DSA)aa);
    				else
    					throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ());
    			}
    		}
    
    		/*
    		 * EncryptedPrivateKeyInfo ::= SEQUENCE {
    		 *	encryptionAlgorithm EncryptionAlgorithmIdentifier,
    		 *	encryptedData EncryptedData 
    		 * }
    		 * 
    		 * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
    		 * 
    		 * EncryptedData ::= OCTET STRING
    		 * 
    		 * --
    		 *  AlgorithmIdentifier  ::= SEQUENCE {
    		 *	algorithm  OBJECT IDENTIFIER,
    		 *	parameters ANY DEFINED BY algorithm OPTIONAL
    		 * }
    		 * 
    		 * -- from PKCS#5
    		 * PBEParameter ::= SEQUENCE {
    		 *	salt OCTET STRING SIZE(8),
    		 *	iterationCount INTEGER 
    		 * }
    		 */
    		public class EncryptedPrivateKeyInfo {
    
    			private string _algorithm;
    			private byte[] _salt;
    			private int _iterations;
    			private byte[] _data;
    
    			public EncryptedPrivateKeyInfo () {}
    
    			public EncryptedPrivateKeyInfo (byte[] data) : this () 
    			{
    				Decode (data);
    			}
    
    			// properties
    
    			public string Algorithm {
    				get { return _algorithm; }
    				set { _algorithm = value; }
    			}
    
    			public byte[] EncryptedData {
    				get { return (_data == null) ? null : (byte[]) _data.Clone (); }
    				set { _data = (value == null) ? null : (byte[]) value.Clone (); }
    			}
    
    			public byte[] Salt {
    				get { 
    					if (_salt == null) {
    						RandomNumberGenerator rng = RandomNumberGenerator.Create ();
    						_salt = new byte [8];
    						rng.GetBytes (_salt);
    					}
    					return (byte[]) _salt.Clone (); 
    				}
    				set { _salt = (byte[]) value.Clone (); }
    			}
    
    			public int IterationCount {
    				get { return _iterations; }
    				set { 
    					if (value < 0)
    						throw new ArgumentOutOfRangeException ("IterationCount", "Negative");
    					_iterations = value; 
    				}
    			}
    
    			// methods
    
    			private void Decode (byte[] data) 
    			{
    				Asn1Parser parser = new Asn1Parser();
    				parser.LoadData( data );
    				Asn1Node  encryptedPrivateKeyInfo = parser.RootNode;
    				if (encryptedPrivateKeyInfo.Tag != 0x30)
    					throw new CryptographicException ("invalid EncryptedPrivateKeyInfo");
    
    				Asn1Node encryptionAlgorithm = encryptedPrivateKeyInfo [0];
    				if (encryptionAlgorithm.Tag != 0x30)
    					throw new CryptographicException ("invalid encryptionAlgorithm");
    				Asn1Node algorithm = encryptionAlgorithm [0];
    				if (algorithm.Tag != 0x06)
    					throw new CryptographicException ("invalid algorithm");
    				_algorithm = Asn1Convert.ToOid (algorithm);
    				// parameters ANY DEFINED BY algorithm OPTIONAL
    				if (encryptionAlgorithm.Count > 1) {
    					Asn1Node parameters = encryptionAlgorithm [1];
    					if (parameters.Tag != 0x30)
    						throw new CryptographicException ("invalid parameters");
    
    					Asn1Node salt = parameters [0];
    					if (salt.Tag != 0x04)
    						throw new CryptographicException ("invalid salt");
    					_salt = salt.Data;
    
    					Asn1Node iterationCount = parameters [1];
    					if (iterationCount.Tag != 0x02)
    						throw new CryptographicException ("invalid iterationCount");
    					_iterations = Asn1Convert.ToInt32 (iterationCount);
    				}
    
    				Asn1Node encryptedData = encryptedPrivateKeyInfo [1];
    				if (encryptedData.Tag != 0x04)
    					throw new CryptographicException ("invalid EncryptedData");
    				_data = encryptedData.Data;
    			}
    
    			// Note: PKCS#8 doesn't define how to generate the key required for encryption
    			// so you're on your own. Just don't try to copy the big guys too much ;)
    			// Netscape:	http://www.cs.auckland.ac.nz/~pgut001/pubs/netscape.txt
    			// Microsoft:	http://www.cs.auckland.ac.nz/~pgut001/pubs/breakms.txt
    			public byte[] GetBytes ()
    			{
    				if (_algorithm == null)
    					throw new CryptographicException ("No algorithm OID specified");
    
    				Asn1Node encryptionAlgorithm = new Asn1Node (0x30);
    				encryptionAlgorithm.Add (Asn1Convert.FromOid (_algorithm));
    
    				// parameters ANY DEFINED BY algorithm OPTIONAL
    				if ((_iterations > 0) || (_salt != null)) {
    					Asn1Node salt = new Asn1Node (0x04, _salt);
    					Asn1Node iterations = Asn1Convert.FromInt32 (_iterations);
    
    					Asn1Node parameters = new Asn1Node (0x30);
    					parameters.Add (salt);
    					parameters.Add (iterations);
    					encryptionAlgorithm.Add (parameters);
    				}
    
    				// encapsulates EncryptedData into an OCTET STRING
    				Asn1Node encryptedData = new Asn1Node (0x04, _data);
    
    				Asn1Node encryptedPrivateKeyInfo = new Asn1Node (0x30);
    				encryptedPrivateKeyInfo.Add (encryptionAlgorithm);
    				encryptedPrivateKeyInfo.Add (encryptedData);
    
    				return encryptedPrivateKeyInfo.GetBytes ();
    			}
    		}
    	}
    }
    

    Thanks in advance!

    Regards
    CSN22


    Viele Grüße / Best regards CSN22

    Friday, August 3, 2012 9:19 AM
  • What I want to do:

    I have a BASE64-GZIP-String that comes as PKCS#7 coded.
    Now I want to decode this string with a PrivateKey that is in a PFX-file; this pfx has two private keys and I need to use the 2nd PK from that file.
    There is a simular thing in the software that is already working fine with the "old" code, so I am trying to use this ...

    Probably there is a much easier way in .NET 2.0 (or 3.5) to do that, than in .NET 1.1 ...

    I am happy for every hint or information. Thanks!


    Viele Grüße / Best regards CSN22


    • Edited by CSN22 Friday, August 3, 2012 9:28 AM
    Friday, August 3, 2012 9:27 AM
  • Hi Csn,

    >>Probably there is a much easier way in .NET 2.0 (or 3.5) to do that, than in .NET 1.1 ...

    I don't think so, since the algorithms doesn't update very much, you way will be still fine.

    Best regards,


    Mike Feng
    MSDN Community Support | Feedback to us
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Thursday, August 9, 2012 1:49 AM
    Moderator