/* $Id: XmlEnc_EncryptXml.cs $ * Last updated: * $Date: 2020-12-07 03:17:00 $ * $Version: 1.0.0 $ */ /* Ref: * [Using CryptoSys PKI to encrypt and decrypt using XMLENC](https://www.cryptosys.net/pki/xmlenc-encrypt-decrypt.html) */ using System; using System.Text; using System.Diagnostics; using CryptoSysPKI; /******************************* LICENSE *********************************** * Copyright (C) 2020 David Ireland, DI Management Services Pty Limited. * All rights reserved. <www.di-mgt.com.au> <www.cryptosys.net> * The code in this module is licensed under the terms of the MIT license. * For a copy, see <http://opensource.org/licenses/MIT> **************************************************************************** */ namespace XmlEnc { class XmlEnc_EncryptXml { static void Main(string[] args) { string cipherValue; Console.WriteLine("Ver={0}", General.Version()); ///////////////////////////////// // Encrypt block cipher CBC mode ///////////////////////////////// // NOTE: these won't reproduce the exact same CipherValues as the examples, because of the "arbitrary" bytes in the W3C padding. // The output will be the same at the start but not at the end. Hey, it shows you are on the right track! cipherValue = xmlenc_encrypt_cbc_data("top secret message\n", "6162636465666768696A6B6C6D6E6F70", CipherAlgorithm.Aes128, "40CA71857AB50ED05EC82F4A7D268C41"); Console.WriteLine(cipherValue); cipherValue = xmlenc_encrypt_cbc_data("top secret message\n", "6162636465666768696A6B6C6D6E6F70", CipherAlgorithm.Aes128); Console.WriteLine(cipherValue); cipherValue = xmlenc_encrypt_cbc_data("top secret message\n", "6162636465666768696A6B6C6D6E6F707172737475767778797A303132333435", CipherAlgorithm.Aes256, "E7496FF7877F0C5262AC95D83945DA06"); Console.WriteLine(cipherValue); string cv = @"0wkECpTy60/FDwbVM4zgd9qJVjR4h0q4PLm5pyyIxAuhbEh0art03yEikmbWBt2H 7qOk2G9iufUdwwqNPuZV5Qw5Rg2FMvTx234lDERGn5p+hhjOTcss5JF9QDzgdiec KABX3vbCESi/f3uwQ8BYDT+6SnxTR+xtcNv5xhbUCIFk/TaenSWx6p6fntTwTl1e lpwnI0EtM1yf4a9tBiH9PNd36BUv2rvSi4cZvJqSB3ZKvGtuwwyRzOzlzl259d1u QuoYysTBEAHw/WIop8eAexU9PUv7UbTkQAQag1yStda+GepVdpXEpu4hcxXQcvfs 9AQgkAgh4JKrnY4Bhz2B/e4CHHfbEedDOi+FVYlZuLn0CzrKMnM+1nUmqxJVWHz7 hytidpuqNRw3gcMkYvgH6g=="; Console.WriteLine("CV=" + Cnv.HexFromBase64(cv)); string elemPaymentInfo = @"<PaymentInfo> <BillingAddress> Dig PLC, 1 First Ave, Dublin 1, Ireland </BillingAddress> <CreditCard Type=""Amex""> <Name>Foo B Baz</Name> <Number>1234 567890 12345</Number> <Expires Month=""1"" Year=""2005"" /> </CreditCard> </PaymentInfo>"; cipherValue = xmlenc_encrypt_cbc_data(elemPaymentInfo, "493185CE8177AC09C4DE6C7C75524B16", CipherAlgorithm.Aes128, "D309040A94F2EB4FC50F06D5338CE077"); Console.WriteLine(cipherValue); ///////////////////////////////////// // Encrypt block cipher AES-GCM mode ///////////////////////////////////// cipherValue = xmlenc_encrypt_gcm("top secret message\n", "6162636465666768696A6B6C6D6E6F707172737475767778797A303132333435", AeadAlgorithm.Aes_256_Gcm, "0102030405060708090A0B0C"); Console.WriteLine("CipherValue={0}", cipherValue); // Encrypt CEK with key wrap KEK cipherValue = xmlenc_encrypt_kw("F65CD2C48C1DA5565ED0AAB2D042713373C74C8E69332F2F", "6162636465666768696A6B6C6D6E6F707172737475767778797A303132333435", CipherAlgorithm.Aes256); Console.WriteLine("CipherValue(CEK)={0}", cipherValue); Debug.Assert(cipherValue.Length > 0); cipherValue = xmlenc_encrypt_rsa_15("493185CE8177AC09C4DE6C7C75524B16", "rsa-w3c.crt"); Console.WriteLine("CipherValue(CEK)={0}", cipherValue); Debug.Assert(cipherValue.Length > 0); // RSA-OAEP cipherValue = xmlenc_encrypt_rsa_oaep("E2493529B46E23907FC77AB0C4DD74BE", "bob-smime.crt", Rsa.HashAlg.Sha256, useMgf1SHA1 : true); Console.WriteLine("CipherValue(RSA-OAEP)={0}", cipherValue); Debug.Assert(cipherValue.Length > 0); cipherValue = xmlenc_encrypt_rsa_oaep("F45F70F9645F73B3CB88DBD17EBA881B", "bob-smime.crt", Rsa.HashAlg.Sha256); Console.WriteLine("CipherValue(RSA-OAEP)={0}", cipherValue); Debug.Assert(cipherValue.Length > 0); } ///////////////////////////////// // GENERIC ENCRYPTION FUNCTIONS ///////////////////////////////// /// <summary> /// Encrypt data using block cipher in CBC mode as per W3C XMLENC. /// </summary> /// <param name="strData">Data to be encrypted as Unicode string</param> /// <param name="keyHex">Key in hex encoding of exact required length for algorithm (16|24|32 bytes)</param> /// <param name="cipherAlg">Block cipher algorithm</param> /// <param name="ivHex">Optional IV in hex encoding for testing [default = generate random IV]</param> /// <returns>Base64-encoded CipherValue = BASE64(IV|CT) or empty string on error.</returns> static string xmlenc_encrypt_cbc_data(string strData, string keyHex, CipherAlgorithm cipherAlg, string ivHex = "") { byte[] iv; if (ivHex.Length > 0) { // IV provided for testing purposes... iv = Cnv.FromHex(ivHex); } else { // Generate a random IV... iv = Rng.Bytes(Cipher.BlockBytes(cipherAlg)); } byte[] data = System.Text.Encoding.UTF8.GetBytes(strData); // Default character encoding for XML documents byte[] ct = Cipher.Encrypt(data, Cnv.FromHex(keyHex), iv, cipherAlg, Mode.CBC, Padding.W3CPadding, Cipher.Opts.PrefixIV); // Why isn't this the same as the example? return Cnv.ToBase64(ct); } /// <summary> /// Encrypt data using block cipher in AES-GCM mode as per W3C XMLENC. /// </summary> /// <param name="strData">Data to be encrypted as Unicode string.</param> /// <param name="keyHex">Key in hex encoding of exact required length for algorithm (16|24|32 bytes)</param> /// <param name="aeadAlg">Authenticated encryption algorithm</param> /// <param name="ivHex">Optional IV in hex encoding for testing [default = generate random IV]</param> /// <returns>Base64-encoded CipherValue = BASE64(IV|CT|TAG) or empty string on error.</returns> static string xmlenc_encrypt_gcm(string strData, string keyHex, AeadAlgorithm aeadAlg, string ivHex = "") { byte[] iv; if (ivHex.Length > 0) { // IV provided for testing purposes... iv = Cnv.FromHex(ivHex); } else { // Generate a random IV, always 12 bytes... iv = Rng.Bytes(12); } byte[] data = System.Text.Encoding.UTF8.GetBytes(strData); // Default character encoding for XML documents byte[] ct = Cipher.EncryptAEAD(data, Cnv.FromHex(keyHex), iv, null, aeadAlg, Cipher.Opts.PrefixIV); return Cnv.ToBase64(ct); } /// <summary> /// Encrypt key material using key wrap. /// </summary> /// <param name="cekHex">Hex-encoded key material to be wrapped.</param> /// <param name="kekHex">Key-encryption key in hex encoding of exact required length for algorithm (16|24|32 bytes).</param> /// <param name="cipherAlg">Cipher algorithm for key wrap.</param> /// <returns>Base64-encoded wrapped key or empty string on error.</returns> static string xmlenc_encrypt_kw(string cekHex, string kekHex, CipherAlgorithm cipherAlg) { byte[] encKey = Cipher.KeyWrap(Cnv.FromHex(cekHex), Cnv.FromHex(kekHex), cipherAlg); return Cnv.ToBase64(encKey); } /// <summary> /// Encrypt key material using RSA-v1.5 /// </summary> /// <param name="cekHex">Hex-encoded key material to be wrapped.</param> /// <param name="pubkeyFile">File containing public key or X.509 certificate, or key or certificate as a PEM string.</param> /// <returns>Base64-encoded enveloped key or empty string on error.</returns> static string xmlenc_encrypt_rsa_15(string cekHex, string pubkeyFile) { byte[] encKey = Rsa.Encrypt(Cnv.FromHex(cekHex), pubkeyFile); return Cnv.ToBase64(encKey); } /// <summary> /// Encrypt key material using RSA-OAEP /// </summary> /// <param name="cekHex">Hex-encoded key material to be wrapped.</param> /// <param name="pubkeyFile">File containing public key or X.509 certificate, or key or certificate as a PEM string.</param> /// <param name="hashAlg">Message digest function to use for the DigestMethod</param> /// <param name="useMgf1SHA1">Use mask generation function <c>MGF1 with SHA1</c> [default = use same digest function as <c>hashAlg</c>]</param> /// <returns>Base64-encoded enveloped key or empty string on error.</returns> static string xmlenc_encrypt_rsa_oaep(string cekHex, string pubkeyFile, Rsa.HashAlg hashAlg, bool useMgf1SHA1 = false) { byte[] encKey = Rsa.Encrypt(Cnv.FromHex(cekHex), pubkeyFile, Rsa.EME.OAEP, hashAlg, (useMgf1SHA1 ? Rsa.AdvOptions.Mgf1_Sha1 : Rsa.AdvOptions.Default)); return Cnv.ToBase64(encKey); } } }