/* $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://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. <https://di-mgt.com.au> <https://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);
}
}
}