/// <summary> /// Lists the thumbprint value for each certificate in the specified store location which include "Key Encipherment" in its Key Usage extension /// </summary> /// <param name="Context">Store location from which to list certificate details (Either <see cref="X509Context.UserReadOnly"/> or <see cref="X509Context.SystemReadOnly"/>)</param> /// <param name="allowExpired">If set to True, expired certificates will be included in the output (Note that .NET will not perform cryptographic operations using a certificate which is not within its validity period)</param> /// <returns>A string expression listing all available certificate thumbprints and their expiration dates</returns> /// <example> /// <code> /// string availableCerts = <see cref="X509Utils"/>.<see cref="ListCerts"/>(<see cref="X509Context.UserReadOnly"/>); /// </code> /// </example> public static string ListCerts(X509Context Context = null, bool allowExpired = false) { if (Context == null) { Context = X509Context.UserReadOnly; } string output = "Key Encipherment Certificates found:\r\n\r\n"; bool firstAdded = false; X509Store store = new X509Store(Context.Location); store.Open(OpenFlags.ReadOnly); foreach (X509Certificate2 cert in store.Certificates) { if (X509CryptoAgent.IsUsable(cert, allowExpired)) { firstAdded = true; output += cert.Subject + "\t" + string.Format("Expires {0}", cert.NotAfter.ToShortDateString()) + "\t" + cert.Thumbprint + "\r\n"; } } if (!firstAdded) { output += "None.\r\n"; } return(output); }
/// <summary> /// Encrypts the specified plaintext expression /// </summary> /// <param name="thumbprint">The thumbprint of the certificate to use for encryption</param> /// <param name="plaintext">The plaintext expression to encrypt</param> /// <param name="Context">The certificate store where the encryption certificate resides</param> /// <param name="verbose">True enables verbose logging</param> /// <returns></returns> /// <example> /// <code> /// string thumbprint = @"ccdc673c40ebb2a433300c0c8a2ba6f443da5688"; /// <see cref="X509Context"/> certStore = <see cref="X509Context"/>.<see cref="X509Context.UserReadOnly"/>; /// string plaintext = @"Please encrypt this"; /// string ciphertext = <see cref="X509Utils"/>.EncryptText(thumbprint, plaintext, certStore); /// </code> /// </example> public static string EncryptText(string thumbprint, string plaintext, X509Context Context, bool verbose = false) { using (X509CryptoAgent cryptoAgent = new X509CryptoAgent(FormatThumbprint(thumbprint), Context)) { return(cryptoAgent.EncryptText(plaintext)); } }
/// <summary> /// Decrypts the specified Base64-encoded ciphertext expression /// </summary> /// <param name="ciphertext">The Base64-encoded ciphertext expression to be decrypted</param> /// <returns>A recovered plaintext string</returns> public string DecryptText(string ciphertext) { string plaintext = string.Empty; using (X509CryptoAgent Agent = new X509CryptoAgent(Thumbprint, Context)) { plaintext = Agent.DecryptText(ciphertext); } return(plaintext); }
/// <summary> /// Decrypts the specified encrypted file /// </summary> /// <param name="thumbprint">The thumbprint of the certificate corresponding to the public key used to encrypt the file</param> /// <param name="ciphertextFilePath">The fully-qualified path of the encrypted file</param> /// <param name="plaintextFilePath">The fully-qualified path in which to write the decrypted file</param> /// <param name="Context">The certificate store where the encryption certificate resides</param> /// <param name="verbose">True enables verbose logging</param> /// <returns>True or false depending upon whether the file decryption succeeded</returns> /// <example> /// <code> /// string thumbprint = @"ccdc673c40ebb2a433300c0c8a2ba6f443da5688"; /// <see cref="X509Context"/> certStore = <see cref="X509Context"/>.<see cref="X509Context.UserReadOnly"/>; /// string encryptedFilePath = @"C:\Data\accounts.csv.ctx"; /// bool success = <see cref="X509Utils"/>.DecryptFile(thumbprint, encryptedFilePath, certStore); /// </code> /// </example> public static bool DecryptFile(string thumbprint, string ciphertextFilePath, string plaintextFilePath, X509Context Context, bool verbose = false) { CheckForFile(ciphertextFilePath); File.Delete(plaintextFilePath); using (X509CryptoAgent cryptoAgent = new X509CryptoAgent(FormatThumbprint(thumbprint), Context)) { cryptoAgent.DecryptFile(ciphertextFilePath, plaintextFilePath); } return(File.Exists(plaintextFilePath)); }
/// <summary> /// This constructor is intended to create a new X509Alias pointing to the specified encryption certificate /// </summary> /// <param name="Name">The desired identifier for the alias</param> /// <param name="Thumbprint">The SHA1 thumbprint of the certificate to be used for cryptographic operations. Must exist in the specified Context</param> /// <param name="Context">The context in which to create the alias</param> /// <param name="complainIfExists">If set to true, an exception is thrown if an existing alias identifier is specified for "Name"</param> public X509Alias(string Name, string Thumbprint, X509Context Context, bool complainIfExists) : this(Context) { this.Name = Name; this.Thumbprint = Thumbprint; LoadIfExists(complainIfExists); if (!X509CryptoAgent.CertificateExists(Thumbprint, Context)) { throw new X509CryptoCertificateNotFoundException(Thumbprint, Context); } }
internal string Reveal(X509Alias Alias) { try { using (X509CryptoAgent Agent = new X509CryptoAgent(Alias)) { return(Agent.DecryptText(Value)); } } catch (Exception ex) { throw new X509CryptoException($"Could not decrypt secret named \"{Key}\" in Alias \"{Alias.Name}\"", ex); } }
/// <summary> /// Recovers the specified encrypted file /// </summary> /// <param name="inFile">The path to the encrypted file to be recovered. Path must exist</param> /// <param name="outFile">The path in which to write the recovered plaintext file</param> /// <param name="wipeTimesToWrite">Performs n-pass forensic wipe of the disk sectors where the input file was stored.</param> public void DecryptFile(string inFile, string outFile, int wipeTimesToWrite = 0) { using (X509CryptoAgent Agent = new X509CryptoAgent(this)) { Agent.DecryptFile(inFile, outFile); } if (!File.Exists(outFile)) { throw new X509CryptoException($"Unable to decrypt the file \"{inFile}\". The plaintext file \"{outFile}\" could not be created."); } if (wipeTimesToWrite > 0) { X509Utils.WipeFile(inFile, wipeTimesToWrite); } }
internal X509Secret(X509Alias Alias, string key, string value) { string cipherText; try { Key = key; using (X509CryptoAgent Agent = new X509CryptoAgent(Alias)) { cipherText = Agent.EncryptText(value); } Value = cipherText; } catch (Exception ex) { throw new X509CryptoException($"Could not encrypt new secret named \"{key}\" in alias \"{Alias.Name}\"", ex); } }
/// <summary> /// Re-encrypts a ciphertext expression using a different certificate /// </summary> /// <param name="oldThumbprint">The thumbprint of the old certificate used for prior encryption</param> /// <param name="newThumbprint">The thumbprint of the new certificate to be used for re-encryption</param> /// <param name="ciphertext">The ciphertext expression to be re-encrypted</param> /// <param name="OldContext">(Optional) The X509Context where the old encryption certificate resides (Default: <see cref="X509Context"/>.<see cref="X509Context.UserReadOnly"/>)</param> /// <param name="NewContext">(Optional) The X509Context where the new encryption certificate resides (Default: <see cref="X509Context"/>.<see cref="X509Context.UserReadOnly"/>)</param> /// <param name="verbose">(Optional) True enables verbose logging (Default: false)</param> /// <returns>The text expression re-encrypted using the new certificate</returns> /// <example> /// <code> /// string oldThumbprint = @"ccdc673c40ebb2a433300c0c8a2ba6f443da5688"; /// string newThumbprint = @"0e7e327aab74e47a702c02d90c659da1115b29f7"; /// string ciphertext = File.ReadAllText(@"C:\data\connectionString.txt"); /// string updatedCiphertext = <see cref="X509Utils"/>.ReEncryptText(oldThumbprint, newThumbprint, ciphertext); /// File.WriteAllText(@"C:\data\connectionString.txt", updatedCiphertext); /// </code> /// </example> public static string ReEncryptText(string oldThumbprint, string newThumbprint, string ciphertext, X509Context OldContext = null, X509Context NewContext = null, bool verbose = false) { if (OldContext == null) { OldContext = X509Context.UserReadOnly; } if (NewContext == null) { NewContext = X509Context.UserReadOnly; } using (X509CryptoAgent oldAgent = new X509CryptoAgent(FormatThumbprint(oldThumbprint), OldContext)) { using (X509CryptoAgent newAgent = new X509CryptoAgent(FormatThumbprint(newThumbprint), NewContext)) { return(newAgent.EncryptText(oldAgent.DecryptText(ciphertext))); } } }
/// <summary> /// Installs an encryption certificate and associated key pair in the specified X509Context /// </summary> /// <param name="infile">The PKCS#12 (usually with a .pfx or .p12 extension) containing the bundled certificate and key pair</param> /// <param name="PfxPassword">The password to unlock the PKCS#12 file</param> /// <param name="Context">The X509Context in which to place the certificate and key pair</param> /// <returns></returns> public static string InstallCert(string infile, SecureString PfxPassword, X509Context Context) { bool certInstalled = false; X509Certificate2Collection certCol = new X509Certificate2Collection(); X509Store keyChain; string thumbprint = string.Empty; try { certCol.Import(infile, PfxPassword.Plaintext(), X509KeyStorageFlags.PersistKeySet); keyChain = new X509Store(StoreName.My, Context.Location); keyChain.Open(OpenFlags.ReadWrite); foreach (X509Certificate2 cert in certCol) { if (X509CryptoAgent.IsUsable(cert, Constants.ProbeMode)) { keyChain.Add(cert); if (Context.Index == X509Context.Indexer.SystemFull || Context.Index == X509Context.Indexer.SystemReadOnly) { AddIISKeyAccess(cert.Thumbprint); } certInstalled = true; thumbprint = cert.Thumbprint; break; } } if (!certInstalled) { throw new X509CryptoException($"The PKCS#12 file {Path.GetFileName(infile).InQuotes()} did not contain a valid encryption certificate"); } else { return(thumbprint); } } finally { certCol = null; keyChain = null; } }
/// <summary> /// Encrypts the specified file /// </summary> /// <param name="thumbprint">The thumbprint of the certificate to use for encryption</param> /// <param name="plaintextFilePath">The fully-qualified path of the plaintext file (can be text or binary)</param> /// <param name="Context">(Optional) The certificate store where the encryption certificate resides (Default: <see cref="X509Context"/>.<see cref="X509Context.UserReadOnly"/>)</param> /// <param name="ciphertextFilePath">(Optional) The fully-qualified path in which to write the encrypted file (If not specified, the plaintext file path is appended with a ".ctx" extension)</param> /// <param name="verbose">(Optional) True enables verbose logging</param> /// <returns></returns> /// <example> /// <code> /// string thumbprint = @"ccdc673c40ebb2a433300c0c8a2ba6f443da5688"; /// <see cref="X509Context"/> certStore = <see cref="X509Context"/>.<see cref="X509Context.UserReadOnly"/> /// string plaintextFilePath = @"C:\Data\accounts.csv"; /// string ciphertextFilePath = /// bool success = <see cref="X509Utils"/>.EncryptFile(thumbprint, plaintextFilePath, certStore); /// </code> /// </example> public static bool EncryptFile(string thumbprint, string plaintextFilePath, X509Context Context = null, string ciphertextFilePath = "", bool verbose = false) { CheckForFile(plaintextFilePath); if (Context == null) { Context = X509Context.UserReadOnly; } if (string.IsNullOrEmpty(ciphertextFilePath)) { ciphertextFilePath = plaintextFilePath + CRYPTO_ENCRYPTED_FILE_EXT; } File.Delete(ciphertextFilePath); using (X509CryptoAgent cryptoAgent = new X509CryptoAgent(FormatThumbprint(thumbprint), Context)) { cryptoAgent.EncryptFile(plaintextFilePath, ciphertextFilePath); } return(File.Exists(ciphertextFilePath)); }
/// <summary> /// Updates this X509Alias to use a new encryption certificate and key pair. The old certificate and key pair must still be available to perform this operation. /// </summary> /// <param name="newThumbprint">The SHA1 thumbprint of the new encryption certificate. The certificate and associated key pair must exist and be available in the specified X509Context</param> /// <param name="newContext">The X509Context where the new encryption certificate and key pair is located</param> public void ReEncrypt(string newThumbprint, X509Context newContext = null) { if (newContext == null) { newContext = Context; } newThumbprint = newThumbprint.RemoveNonHexChars(); if (!X509CryptoAgent.CertificateExists(newThumbprint, newContext)) { throw new X509CryptoException($"A valid encryption certificate with thumbprint {newThumbprint} was not found in the {Context.Name} context"); } foreach (X509Secret secret in Secrets) { secret.ReEncrypt(this, newThumbprint, newContext); } Thumbprint = newThumbprint; Context = newContext; Commit(); }
internal static Dictionary <string, X509Certificate2> GetAll(X509Context Context) { Dictionary <string, X509Certificate2> Aliases = new Dictionary <string, X509Certificate2>(); X509Certificate2Collection CertStore = GetCertificates(Context); X509Alias CurrentAlias; foreach (string aliasName in Context.GetAliasNames()) { CurrentAlias = new X509Alias(aliasName, Context); if (X509CryptoAgent.CertificateExists(CurrentAlias.Thumbprint, Context)) { foreach (X509Certificate2 Cert in CertStore) { if (Cert.Thumbprint.Matches(CurrentAlias.Thumbprint)) { Aliases.Add(aliasName, Cert); break; } } } } return(Aliases); }
/// <summary> /// Re-encrypts an encrypted file using a different encryption certificate /// </summary> /// <param name="oldThumbprint">The thumbprint of the old certificate used for prior encryption</param> /// <param name="newThumbprint">The thumbprint of the new certificate to be used for re-encryption</param> /// <param name="ciphertextFilePath">The fully-qualified path to the ciphertext file to be re-encrypted</param> /// <param name="OldContext">(Optional) The certificate store where the old encryption certificate resides (Default: <see cref="X509Context"/>.<see cref="X509Context.UserReadOnly"/>)</param> /// <param name="NewContext">(Optional) The certificate store where the new encryption certificate resides (Default: <see cref="X509Context"/>.<see cref="X509Context.UserReadOnly"/>)</param> /// <param name="verbose">(Optional) True enables verbose logging (Default: false)</param> /// <example> /// <code> /// string oldThumbprint = @"ccdc673c40ebb2a433300c0c8a2ba6f443da5688"; /// string newThumbprint = @"0e7e327aab74e47a702c02d90c659da1115b29f7"; /// string encryptedFilePath = @"C:\data\accounts.csv.ctx"; /// <see cref="X509Utils"/>.ReEncryptFile"(oldThumbprint, newThumbprint, encryptedFilePath); /// </code> /// </example> public static void ReEncryptFile(string oldThumbprint, string newThumbprint, string ciphertextFilePath, X509Context OldContext = null, X509Context NewContext = null, bool verbose = false) { CheckForFile(ciphertextFilePath); if (OldContext == null) { OldContext = X509Context.UserReadOnly; } if (NewContext == null) { NewContext = X509Context.UserReadOnly; } byte[] hashOrig, hashCopy; string tmpCopy; hashOrig = Hash(ciphertextFilePath); tmpCopy = string.Format(@"{0}\cryptotmp_{1}", Path.GetDirectoryName(ciphertextFilePath), Rnd(6)); File.Copy(ciphertextFilePath, tmpCopy); hashCopy = Hash(tmpCopy); if (hashOrig.SequenceEqual(hashCopy)) { File.Delete(ciphertextFilePath); } else { try { File.Delete(tmpCopy); } catch { } throw new Exception(string.Format("Could not back up original file \"{0}\"", ciphertextFilePath)); } try { using (X509CryptoAgent oldAgent = new X509CryptoAgent(oldThumbprint.RemoveNonHexChars(), OldContext)) { byte[] data = oldAgent.DecryptFileToByteArray(tmpCopy); using (X509CryptoAgent newAgent = new X509CryptoAgent(newThumbprint.RemoveNonHexChars(), NewContext)) { newAgent.EncryptFileFromByteArray(data, ciphertextFilePath); } } if (!File.Exists(ciphertextFilePath)) { throw new FileNotFoundException($"\"{ciphertextFilePath}\": File not found after cryptographic operation. Restoring original"); } } catch (Exception ex) { if (File.Exists(ciphertextFilePath)) { if (!Hash(ciphertextFilePath).SequenceEqual(hashCopy)) { File.Delete(ciphertextFilePath); File.Copy(tmpCopy, ciphertextFilePath); } } else { File.Copy(tmpCopy, ciphertextFilePath); } throw ex; } }
/// <summary> /// Re-encrypts the specified ciphertext expression using a different X509CryptoAgent /// </summary> /// <param name="ciphertext">the ciphertext expression to be re-encrypted</param> /// <param name="newAgent">the X509CryptoAgent to be used to perform re-encryption</param> /// <returns></returns> public string ReEncryptText(string ciphertext, X509CryptoAgent newAgent) { return(newAgent.EncryptText(DecryptText(ciphertext))); }
/// <summary> /// Exports the encryption certificate contained in this alias to a Base64-encoded text file. The private key is not exported. /// </summary> /// <param name="path">The fully-qualified path where the export file should be written</param> public void ExportCert(string path) { X509CryptoAgent.ExportCert(Thumbprint, Context, path); }