a575963da9
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
720 lines
22 KiB
C#
720 lines
22 KiB
C#
//
|
|
// CertMgr.cs: Certificate Manager clone tool (CLI version)
|
|
//
|
|
// Author:
|
|
// Sebastien Pouliot <sebastien@ximian.com>
|
|
//
|
|
// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
|
|
//
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Reflection;
|
|
using System.Security.Cryptography;
|
|
using SSCX = System.Security.Cryptography.X509Certificates;
|
|
using System.Text;
|
|
|
|
using Mono.Security.Authenticode;
|
|
using Mono.Security.Cryptography;
|
|
using Mono.Security.X509;
|
|
using Mono.Security.Protocol.Tls;
|
|
|
|
[assembly: AssemblyTitle ("Mono Certificate Manager")]
|
|
[assembly: AssemblyDescription ("Manage X.509 certificates and CRL from stores.")]
|
|
|
|
namespace Mono.Tools {
|
|
|
|
class CertificateManager {
|
|
|
|
static private void Header ()
|
|
{
|
|
Console.WriteLine (new AssemblyInfo ().ToString ());
|
|
}
|
|
|
|
static private void Help ()
|
|
{
|
|
Console.WriteLine ("Usage: certmgr [action] [object-type] [options] store [filename]");
|
|
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 -put object-type [options] store certfile");
|
|
Console.WriteLine (" or: certmgr -importKey [options] store pkcs12file");
|
|
Console.WriteLine ();
|
|
Console.WriteLine ("actions");
|
|
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 or 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\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\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-pem\t\tPut certificate in Base-64 encoded format (default DER encoded)");
|
|
Console.WriteLine ("\t-?\t\th[elp]\tDisplay this help message");
|
|
Console.WriteLine ();
|
|
}
|
|
|
|
static string GetCommand (string arg)
|
|
{
|
|
if ((arg == null) || (arg.Length < 1))
|
|
return null;
|
|
|
|
switch (arg [0]) {
|
|
case '/':
|
|
return arg.Substring (1).ToUpper ();
|
|
case '-':
|
|
if (arg.Length < 2)
|
|
return null;
|
|
int n = ((arg [1] == '-') ? 2 : 1);
|
|
return arg.Substring (n).ToUpper ();
|
|
default:
|
|
return arg;
|
|
}
|
|
}
|
|
|
|
enum Action {
|
|
None,
|
|
Add,
|
|
Delete,
|
|
Put,
|
|
List,
|
|
Ssl,
|
|
ImportKey
|
|
}
|
|
|
|
static Action GetAction (string arg)
|
|
{
|
|
Action action = Action.None;
|
|
switch (GetCommand (arg)) {
|
|
case "ADD":
|
|
action = Action.Add;
|
|
break;
|
|
case "DEL":
|
|
case "DELETE":
|
|
action = Action.Delete;
|
|
break;
|
|
case "PUT":
|
|
action = Action.Put;
|
|
break;
|
|
case "LST":
|
|
case "LIST":
|
|
action = Action.List;
|
|
break;
|
|
case "SSL":
|
|
case "TLS":
|
|
action = Action.Ssl;
|
|
break;
|
|
case "IMPORTKEY":
|
|
action = Action.ImportKey;
|
|
break;
|
|
}
|
|
return action;
|
|
}
|
|
|
|
enum ObjectType {
|
|
None,
|
|
Certificate,
|
|
CRL,
|
|
CTL
|
|
}
|
|
|
|
static ObjectType GetObjectType (string arg)
|
|
{
|
|
ObjectType type = ObjectType.None;
|
|
switch (GetCommand (arg)) {
|
|
case "C":
|
|
case "CERT":
|
|
case "CERTIFICATE":
|
|
type = ObjectType.Certificate;
|
|
break;
|
|
case "CRL":
|
|
type = ObjectType.CRL;
|
|
break;
|
|
case "CTL":
|
|
type = ObjectType.CTL;
|
|
break;
|
|
}
|
|
return type;
|
|
}
|
|
|
|
static X509Store GetStoreFromName (string storeName, bool machine)
|
|
{
|
|
X509Stores stores = ((machine) ? X509StoreManager.LocalMachine : X509StoreManager.CurrentUser);
|
|
X509Store store = null;
|
|
switch (storeName) {
|
|
case X509Stores.Names.Personal:
|
|
return stores.Personal;
|
|
case X509Stores.Names.OtherPeople:
|
|
return stores.OtherPeople;
|
|
case X509Stores.Names.IntermediateCA:
|
|
return stores.IntermediateCA;
|
|
case "Root": // special case (same as trusted root)
|
|
case X509Stores.Names.TrustedRoot:
|
|
return stores.TrustedRoot;
|
|
case X509Stores.Names.Untrusted:
|
|
return stores.Untrusted;
|
|
}
|
|
return store;
|
|
}
|
|
|
|
static byte[] PEM (string type, byte[] data)
|
|
{
|
|
string pem = Encoding.ASCII.GetString (data);
|
|
string header = String.Format ("-----BEGIN {0}-----", type);
|
|
string footer = String.Format ("-----END {0}-----", type);
|
|
int start = pem.IndexOf (header) + header.Length;
|
|
int end = pem.IndexOf (footer, start);
|
|
string base64 = pem.Substring (start, (end - start));
|
|
return Convert.FromBase64String (base64);
|
|
}
|
|
|
|
static byte[] ToPEM (string type, byte[] data)
|
|
{
|
|
string header = String.Format ("-----BEGIN {0}-----", type);
|
|
string footer = String.Format ("-----END {0}-----", type);
|
|
|
|
string encodedString = Convert.ToBase64String (data);
|
|
|
|
StringBuilder sb = new StringBuilder ();
|
|
int remaining = encodedString.Length;
|
|
sb.AppendLine (header);
|
|
for (int i = 0; i <= encodedString.Length; i += 64) {
|
|
if (remaining >= 64) {
|
|
sb.AppendLine (encodedString.Substring (i, 64));
|
|
} else {
|
|
sb.AppendLine (encodedString.Substring (i, remaining));
|
|
}
|
|
remaining -= 64;
|
|
}
|
|
sb.AppendLine (footer);
|
|
return Encoding.ASCII.GetBytes (sb.ToString ());
|
|
}
|
|
|
|
static X509CertificateCollection LoadCertificates (string filename, string password, bool verbose)
|
|
{
|
|
X509Certificate x509 = null;
|
|
X509CertificateCollection coll = new X509CertificateCollection ();
|
|
switch (Path.GetExtension (filename).ToUpper ()) {
|
|
case ".P7B":
|
|
case ".SPC":
|
|
SoftwarePublisherCertificate spc = SoftwarePublisherCertificate.CreateFromFile (filename);
|
|
coll.AddRange (spc.Certificates);
|
|
spc = null;
|
|
break;
|
|
case ".CER":
|
|
case ".CRT":
|
|
using (FileStream fs = File.OpenRead (filename)) {
|
|
byte[] data = new byte [fs.Length];
|
|
fs.Read (data, 0, data.Length);
|
|
if (data [0] != 0x30) {
|
|
// maybe it's ASCII PEM base64 encoded ?
|
|
data = PEM ("CERTIFICATE", data);
|
|
}
|
|
if (data != null)
|
|
x509 = new X509Certificate (data);
|
|
}
|
|
if (x509 != null)
|
|
coll.Add (x509);
|
|
break;
|
|
case ".P12":
|
|
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}",
|
|
Path.GetExtension (filename));
|
|
break;
|
|
}
|
|
return coll;
|
|
}
|
|
|
|
static ArrayList LoadCRLs (string filename)
|
|
{
|
|
X509Crl crl = null;
|
|
ArrayList list = new ArrayList ();
|
|
switch (Path.GetExtension (filename).ToUpper ()) {
|
|
case ".P7B":
|
|
case ".SPC":
|
|
SoftwarePublisherCertificate spc = SoftwarePublisherCertificate.CreateFromFile (filename);
|
|
list.AddRange (spc.Crls);
|
|
spc = null;
|
|
break;
|
|
case ".CRL":
|
|
using (FileStream fs = File.OpenRead (filename)) {
|
|
byte[] data = new byte [fs.Length];
|
|
fs.Read (data, 0, data.Length);
|
|
crl = new X509Crl (data);
|
|
}
|
|
list.Add (crl);
|
|
break;
|
|
default:
|
|
Console.WriteLine ("Unknown file extension: {0}",
|
|
Path.GetExtension (filename));
|
|
break;
|
|
}
|
|
return list;
|
|
}
|
|
|
|
static void Add (ObjectType type, X509Store store, string file, string password, bool verbose)
|
|
{
|
|
switch (type) {
|
|
case ObjectType.Certificate:
|
|
X509CertificateCollection coll = LoadCertificates (file, password, verbose);
|
|
foreach (X509Certificate x509 in coll) {
|
|
store.Import (x509);
|
|
}
|
|
Console.WriteLine ("{0} certificate(s) added to store {1}.",
|
|
coll.Count, store.Name);
|
|
break;
|
|
case ObjectType.CRL:
|
|
ArrayList list = LoadCRLs (file);
|
|
foreach (X509Crl crl in list) {
|
|
store.Import (crl);
|
|
}
|
|
Console.WriteLine ("{0} CRL(s) added to store {1}.",
|
|
list.Count, store.Name);
|
|
break;
|
|
default:
|
|
throw new NotSupportedException (type.ToString ());
|
|
}
|
|
}
|
|
|
|
static void Delete (ObjectType type, X509Store store, string hash, bool verbose)
|
|
{
|
|
switch (type) {
|
|
case ObjectType.Certificate:
|
|
foreach (X509Certificate x509 in store.Certificates) {
|
|
if (hash == CryptoConvert.ToHex (x509.Hash)) {
|
|
store.Remove (x509);
|
|
Console.WriteLine ("Certificate removed from store.");
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
case ObjectType.CRL:
|
|
foreach (X509Crl crl in store.Crls) {
|
|
if (hash == CryptoConvert.ToHex (crl.Hash)) {
|
|
store.Remove (crl);
|
|
Console.WriteLine ("CRL removed from store.");
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
throw new NotSupportedException (type.ToString ());
|
|
}
|
|
}
|
|
|
|
static void Put (ObjectType type, X509Store store, string file, bool machine, bool pem, bool verbose)
|
|
{
|
|
if (String.IsNullOrEmpty (file)) {
|
|
Console.Error.WriteLine("error: no filename provided to put the certificate.");
|
|
Help();
|
|
return;
|
|
}
|
|
|
|
switch (type) {
|
|
case ObjectType.Certificate:
|
|
for(int i = 0; i < store.Certificates.Count; i++) {
|
|
Console.WriteLine ("==============Certificate # {0} ==========", i + 1);
|
|
DisplayCertificate (store.Certificates[i], machine, verbose);
|
|
}
|
|
int selection;
|
|
Console.Write("Enter cert # from the above list to put-->");
|
|
if (!int.TryParse(Console.ReadLine(), out selection) || selection > store.Certificates.Count) {
|
|
Console.Error.WriteLine ("error: invalid selection.");
|
|
return;
|
|
}
|
|
|
|
SSCX.X509Certificate2 cert = new SSCX.X509Certificate2 (store.Certificates[selection-1].RawData);
|
|
byte[] data = null;
|
|
if(pem) {
|
|
data = ToPEM ("CERTIFICATE", cert.Export (SSCX.X509ContentType.Cert));
|
|
} else {
|
|
data = cert.Export (SSCX.X509ContentType.Cert);
|
|
}
|
|
|
|
using (FileStream fs = File.Create (file)) {
|
|
fs.Write(data, 0, data.Length);
|
|
}
|
|
|
|
Console.WriteLine ("Certificate put to {0}.", file);
|
|
break;
|
|
default:
|
|
throw new NotSupportedException ("Put " + type + " not supported yet");
|
|
}
|
|
}
|
|
|
|
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));
|
|
Console.WriteLine (" Issuer Name: {0}", x509.IssuerName);
|
|
Console.WriteLine (" Subject Name: {0}", x509.SubjectName);
|
|
Console.WriteLine (" Valid From: {0}", x509.ValidFrom);
|
|
Console.WriteLine (" Valid Until: {0}", x509.ValidUntil);
|
|
Console.WriteLine (" Unique Hash: {0}", CryptoConvert.ToHex (x509.Hash));
|
|
if (verbose) {
|
|
Console.WriteLine (" Key Algorithm: {0}", x509.KeyAlgorithm);
|
|
Console.WriteLine (" Algorithm Parameters: {0}", (x509.KeyAlgorithmParameters == null) ? "None" :
|
|
CryptoConvert.ToHex (x509.KeyAlgorithmParameters));
|
|
Console.WriteLine (" Public Key: {0}", CryptoConvert.ToHex (x509.PublicKey));
|
|
Console.WriteLine (" Signature Algorithm: {0}", x509.SignatureAlgorithm);
|
|
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 DisplayCrl (X509Crl crl, bool machine, bool verbose)
|
|
{
|
|
Console.WriteLine ("X.509 v{0} CRL", crl.Version);
|
|
Console.WriteLine (" Issuer Name: {0}", crl.IssuerName);
|
|
Console.WriteLine (" This Update: {0}", crl.ThisUpdate);
|
|
Console.WriteLine (" Next Update: {0} {1}", crl.NextUpdate, crl.IsCurrent ? String.Empty : "update overdue!");
|
|
Console.WriteLine (" Unique Hash: {0}", CryptoConvert.ToHex (crl.Hash));
|
|
if (verbose) {
|
|
Console.WriteLine (" Signature Algorithm: {0}", crl.SignatureAlgorithm);
|
|
Console.WriteLine (" Signature: {0}", CryptoConvert.ToHex (crl.Signature));
|
|
int n = 0;
|
|
foreach (X509Crl.X509CrlEntry entry in crl.Entries) {
|
|
Console.WriteLine (" #{0}: Serial: {1} revoked on {2}",
|
|
++n, CryptoConvert.ToHex (entry.SerialNumber), entry.RevocationDate);
|
|
}
|
|
}
|
|
}
|
|
|
|
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, machine, verbose);
|
|
}
|
|
break;
|
|
case ObjectType.CRL:
|
|
foreach (X509Crl crl in store.Crls) {
|
|
DisplayCrl (crl, machine, verbose);
|
|
}
|
|
break;
|
|
default:
|
|
throw new NotSupportedException (type.ToString ());
|
|
}
|
|
}
|
|
|
|
static X509CertificateCollection GetCertificatesFromSslSession (string url)
|
|
{
|
|
Uri uri = new Uri (url);
|
|
IPHostEntry host = Dns.Resolve (uri.Host);
|
|
IPAddress ip = host.AddressList [0];
|
|
Socket socket = new Socket (ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
|
socket.Connect (new IPEndPoint (ip, uri.Port));
|
|
NetworkStream ns = new NetworkStream (socket, false);
|
|
SslClientStream ssl = new SslClientStream (ns, uri.Host, false, Mono.Security.Protocol.Tls.SecurityProtocolType.Default, null);
|
|
ssl.ServerCertValidationDelegate += new CertificateValidationCallback (CertificateValidation);
|
|
|
|
try {
|
|
// we don't really want to write to the server (as we don't know
|
|
// the protocol it using) but we must send something to be sure the
|
|
// SSL handshake is done (so we receive the X.509 certificates).
|
|
StreamWriter sw = new StreamWriter (ssl);
|
|
sw.WriteLine (Environment.NewLine);
|
|
sw.Flush ();
|
|
socket.Poll (30000, SelectMode.SelectRead);
|
|
}
|
|
finally {
|
|
socket.Close ();
|
|
}
|
|
|
|
// we need a little reflection magic to get this information
|
|
PropertyInfo pi = typeof (SslStreamBase).GetProperty ("ServerCertificates", BindingFlags.Instance | BindingFlags.NonPublic);
|
|
if (pi == null) {
|
|
Console.WriteLine ("Sorry but you need a newer version of Mono.Security.dll to use this feature.");
|
|
return null;
|
|
}
|
|
return (X509CertificateCollection) pi.GetValue (ssl, null);
|
|
}
|
|
|
|
static bool CertificateValidation (SSCX.X509Certificate certificate, int[] certificateErrors)
|
|
{
|
|
// the main reason to download it is that it's not trusted
|
|
return true;
|
|
// OTOH we ask user confirmation before adding certificates into the stores
|
|
}
|
|
|
|
static void Ssl (string host, bool machine, bool verbose)
|
|
{
|
|
if (verbose) {
|
|
Console.WriteLine ("Importing certificates from '{0}' into the {1} stores.",
|
|
host, machine ? "machine" : "user");
|
|
}
|
|
int n=0;
|
|
|
|
X509CertificateCollection coll = GetCertificatesFromSslSession (host);
|
|
if (coll != null) {
|
|
X509Store store = null;
|
|
// start by the end (root) so we can stop adding them anytime afterward
|
|
for (int i = coll.Count - 1; i >= 0; i--) {
|
|
X509Certificate x509 = coll [i];
|
|
bool selfsign = false;
|
|
bool failed = false;
|
|
try {
|
|
selfsign = x509.IsSelfSigned;
|
|
}
|
|
catch {
|
|
// sadly it's hard to interpret old certificates with MD2
|
|
// without manually changing the machine.config file
|
|
failed = true;
|
|
}
|
|
|
|
if (selfsign) {
|
|
// this is a root
|
|
store = GetStoreFromName (X509Stores.Names.TrustedRoot, machine);
|
|
} else if (i == 0) {
|
|
// server certificate isn't (generally) an intermediate CA
|
|
store = GetStoreFromName (X509Stores.Names.OtherPeople, machine);
|
|
} else {
|
|
// all other certificates should be intermediate CA
|
|
store = GetStoreFromName (X509Stores.Names.IntermediateCA, machine);
|
|
}
|
|
|
|
Console.WriteLine ("{0}{1}X.509 Certificate v{2}",
|
|
Environment.NewLine,
|
|
selfsign ? "Self-signed " : String.Empty,
|
|
x509.Version);
|
|
Console.WriteLine (" Issued from: {0}", x509.IssuerName);
|
|
Console.WriteLine (" Issued to: {0}", x509.SubjectName);
|
|
Console.WriteLine (" Valid from: {0}", x509.ValidFrom);
|
|
Console.WriteLine (" Valid until: {0}", x509.ValidUntil);
|
|
|
|
if (!x509.IsCurrent)
|
|
Console.WriteLine (" *** WARNING: Certificate isn't current ***");
|
|
if ((i > 0) && !selfsign) {
|
|
X509Certificate signer = coll [i-1];
|
|
bool signed = false;
|
|
try {
|
|
if (signer.RSA != null) {
|
|
signed = x509.VerifySignature (signer.RSA);
|
|
} else if (signer.DSA != null) {
|
|
signed = x509.VerifySignature (signer.DSA);
|
|
} else {
|
|
Console.WriteLine (" *** WARNING: Couldn't not find who signed this certificate ***");
|
|
signed = true; // skip next warning
|
|
}
|
|
|
|
if (!signed)
|
|
Console.WriteLine (" *** WARNING: Certificate signature is INVALID ***");
|
|
}
|
|
catch {
|
|
failed = true;
|
|
}
|
|
}
|
|
if (failed) {
|
|
Console.WriteLine (" *** ERROR: Couldn't decode certificate properly ***");
|
|
Console.WriteLine (" *** try 'man certmgr' for additional help or report to bugzilla.novell.com ***");
|
|
break;
|
|
}
|
|
|
|
if (store.Certificates.Contains (x509)) {
|
|
Console.WriteLine ("This certificate is already in the {0} store.", store.Name);
|
|
} else {
|
|
Console.Write ("Import this certificate into the {0} store ?", store.Name);
|
|
string answer = Console.ReadLine ().ToUpper ();
|
|
if ((answer == "YES") || (answer == "Y")) {
|
|
store.Import (x509);
|
|
n++;
|
|
} else {
|
|
if (verbose) {
|
|
Console.WriteLine ("Certificate not imported into store {0}.",
|
|
store.Name);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Console.WriteLine ();
|
|
if (n == 0) {
|
|
Console.WriteLine ("No certificate were added to the stores.");
|
|
} else {
|
|
Console.WriteLine ("{0} certificate{1} added to the stores.",
|
|
n, (n == 1) ? String.Empty : "s");
|
|
}
|
|
}
|
|
|
|
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;
|
|
bool verbose = false;
|
|
bool pem = false;
|
|
bool machine = false;
|
|
|
|
Header ();
|
|
if (args.Length < 2) {
|
|
Help ();
|
|
return;
|
|
}
|
|
|
|
Action action = GetAction (args [0]);
|
|
ObjectType type = ObjectType.None;
|
|
|
|
int n = 1;
|
|
if (action != Action.Ssl) {
|
|
type = GetObjectType (args [n]);
|
|
if (type != ObjectType.None)
|
|
n++;
|
|
}
|
|
|
|
for (int i = n; i < args.Length; i++) {
|
|
switch (GetCommand (args[i])) {
|
|
case "V":
|
|
verbose = true;
|
|
n++;
|
|
break;
|
|
case "M":
|
|
machine = true;
|
|
n++;
|
|
break;
|
|
case "P":
|
|
password = args[++n];
|
|
n++;
|
|
break;
|
|
case "PEM":
|
|
pem = true;
|
|
n++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
X509Store store = null;
|
|
string storeName = null;
|
|
if (action != Action.Ssl) {
|
|
if ((action == Action.None) || (type == ObjectType.None)) {
|
|
Help ();
|
|
return;
|
|
}
|
|
if (type == ObjectType.CTL) {
|
|
Console.WriteLine ("CTL are not supported");
|
|
return;
|
|
}
|
|
|
|
storeName = args [n++];
|
|
store = GetStoreFromName (storeName, machine);
|
|
if (store == null) {
|
|
Console.WriteLine ("Invalid Store: {0}", storeName);
|
|
Console.WriteLine ("Valid stores are: {0}, {1}, {2}, {3} and {4}",
|
|
X509Stores.Names.Personal,
|
|
X509Stores.Names.OtherPeople,
|
|
X509Stores.Names.IntermediateCA,
|
|
X509Stores.Names.TrustedRoot,
|
|
X509Stores.Names.Untrusted);
|
|
return;
|
|
}
|
|
}
|
|
|
|
string file = (n < args.Length) ? args [n] : null;
|
|
|
|
// now action!
|
|
try {
|
|
switch (action) {
|
|
case Action.Add:
|
|
Add (type, store, file, password, verbose);
|
|
break;
|
|
case Action.Delete:
|
|
Delete (type, store, file, verbose);
|
|
break;
|
|
case Action.Put:
|
|
Put (type, store, file, machine, pem, verbose);
|
|
break;
|
|
case Action.List:
|
|
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 ());
|
|
}
|
|
}
|
|
catch (UnauthorizedAccessException uae) {
|
|
Console.WriteLine ("Access to the {0} '{1}' certificate store has been denied.",
|
|
(machine ? "machine" : "user"), storeName);
|
|
if (verbose) {
|
|
Console.WriteLine (uae);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|