From dead65215310383ca5f7f3f042382e3f95c6b1b8 Mon Sep 17 00:00:00 2001 From: Pablo Ruiz Garcia Date: Wed, 20 Oct 2010 23:36:08 +0200 Subject: [PATCH] [API] X509 related enhacemnts to allow 1) associating keystore's private keys with their corresponding public certificates found at X509Stores, and 2) importing private keys to X509Stores along with certificates. mcs/class/corlib/Mono.Security.X509/X509Store.cs: mcs/class/Mono.Security/Mono.Security.X509/X509Store.cs: mcs/class/corlib/Mono.Security.X509/X509StoreManager.cs: mcs/class/Mono.Security/Mono.Security.X509/X509StoreManager.cs: Modified MSX.X509Store to allow importing privateKeys along certificates (by using RSACryptoServiceProvider and KeyPairPersister). Fixed X509Store to use certificate's hash as CspParameters.KeyContainerName. mcs/tools/security/certmgr.cs: Added a new -importKey action to certmgr, which allows importing a PKCS12 contained key into our KeyPair store. Added a more (verbose) output to certmgr's -list action. man/certmgr.1: Updated certmgr man page ti reflect -importKey addition. mcs/class/corlib/System.Security.Cryptography/CspParameters.cs: mcs/class/corlib/System.Security.Cryptography/RSACryptoServiceProvider.cs: Implemented RSACryptoServiceProvider.CspKeyContainerInfo getter RSACryptoServiceProvider will not take into account CspParameterFlags.UseExistingKey by throwing a CryptographicException if keyset identified by KeyContainerName does not exists. --- man/certmgr.1 | 19 ++- .../Mono.Security.X509/X509Store.cs | 77 +++++++++- .../Mono.Security.X509/X509StoreManager.cs | 50 ++++--- .../corlib/Mono.Security.X509/X509Store.cs | 77 +++++++++- .../Mono.Security.X509/X509StoreManager.cs | 52 ++++--- .../CspParameters.cs | 16 ++- .../RSACryptoServiceProvider.cs | 13 +- mcs/tools/security/certmgr.cs | 131 ++++++++++++++---- 8 files changed, 350 insertions(+), 85 deletions(-) diff --git a/man/certmgr.1 b/man/certmgr.1 index b67ee8fe3c0a..04100342a006 100644 --- a/man/certmgr.1 +++ b/man/certmgr.1 @@ -1,8 +1,10 @@ .\" .\" certmgr manual page. .\" Copyright 2004-2005 Novell +.\" Copyright 2010 Pablo Ruiz .\" Author: .\" Sebastien Pouliot +.\" Pablo Ruiz Garcia .\" .TH Mono "certmgr" .SH NAME @@ -24,7 +26,9 @@ server certificates. List the certificates, CTL or CTL in the specified store. .TP .I "-add" -Add a certificate, CRL or CTL to specified store. +Add a certificate, CRL or CTL to specified store. If filename it's a pkcs12 +or pfx file, and it contains a private key, it will be imported to local key +pair container. .TP .I "-del" Remove a certificate, CRL or CTL from specified store. You must specify the @@ -44,6 +48,11 @@ This action assume an certificate (-c) object type and will import the certificates in appropriate stores (i.e. server certificate in the OtherPeople store, the root certificate in the Trust store, any other intermediate certificates in the IntermediateCA store). +.TP +.I "-importKey" +Allows importing a private key from a pkcs12 file into a local key pair +store. (Usefull when you already have the key's corresponding certificate +installed at the specific store.) .SH OBJECT TYPES .TP @@ -66,6 +75,9 @@ Use the machine's certificate stores (instead of the default user's stores). .I "-v" More details displayed on the console. .TP +.I "-p password" +Use the specify password when accessing a pkcs12 file. +.TP .I "-help", "-h", "-?", "/?" Display help about this tool. @@ -88,6 +100,9 @@ The filenames either starts with (subject key identifier). .TP The rest of the filename is the base64-encoded value (tbp or ski). +.TP +Private key data is stored under +.I ~/.config/.mono/keypairs/ .SH EXAMPLES .TP @@ -140,6 +155,8 @@ element of your machine.config file. .SH AUTHOR Written by Sebastien Pouliot + +Minor additions by Pablo Ruiz GarcĂ­a .SH COPYRIGHT Copyright (C) 2004-2005 Novell. .SH MAILING LISTS diff --git a/mcs/class/Mono.Security/Mono.Security.X509/X509Store.cs b/mcs/class/Mono.Security/Mono.Security.X509/X509Store.cs index 1acd57d890e4..11461804b069 100644 --- a/mcs/class/Mono.Security/Mono.Security.X509/X509Store.cs +++ b/mcs/class/Mono.Security/Mono.Security.X509/X509Store.cs @@ -3,8 +3,10 @@ // // Author: // Sebastien Pouliot +// Pablo Ruiz // // Copyright (C) 2004 Novell, Inc (http://www.novell.com) +// (C) 2010 Pablo Ruiz. // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the @@ -30,8 +32,10 @@ using System.Collections; using System.Globalization; using System.IO; -using System.Text; - +using System.Text; +using System.Security.Cryptography; + +using Mono.Security.Cryptography; using Mono.Security.X509.Extensions; namespace Mono.Security.X509 { @@ -41,8 +45,8 @@ namespace Mono.Security.X509 { #else public #endif - class X509Store { - + class X509Store { + private string _storePath; private X509CertificateCollection _certificates; private ArrayList _crls; @@ -114,6 +118,16 @@ public void Import (X509Certificate certificate) fs.Close (); } } + + // Try to save privateKey if available.. + CspParameters cspParams = new CspParameters (); + cspParams.KeyContainerName = CryptoConvert.ToHex (certificate.Hash); + + // Right now this seems to be the best way to know if we should use LM store.. ;) + if (_storePath.StartsWith (X509StoreManager.LocalMachinePath)) + cspParams.Flags = CspProviderFlags.UseMachineKeyStore; + + ImportPrivateKey (certificate, cspParams); } public void Import (X509Crl crl) @@ -127,7 +141,7 @@ public void Import (X509Crl crl) fs.Write (data, 0, data.Length); } } - } + } public void Remove (X509Certificate certificate) { @@ -188,6 +202,7 @@ private byte[] GetUniqueName (X509ExtensionCollection extensions) private string GetUniqueName (string method, byte[] name, string fileExtension) { StringBuilder sb = new StringBuilder (method); + sb.Append ("-"); foreach (byte b in name) { sb.Append (b.ToString ("X2", CultureInfo.InvariantCulture)); @@ -195,7 +210,7 @@ private string GetUniqueName (string method, byte[] name, string fileExtension) sb.Append (fileExtension); return sb.ToString (); - } + } private byte[] Load (string filename) { @@ -212,6 +227,21 @@ private X509Certificate LoadCertificate (string filename) { byte[] data = Load (filename); X509Certificate cert = new X509Certificate (data); + + // If privateKey it's available, load it too.. + CspParameters cspParams = new CspParameters (); + cspParams.KeyContainerName = CryptoConvert.ToHex (cert.Hash); + cspParams.Flags = CspProviderFlags.UseMachineKeyStore; + KeyPairPersistence kpp = new KeyPairPersistence (cspParams); + + if (!kpp.Load ()) + return cert; + + if (cert.RSA != null) + cert.RSA = new RSACryptoServiceProvider (cspParams); + else if (cert.DSA != null) + cert.DSA = new DSACryptoServiceProvider (cspParams); + return cert; } @@ -282,6 +312,41 @@ private ArrayList BuildCrlsCollection (string storeName) } } return list; + } + + private void ImportPrivateKey (X509Certificate certificate, CspParameters cspParams) + { + RSACryptoServiceProvider rsaCsp = certificate.RSA as RSACryptoServiceProvider; + if (rsaCsp != null) { + if (rsaCsp.PublicOnly) + return; + + RSACryptoServiceProvider csp = new RSACryptoServiceProvider(cspParams); + csp.ImportParameters(rsaCsp.ExportParameters(true)); + csp.PersistKeyInCsp = true; + return; + } + + RSAManaged rsaMng = certificate.RSA as RSAManaged; + if (rsaMng != null) { + if (rsaMng.PublicOnly) + return; + + RSACryptoServiceProvider csp = new RSACryptoServiceProvider(cspParams); + csp.ImportParameters(rsaMng.ExportParameters(true)); + csp.PersistKeyInCsp = true; + return; + } + + DSACryptoServiceProvider dsaCsp = certificate.DSA as DSACryptoServiceProvider; + if (dsaCsp != null) { + if (dsaCsp.PublicOnly) + return; + + DSACryptoServiceProvider csp = new DSACryptoServiceProvider(cspParams); + csp.ImportParameters(dsaCsp.ExportParameters(true)); + csp.PersistKeyInCsp = true; + } } } } diff --git a/mcs/class/Mono.Security/Mono.Security.X509/X509StoreManager.cs b/mcs/class/Mono.Security/Mono.Security.X509/X509StoreManager.cs index 50f45b47b28a..bbdef604c11f 100644 --- a/mcs/class/Mono.Security/Mono.Security.X509/X509StoreManager.cs +++ b/mcs/class/Mono.Security/Mono.Security.X509/X509StoreManager.cs @@ -41,8 +41,10 @@ namespace Mono.Security.X509 { #else public #endif - sealed class X509StoreManager { - + sealed class X509StoreManager { + + static private string _userPath; + static private string _localMachinePath; static private X509Stores _userStore; static private X509Stores _machineStore; @@ -50,30 +52,44 @@ private X509StoreManager () { } - static public X509Stores CurrentUser { - get { - if (_userStore == null) { - string _userPath = Path.Combine ( - Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData), + internal static string CurrentUserPath { + get { + if (_userPath == null) { + _userPath = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + ".mono"); + _userPath = Path.Combine(_userPath, "certs"); + } + return _userPath; + } + } + + internal static string LocalMachinePath { + get { + if (_localMachinePath == null) { + _localMachinePath = Path.Combine ( + Environment.GetFolderPath (Environment.SpecialFolder.CommonApplicationData), ".mono"); - _userPath = Path.Combine (_userPath, "certs"); + _localMachinePath = Path.Combine (_localMachinePath, "certs"); + } + return _localMachinePath; + } + } - _userStore = new X509Stores (_userPath); - } + static public X509Stores CurrentUser { + get { + if (_userStore == null) + _userStore = new X509Stores(CurrentUserPath); + return _userStore; } } static public X509Stores LocalMachine { get { - if (_machineStore == null) { - string _machinePath = Path.Combine ( - Environment.GetFolderPath (Environment.SpecialFolder.CommonApplicationData), - ".mono"); - _machinePath = Path.Combine (_machinePath, "certs"); + if (_machineStore == null) + _machineStore = new X509Stores (LocalMachinePath); - _machineStore = new X509Stores (_machinePath); - } return _machineStore; } } diff --git a/mcs/class/corlib/Mono.Security.X509/X509Store.cs b/mcs/class/corlib/Mono.Security.X509/X509Store.cs index 1acd57d890e4..11461804b069 100644 --- a/mcs/class/corlib/Mono.Security.X509/X509Store.cs +++ b/mcs/class/corlib/Mono.Security.X509/X509Store.cs @@ -3,8 +3,10 @@ // // Author: // Sebastien Pouliot +// Pablo Ruiz // // Copyright (C) 2004 Novell, Inc (http://www.novell.com) +// (C) 2010 Pablo Ruiz. // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the @@ -30,8 +32,10 @@ using System.Collections; using System.Globalization; using System.IO; -using System.Text; - +using System.Text; +using System.Security.Cryptography; + +using Mono.Security.Cryptography; using Mono.Security.X509.Extensions; namespace Mono.Security.X509 { @@ -41,8 +45,8 @@ namespace Mono.Security.X509 { #else public #endif - class X509Store { - + class X509Store { + private string _storePath; private X509CertificateCollection _certificates; private ArrayList _crls; @@ -114,6 +118,16 @@ public void Import (X509Certificate certificate) fs.Close (); } } + + // Try to save privateKey if available.. + CspParameters cspParams = new CspParameters (); + cspParams.KeyContainerName = CryptoConvert.ToHex (certificate.Hash); + + // Right now this seems to be the best way to know if we should use LM store.. ;) + if (_storePath.StartsWith (X509StoreManager.LocalMachinePath)) + cspParams.Flags = CspProviderFlags.UseMachineKeyStore; + + ImportPrivateKey (certificate, cspParams); } public void Import (X509Crl crl) @@ -127,7 +141,7 @@ public void Import (X509Crl crl) fs.Write (data, 0, data.Length); } } - } + } public void Remove (X509Certificate certificate) { @@ -188,6 +202,7 @@ private byte[] GetUniqueName (X509ExtensionCollection extensions) private string GetUniqueName (string method, byte[] name, string fileExtension) { StringBuilder sb = new StringBuilder (method); + sb.Append ("-"); foreach (byte b in name) { sb.Append (b.ToString ("X2", CultureInfo.InvariantCulture)); @@ -195,7 +210,7 @@ private string GetUniqueName (string method, byte[] name, string fileExtension) sb.Append (fileExtension); return sb.ToString (); - } + } private byte[] Load (string filename) { @@ -212,6 +227,21 @@ private X509Certificate LoadCertificate (string filename) { byte[] data = Load (filename); X509Certificate cert = new X509Certificate (data); + + // If privateKey it's available, load it too.. + CspParameters cspParams = new CspParameters (); + cspParams.KeyContainerName = CryptoConvert.ToHex (cert.Hash); + cspParams.Flags = CspProviderFlags.UseMachineKeyStore; + KeyPairPersistence kpp = new KeyPairPersistence (cspParams); + + if (!kpp.Load ()) + return cert; + + if (cert.RSA != null) + cert.RSA = new RSACryptoServiceProvider (cspParams); + else if (cert.DSA != null) + cert.DSA = new DSACryptoServiceProvider (cspParams); + return cert; } @@ -282,6 +312,41 @@ private ArrayList BuildCrlsCollection (string storeName) } } return list; + } + + private void ImportPrivateKey (X509Certificate certificate, CspParameters cspParams) + { + RSACryptoServiceProvider rsaCsp = certificate.RSA as RSACryptoServiceProvider; + if (rsaCsp != null) { + if (rsaCsp.PublicOnly) + return; + + RSACryptoServiceProvider csp = new RSACryptoServiceProvider(cspParams); + csp.ImportParameters(rsaCsp.ExportParameters(true)); + csp.PersistKeyInCsp = true; + return; + } + + RSAManaged rsaMng = certificate.RSA as RSAManaged; + if (rsaMng != null) { + if (rsaMng.PublicOnly) + return; + + RSACryptoServiceProvider csp = new RSACryptoServiceProvider(cspParams); + csp.ImportParameters(rsaMng.ExportParameters(true)); + csp.PersistKeyInCsp = true; + return; + } + + DSACryptoServiceProvider dsaCsp = certificate.DSA as DSACryptoServiceProvider; + if (dsaCsp != null) { + if (dsaCsp.PublicOnly) + return; + + DSACryptoServiceProvider csp = new DSACryptoServiceProvider(cspParams); + csp.ImportParameters(dsaCsp.ExportParameters(true)); + csp.PersistKeyInCsp = true; + } } } } diff --git a/mcs/class/corlib/Mono.Security.X509/X509StoreManager.cs b/mcs/class/corlib/Mono.Security.X509/X509StoreManager.cs index d7b9c2c39fe4..bbdef604c11f 100644 --- a/mcs/class/corlib/Mono.Security.X509/X509StoreManager.cs +++ b/mcs/class/corlib/Mono.Security.X509/X509StoreManager.cs @@ -7,8 +7,6 @@ // (C) 2004 Novell (http://www.novell.com) // -// -// Copyright (C) 2004 Novell, Inc (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the @@ -43,8 +41,10 @@ namespace Mono.Security.X509 { #else public #endif - sealed class X509StoreManager { - + sealed class X509StoreManager { + + static private string _userPath; + static private string _localMachinePath; static private X509Stores _userStore; static private X509Stores _machineStore; @@ -52,30 +52,44 @@ private X509StoreManager () { } - static public X509Stores CurrentUser { - get { - if (_userStore == null) { - string _userPath = Path.Combine ( - Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData), + internal static string CurrentUserPath { + get { + if (_userPath == null) { + _userPath = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + ".mono"); + _userPath = Path.Combine(_userPath, "certs"); + } + return _userPath; + } + } + + internal static string LocalMachinePath { + get { + if (_localMachinePath == null) { + _localMachinePath = Path.Combine ( + Environment.GetFolderPath (Environment.SpecialFolder.CommonApplicationData), ".mono"); - _userPath = Path.Combine (_userPath, "certs"); + _localMachinePath = Path.Combine (_localMachinePath, "certs"); + } + return _localMachinePath; + } + } - _userStore = new X509Stores (_userPath); - } + static public X509Stores CurrentUser { + get { + if (_userStore == null) + _userStore = new X509Stores(CurrentUserPath); + return _userStore; } } static public X509Stores LocalMachine { get { - if (_machineStore == null) { - string _machinePath = Path.Combine ( - Environment.GetFolderPath (Environment.SpecialFolder.CommonApplicationData), - ".mono"); - _machinePath = Path.Combine (_machinePath, "certs"); + if (_machineStore == null) + _machineStore = new X509Stores (LocalMachinePath); - _machineStore = new X509Stores (_machinePath); - } return _machineStore; } } diff --git a/mcs/class/corlib/System.Security.Cryptography/CspParameters.cs b/mcs/class/corlib/System.Security.Cryptography/CspParameters.cs index 653471ed43cc..0df95669944f 100644 --- a/mcs/class/corlib/System.Security.Cryptography/CspParameters.cs +++ b/mcs/class/corlib/System.Security.Cryptography/CspParameters.cs @@ -61,7 +61,7 @@ public CspParameters (int dwTypeIn, string strProviderNameIn, string strContaine // not defined in specs, only tested from M$ impl KeyNumber = -1; } - + public string KeyContainerName; public int KeyNumber; @@ -94,7 +94,19 @@ public CspParameters (int providerType, string providerName, string keyContainer if (cryptoKeySecurity != null) CryptoKeySecurity = cryptoKeySecurity; _password = keyPassword; - } + } + + internal CspParameters(CspParameters parameters) + : this(parameters.ProviderType, parameters.ProviderName, parameters.KeyContainerName) + { + if (parameters.CryptoKeySecurity != null) + CryptoKeySecurity = parameters.CryptoKeySecurity; + + _Flags = parameters.Flags; + KeyNumber = parameters.KeyNumber; + _password = parameters.KeyPassword; + _windowHandle = parameters.ParentWindowHandle; + } [MonoTODO ("access control isn't implemented")] public CryptoKeySecurity CryptoKeySecurity { diff --git a/mcs/class/corlib/System.Security.Cryptography/RSACryptoServiceProvider.cs b/mcs/class/corlib/System.Security.Cryptography/RSACryptoServiceProvider.cs index ef607bc5fd28..cf8b7e958ee2 100644 --- a/mcs/class/corlib/System.Security.Cryptography/RSACryptoServiceProvider.cs +++ b/mcs/class/corlib/System.Security.Cryptography/RSACryptoServiceProvider.cs @@ -105,7 +105,12 @@ private void Common (int dwKeySize, CspParameters p) } else { store = new KeyPairPersistence (p); - store.Load (); + bool exists = store.Load (); + bool required = (p.Flags & CspProviderFlags.UseExistingKey) != 0; + + if (required && !exists) + throw new CryptographicException ("Keyset does not exist"); + if (store.KeyValue != null) { persisted = true; this.FromXmlString (store.KeyValue); @@ -355,11 +360,11 @@ private void OnKeyGenerated (object sender, EventArgs e) } // ICspAsymmetricAlgorithm - [MonoTODO ("Always return null")] - // FIXME: call into KeyPairPersistence to get details [ComVisible (false)] public CspKeyContainerInfo CspKeyContainerInfo { - get { return null; } + get { + return new CspKeyContainerInfo(store.Parameters); + } } [ComVisible (false)] diff --git a/mcs/tools/security/certmgr.cs b/mcs/tools/security/certmgr.cs index 5799fcfbaa6b..1cf1789070e5 100644 --- a/mcs/tools/security/certmgr.cs +++ b/mcs/tools/security/certmgr.cs @@ -8,8 +8,9 @@ // using System; -using System.Collections; -using System.IO; +using System.Collections; +using System.Globalization; +using System.IO; using System.Net; using System.Net.Sockets; using System.Reflection; @@ -40,21 +41,24 @@ static private void Help () Console.WriteLine (" or: certmgr -list object-type [options] store"); Console.WriteLine (" or: certmgr -del object-type [options] store certhash"); Console.WriteLine (" or: certmgr -ssl [options] url"); + Console.WriteLine (" or: certmgr -importKey [options] store pkcs12file"); Console.WriteLine (); Console.WriteLine ("actions"); - Console.WriteLine ("\t-add\tAdd a certificate, CRL or CTL to specified store"); - Console.WriteLine ("\t-del\tRemove a certificate, CRL or CTL to specified store"); - Console.WriteLine ("\t-put\tCopy a certificate, CRL or CTL from a store to a file"); - Console.WriteLine ("\t-list\tList certificates, CRL ot CTL in the specified store."); - Console.WriteLine ("\t-ssl\tDownload and add certificates from an SSL session"); + Console.WriteLine ("\t-add\t\tAdd a certificate, CRL or CTL to specified store"); + Console.WriteLine ("\t-del\t\tRemove a certificate, CRL or CTL to specified store"); + Console.WriteLine ("\t-put\t\tCopy a certificate, CRL or CTL from a store to a file"); + Console.WriteLine ("\t-list\t\tList certificates, CRL ot CTL in the specified store."); + Console.WriteLine ("\t-ssl\t\tDownload and add certificates from an SSL session"); + Console.WriteLine ("\t-importKey\tImport PKCS12 privateKey to keypair store."); Console.WriteLine ("object types"); - Console.WriteLine ("\t-c\tadd/del/put certificates"); - Console.WriteLine ("\t-crl\tadd/del/put certificate revocation lists"); - Console.WriteLine ("\t-ctl\tadd/del/put certificate trust lists [unsupported]"); + Console.WriteLine ("\t-c\t\tadd/del/put certificates"); + Console.WriteLine ("\t-crl\t\tadd/del/put certificate revocation lists"); + Console.WriteLine ("\t-ctl\t\tadd/del/put certificate trust lists [unsupported]"); Console.WriteLine ("other options"); - Console.WriteLine ("\t-m\tuse the machine certificate store (default to user)"); - Console.WriteLine ("\t-v\tverbose mode (display status for every steps)"); - Console.WriteLine ("\t-?\th[elp]\tDisplay this help message"); + Console.WriteLine ("\t-m\t\tuse the machine certificate store (default to user)"); + Console.WriteLine ("\t-v\t\tverbose mode (display status for every steps)"); + Console.WriteLine ("\t-p [password]\tPassword used to decrypt PKCS12"); + Console.WriteLine ("\t-?\t\th[elp]\tDisplay this help message"); Console.WriteLine (); } @@ -82,7 +86,8 @@ enum Action { Delete, Put, List, - Ssl + Ssl, + ImportKey } static Action GetAction (string arg) @@ -107,6 +112,9 @@ static Action GetAction (string arg) case "TLS": action = Action.Ssl; break; + case "IMPORTKEY": + action = Action.ImportKey; + break; } return action; } @@ -168,7 +176,7 @@ static byte[] PEM (string type, byte[] data) return Convert.FromBase64String (base64); } - static X509CertificateCollection LoadCertificates (string filename) + static X509CertificateCollection LoadCertificates (string filename, string password, bool verbose) { X509Certificate x509 = null; X509CertificateCollection coll = new X509CertificateCollection (); @@ -195,11 +203,25 @@ static X509CertificateCollection LoadCertificates (string filename) coll.Add (x509); break; case ".P12": - case ".PFX": - // TODO - support PKCS12 with passwords - PKCS12 p12 = PKCS12.LoadFromFile (filename); - coll.AddRange (p12.Certificates); - p12 = null; + case ".PFX": + PKCS12 p12 = password == null ? PKCS12.LoadFromFile (filename) + : PKCS12.LoadFromFile (filename, password); + X509CertificateCollection tmp = new X509CertificateCollection (p12.Certificates); + + for (int i = 0; i != p12.Keys.Count; i++) { + X509Certificate cert = p12.Certificates[i]; + RSACryptoServiceProvider pk = p12.Keys[i] as RSACryptoServiceProvider; + + if (pk == null || pk.PublicOnly) + continue; + + if (verbose) + Console.WriteLine ("Found key for certificate: {0}", cert.SubjectName); + + tmp[0].RSA = pk; + } + coll.AddRange(tmp); + p12 = null; break; default: Console.WriteLine ("Unknown file extension: {0}", @@ -236,11 +258,11 @@ static ArrayList LoadCRLs (string filename) return list; } - static void Add (ObjectType type, X509Store store, string file, bool verbose) + static void Add (ObjectType type, X509Store store, string file, string password, bool verbose) { switch (type) { case ObjectType.Certificate: - X509CertificateCollection coll = LoadCertificates (file); + X509CertificateCollection coll = LoadCertificates (file, password, verbose); foreach (X509Certificate x509 in coll) { store.Import (x509); } @@ -286,7 +308,7 @@ static void Delete (ObjectType type, X509Store store, string hash, bool verbose) } } - static void Put (ObjectType type, X509Store store, string file, bool verbose) + static void Put (ObjectType type, X509Store store, string file, string password, bool verbose) { throw new NotImplementedException ("Put not yet supported"); /* switch (type) { @@ -300,7 +322,7 @@ static void Put (ObjectType type, X509Store store, string file, bool verbose) }*/ } - static void DisplayCertificate (X509Certificate x509, bool verbose) + static void DisplayCertificate (X509Certificate x509, bool machine, bool verbose) { Console.WriteLine ("{0}X.509 v{1} Certificate", (x509.IsSelfSigned ? "Self-signed " : String.Empty), x509.Version); Console.WriteLine (" Serial Number: {0}", CryptoConvert.ToHex (x509.SerialNumber)); @@ -318,16 +340,25 @@ static void DisplayCertificate (X509Certificate x509, bool verbose) Console.WriteLine (" Algorithm Parameters: {0}", (x509.SignatureAlgorithmParameters == null) ? "None" : CryptoConvert.ToHex (x509.SignatureAlgorithmParameters)); Console.WriteLine (" Signature: {0}", CryptoConvert.ToHex (x509.Signature)); + RSACryptoServiceProvider rsaCsp = x509.RSA as RSACryptoServiceProvider; + RSAManaged rsaManaged = x509.RSA as RSAManaged; + Console.WriteLine (" Private Key: {0}", ((rsaCsp != null && !rsaCsp.PublicOnly) + || (rsaManaged != null && !rsaManaged.PublicOnly))); + CspParameters cspParams = new CspParameters (); + cspParams.KeyContainerName = CryptoConvert.ToHex (x509.Hash); + cspParams.Flags = machine ? CspProviderFlags.UseMachineKeyStore : 0; + KeyPairPersistence kpp = new KeyPairPersistence (cspParams); + Console.WriteLine (" KeyPair Key: {0}", kpp.Load ()); } Console.WriteLine (); } - static void List (ObjectType type, X509Store store, string file, bool verbose) + static void List (ObjectType type, X509Store store, bool machine, string file, bool verbose) { switch (type) { case ObjectType.Certificate: foreach (X509Certificate x509 in store.Certificates) { - DisplayCertificate (x509, verbose); + DisplayCertificate (x509, machine, verbose); } break; case ObjectType.CRL: @@ -479,9 +510,40 @@ static void Ssl (string host, bool machine, bool verbose) } } + static void ImportKey (ObjectType type, bool machine, string file, string password, bool verbose) + { + switch (type) { + case ObjectType.Certificate: + X509CertificateCollection coll = LoadCertificates (file, password, verbose); + int count = 0; + + foreach (X509Certificate x509 in coll) { + RSACryptoServiceProvider pk = x509.RSA as RSACryptoServiceProvider; + + if (pk == null || pk.PublicOnly) + continue; + + CspParameters csp = new CspParameters (); + csp.KeyContainerName = CryptoConvert.ToHex (x509.Hash); + csp.Flags = machine ? CspProviderFlags.UseMachineKeyStore : 0; + RSACryptoServiceProvider rsa = new RSACryptoServiceProvider (csp); + rsa.ImportParameters (pk.ExportParameters (true)); + rsa.PersistKeyInCsp = true; + count++; + } + Console.WriteLine ("{0} keys(s) imported to KeyPair {1} persister.", + count, machine ? "LocalMachine" : "CurrentUser"); + break; + default: + throw new NotSupportedException (type.ToString ()); + } + } + [STAThread] static void Main (string[] args) - { + { + string password = null; + Header (); if (args.Length < 2) { Help (); @@ -505,6 +567,12 @@ static void Main (string[] args) if (machine) n++; + if (GetCommand (args [n]) == "P") + { + n++; + password = args[n++]; + } + X509Store store = null; string storeName = null; if (action != Action.Ssl) { @@ -537,20 +605,23 @@ static void Main (string[] args) try { switch (action) { case Action.Add: - Add (type, store, file, verbose); + Add (type, store, file, password, verbose); break; case Action.Delete: Delete (type, store, file, verbose); break; case Action.Put: - Put (type, store, file, verbose); + Put (type, store, file, password, verbose); break; case Action.List: - List (type, store, file, verbose); + List (type, store, machine, file, verbose); break; case Action.Ssl: Ssl (file, machine, verbose); break; + case Action.ImportKey: + ImportKey (type, machine, file, password, verbose); + break; default: throw new NotSupportedException (action.ToString ()); }