This is a companion page to Encryption in XML documents using XMLENC. It gives code samples showing how to use CryptoSys PKI Pro to carry out the encryption and decryption operations required for the example XML documents using the W3C recommendation XML Encryption Syntax and Processing [XMLENC].
diCrSysPKINet.dll
(you should find this in C:\Program Files (x86)\CryptoSysPKI\DotNet
).
Add a using directive to the CryptoSysPKI
namespace in your code.
using CryptoSysPKI;
CipherValue = BASE64(IV|CT) where CT = ENCRYPT(KEY, IV, PADW3C(PT))
/// <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); }
Note you may not get exactly the same output as the original, even if you supply the IV. That's because the "arbitrary" bytes used in the W3C padding will make the last block different.
cipherValue = xmlenc_encrypt_cbc_data("top secret message\n", "6162636465666768696A6B6C6D6E6F70", CipherAlgorithm.Aes128, "40CA71857AB50ED05EC82F4A7D268C41");
QMpxhXq1DtBeyC9KfSaMQWrEtefe+e935gF/x62spvmlfisW3QMpqTyhfZvw2GgY
CipherValue = BASE64(IV|CT|TAG) where (CT, TAG) = ENCRYPT_GCM(KEY, IV, PT)
/// <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); }
cipherValue = xmlenc_encrypt_gcm("top secret message\n", "6162636465666768696A6B6C6D6E6F707172737475767778797A303132333435", AeadAlgorithm.Aes_256_Gcm, "0102030405060708090A0B0C");
AQIDBAUGBwgJCgsMqYHeLKkzx8/iBLZa8f+ZCMGHulqDdiELj+ghwhU1jg62WW0=
CipherValue = BASE64(WrappedKey) where WrappedKey = ENCRYPT_KW(KEK, CEK) CEK = content-encryption key KEK = Key-encryption key used to encrypt CEK
/// <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); }
cipherValue = xmlenc_encrypt_kw("F65CD2C48C1DA5565ED0AAB2D042713373C74C8E69332F2F", "6162636465666768696A6B6C6D6E6F707172737475767778797A303132333435", CipherAlgorithm.Aes256);
4AAgyi3M7xNdBimbQZKdGJLn3/cS4Yv8QKuA01+gUnY=
/// <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); }
cipherValue = xmlenc_encrypt_rsa_15("493185CE8177AC09C4DE6C7C75524B16", "rsa-w3c.crt");
3Twqw4pUB5Bz4ArmPbDBlJijSDOT5Nl7caqe0WgyMEtg1kZNjwNBj7PW8rsC8iHhz4PggeuTaPQCTSXu4EZU/4 06qJCjFrX+uyl5sCJSEB89o75HpPuvLGleVatfwFZE6b3PhhwxZHgWGbvY1U/Ft2cCt1sepy9kt0ibY4lC9eA=
/// <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); }
Note that the output will always be different each time due to random seeding.
Case 1: rsa-oaep-mgf1p with digest method algorithm sha256. MGF is always mgf1sha1
.
<EncryptionMethod Algorithm="http://www.w3.org/2009/xmlenc11#rsa-oaep-mgf1p"> <DigestMethod xmlns="http://www.w3.org/2000/09/xmldsig#" Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> </EncryptionMethod>
cipherValue = xmlenc_encrypt_rsa_oaep("E2493529B46E23907FC77AB0C4DD74BE", "bob-smime.crt", Rsa.HashAlg.Sha256);
Gk1xynxKQiwi9+y/uhwzES1ifOPTjGQxt/DYFIKLp/GPBWRvUhc84X4l5H1P7F1g/C0SlKajZiwGoC+1eLYk+g tWRGh1a5yNyBy72CUHgMnTo8c5gg6LynB7F32TZWlQLHidNPCv6Gt2Vol2OZ0YcKHLU/HuFmXa2CsnWGqbz3yW DT7A2o5+oirYte3vxEJjY8tBwpS9pjamSlQfDrfmJwrkqde/OaedIN+f2QlhRnJma1PTL1BNnaot0vjnri3SEr vkzuHGPkXoM1Ae2gEuy/AC5HDYPDdFNkI9Dh4WWzCtYP0yhrJoM2MHghzpj5LAdab1xHcLclXGGBzdwsNJCg==
Case 2: rsa-oaep with digest method algorithm sha256 and mgf1sha256. By default, the Toolkit will use the same MGF1 hash function as the digest method.
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep"> <xenc11:MGF Algorithm="http://www.w3.org/2009/xmlenc11#mgf1sha256" /> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" /> </xenc:EncryptionMethod>
cipherValue = xmlenc_encrypt_rsa_oaep("F45F70F9645F73B3CB88DBD17EBA881B", "bob-smime.crt", Rsa.HashAlg.Sha256);
TVHwXmwyJ0/cToVhqni8XV5iazF1lCglDZWWx6E/PL1/mQgM0Kx6XS3YbaW3ISWEbz1VY+G+MnuxgwQG8HFVxJ GzcnZ0XFpsXTEwBIHbe01UPUP1dyMd3waW+80YUcT+o7iW3yTe7UO6B1nCPB9Qnx2lDoRsRvjsML074OkfSyzz B9iAQBuLtae738KrCbBNzdhUpbBr1ELLDcLH3LpSWQABBJ7Huhau5YFcXi27M+TORheS6d4L0npSDy/8ngZ/9v wSrcgQn3H28qS7Xo1unqGpcXx7Tvp1J7qcWBpKF8NWZ+Qoe6DOSi6B+gHMRLdIcqpQNEwBRX5erq5+ltRyTQ==
/// <summary> /// Decrypt base64-encoded CipherValue using block cipher in CBC mode as per W3C XMLENC. /// </summary> /// <param name="cipherValue">Base64-encoded CipherValue</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> /// <returns>Decrypted plaintext as a Unicode string or the empty string on error.</returns> static string xmlenc_decrypt_cbc_data(string cipherValue, string keyHex, CipherAlgorithm cipherAlg) { byte[] pt = Cipher.Decrypt(Cnv.FromBase64(cipherValue), Cnv.FromHex(keyHex), null, cipherAlg, Mode.CBC, Padding.W3CPadding, Cipher.Opts.PrefixIV); return System.Text.Encoding.UTF8.GetString(pt); }
encrypt-data-aes128-cbc.xml
//EncryptedData/CipherData/CipherValue
http://www.w3.org/2001/04/xmlenc#aes128-cbc
A one-liner in C#:
s = System.Text.Encoding.UTF8.GetString(Cipher.Decrypt( Cnv.FromBase64("QMpxhXq1DtBeyC9KfSaMQWrEtefe+e935gF/x62spvmL6IW0XeS0W4Kk31OgWzN0"), Cnv.FromHex("6162636465666768696A6B6C6D6E6F70"), null, CipherAlgorithm.Aes128, Mode.CBC, Padding.W3CPadding, Cipher.Opts.PrefixIV)); Console.WriteLine("PT='{0}'", s); Debug.Assert(s.Length > 0);
PT='top secret message '
The same using xmlenc_decrypt_cbc_data() function above.
cipherValue = "QMpxhXq1DtBeyC9KfSaMQWrEtefe+e935gF/x62spvmL6IW0XeS0W4Kk31OgWzN0"; s = xmlenc_decrypt_cbc_data(cipherValue, "6162636465666768696A6B6C6D6E6F70", CipherAlgorithm.Aes128); Console.WriteLine("PT='{0}'", s); Debug.Assert(s.Length > 0);
/// <summary> /// Decrypt base64-encoded CipherValue using block cipher in AES-GCM mode as per W3C XMLENC. /// </summary> /// <param name="cipherValue">Base64-encoded CipherValue</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> /// <returns>Decrypted plaintext as a Unicode string or the empty string on error.</returns> static string xmlenc_decrypt_gcm(string cipherValue, string keyHex, AeadAlgorithm aeadAlg) { byte[] pt = Cipher.DecryptAEAD(Cnv.FromBase64(cipherValue), Cnv.FromHex(keyHex), null, null, aeadAlg, Cipher.Opts.PrefixIV); return System.Text.Encoding.UTF8.GetString(pt); }
encrypt-data-aes256-gcm.xml
//EncryptedData/CipherData/CipherValue
http://www.w3.org/2009/xmlenc11#aes256-gcm
A one-liner in C#:
s = System.Text.Encoding.UTF8.GetString(Cipher.DecryptAEAD( Cnv.FromBase64("AQIDBAUGBwgJCgsMqYHeLKkzx8/iBLZa8f+ZCMGHulqDdiELj+ghwhU1jg62WW0="), Cnv.FromHex("6162636465666768696A6B6C6D6E6F707172737475767778797A303132333435"), null, null, AeadAlgorithm.Aes_256_Gcm, Cipher.Opts.PrefixIV));
Same using function above:
cipherValue = "AQIDBAUGBwgJCgsMqYHeLKkzx8/iBLZa8f+ZCMGHulqDdiELj+ghwhU1jg62WW0="; s = xmlenc_decrypt_gcm(cipherValue, "6162636465666768696A6B6C6D6E6F707172737475767778797A303132333435", AeadAlgorithm.Aes_256_Gcm); Console.WriteLine("PT='{0}'", s); Debug.Assert(s.Length > 0);
/// <summary> /// Decrypt key wrap material. /// </summary> /// <param name="cipherValue">Base64-encoded CipherValue</param> /// <param name="kekHex">Key-encryption key (KEK) encoded in hex of exact required length for algorithm (16|24|32 bytes)</param> /// <param name="cipherAlg">Cipher algorithm</param> /// <returns>Key material encoded in hex.</returns> static string xmlenc_decrypt_kw_keyhex(string cipherValue, string kekHex, CipherAlgorithm cipherAlg) { byte[] key = Cipher.KeyUnwrap(Cnv.FromBase64(cipherValue), Cnv.FromHex(kekHex), cipherAlg); return Cnv.ToHex(key); }
encrypt-data-aes192-cbc-kw-aes256.xml
//EncryptedData/KeyInfo/EncryptedKey/CipherData/CipherValue
http://www.w3.org/2001/04/xmlenc#kw-aes256
A one-liner in C#:
s = Cnv.ToHex(Cipher.KeyUnwrap( Cnv.FromBase64("4AAgyi3M7xNdBimbQZKdGJLn3/cS4Yv8QKuA01+gUnY="), Cnv.FromHex("6162636465666768696A6B6C6D6E6F707172737475767778797A303132333435"), CipherAlgorithm.Aes256));
Same using function above:
cipherValue = "4AAgyi3M7xNdBimbQZKdGJLn3/cS4Yv8QKuA01+gUnY="; s = xmlenc_decrypt_kw_keyhex(cipherValue, "6162636465666768696A6B6C6D6E6F707172737475767778797A303132333435", CipherAlgorithm.Aes256); Console.WriteLine("CEK=0x{0}", s); Debug.Assert(s.Length > 0);
/// <summary> /// Decrypt key encrypted using RSA PKCS#1 v1.5 key transport algorithm. /// </summary> /// <param name="cipherValue">Base64-encoded CipherValue</param> /// <param name="rsaKeyFile">File containing RSA private key or a string containing the key in PEM format.</param> /// <param name="password">Password for key file or <c>""</c> if not required.</param> /// <returns>Key material encoded in hex.</returns> static string xmlenc_decrypt_rsa_15_keyhex(string cipherValue, string rsaKeyFile, string password = "") { byte[] key = Rsa.Decrypt(Cnv.FromBase64(cipherValue), rsaKeyFile, password); return Cnv.ToHex(key); }
encrypt-element-aes128-cbc-rsa-1_5.xml
//EncryptedData/EncryptedKey/KeyInfo/CipherData/CipherValue
http://www.w3.org/2001/04/xmlenc#rsa-1_5
A one-liner in C#:
s = Cnv.ToHex(Rsa.Decrypt(Cnv.FromBase64(@"heZshNX5m7arS3OmR72+8WNCMMpznxE41dLWkgd6XJpzl+IN2xuijAf4YPEjjJmZ nt9PlO3/hiHl0Cvpg5vMR6AhvL49BvCz9JCeMG6x3MHBiKbRNhyEq2rX7o1GdJhC 5cm35Q/ZDKV9DHG8jWmPcOb8yKU9NYo2LJKDb3YHOJY="), "rsa-w3c.p8", ""));
Same using function above:
cipherValue = @"heZshNX5m7arS3OmR72+8WNCMMpznxE41dLWkgd6XJpzl+IN2xuijAf4YPEjjJmZ nt9PlO3/hiHl0Cvpg5vMR6AhvL49BvCz9JCeMG6x3MHBiKbRNhyEq2rX7o1GdJhC 5cm35Q/ZDKV9DHG8jWmPcOb8yKU9NYo2LJKDb3YHOJY="; s = xmlenc_decrypt_rsa_15_keyhex(cipherValue, "rsa-w3c.p8", ""); Console.WriteLine("CEK=0x{0}", s); Debug.Assert(s.Length > 0);
/// <summary> /// Decrypt key encrypted using RSA-OAEP key transport algorithm. /// </summary> /// <param name="cipherValue">Base64-encoded CipherValue</param> /// <param name="rsaKeyFile">File containing RSA private key or a string containing the key in PEM format.</param> /// <param name="password">Password for key file or <c>""</c> if not required.</param> /// <param name="hashAlg">Hash function for EME-OAEP encoding, otherwise ignored. [default = SHA-1]</param> /// <param name="advOpts"><c>Mgf1_Sha1</c> to force the MGF hash function to be SHA-1 [default = same as hash function set by <c>hashAlg</c>]</param> /// <returns>Key material encoded in hex.</returns> static string xmlenc_decrypt_rsa_oaep_keyhex(string cipherValue, string rsaKeyFile, string password = "", Rsa.HashAlg hashAlg = Rsa.HashAlg.Sha1, Rsa.AdvOptions advOpts = Rsa.AdvOptions.Default) { byte[] key = Rsa.Decrypt(Cnv.FromBase64(cipherValue), rsaKeyFile, password, Rsa.EME.OAEP, hashAlg, advOpts); return Cnv.ToHex(key); }
root-bob-oaep-gcm.xml
//EncryptedData/KeyInfo/EncryptedKey/CipherData/CipherValue
http://www.w3.org/2009/xmlenc11#rsa-oaep-mgf1p
http://www.w3.org/2001/04/xmlenc#sha256
cipherValue = @"ne3BEmELlWOKCvevCnKmgZ9//JYNgSrtdighAztmUJAtuGCXWMqtfsSLccTUpEdm k4Vl+BampcxIiEvHduNkq1un9oV7ikyrTiT0A9jChlC8Tf8HU9XeU3I/I/5FqP8V IxWfnTLuvu/jp4lVyqoNmDtsooqlQtcaC0C7JRoc+bKeqMhzZxOgdUK3UM34aDDk HZHZSaMbWp8P59kJLUECMXTP+dW3q8MMucr7PEGhN5VGBKNH3/IlCvDvg3ROW/cY csCTh8Vve5h+eyiZUl2DcBITgj31qFkZ/QJtzoIG7PbkS5WU0NNtAguO6z7MbZlv PAOyQgvJpmSQqKUOrHro8g=="; s = xmlenc_decrypt_rsa_oaep_keyhex(cipherValue, "bob-smime.p8", "", Rsa.HashAlg.Sha256, Rsa.AdvOptions.Mgf1_Sha1); Console.WriteLine("CEK=0x{0}", s); Debug.Assert(s.Length > 0);
root-oaep-256-gcm-x2.xml
//EncryptedData/KeyInfo/EncryptedKey[2]/CipherData/CipherValue
http://www.w3.org/2009/xmlenc11#rsa-oaep
http://www.w3.org/2009/xmlenc11#mgf1sha256
http://www.w3.org/2001/04/xmlenc#sha256
cipherValue = @"qsIPCYkqgCSDv+xxNf0swqJiDZiVTsPrMdPcPFeaZW+yK3KqeHYHZubrTMV0Cj5b gezvKe/fq+OdIZVvxIDR/nJy0M/Jm6GiVnOmm2FzzP9YnsARubF2piwGGT5bo4a0 wkoH+bBBcZEMPG1LHWBUThBn+pz6ApV/ogLB4q5f5bkiqKSi4rZktT8fxxJJvqdg lrYmLNSM+QS51YP35xtBwMups8BTHNQ/n+YOoz0/X3xBMIgCc0K0W14LG9qgXsgR AVtxezJkiOq8q+jj12vNyBLqQoR3MnTaOM9kmHcwHFkCoG4HBgDTQJWOd2oIrHGi 2TfIvBsPHXk6STZKEj7u4A=="; s = xmlenc_decrypt_rsa_oaep_keyhex(cipherValue, "bob-smime.p8", "", Rsa.HashAlg.Sha256); Console.WriteLine("CEK=0x{0}", s); Debug.Assert(s.Length > 0);
Download example files used above: xmlenc-examples.zip (21 kB).
Code snippets from above in source code files.
encrypt-data-aes128-cbc.xml
)encrypt-data-aes192-cbc-kw-aes256.xml
)encrypt-element-aes128-cbc-rsa-1_5.xml
)encrypt-element-aes256-cbc-carried-kw-aes256.xml
)encrypt-data-aes256-gcm.xml
)root-bob-oaep-gcm.xml
)root-oaep-256-gcm-x2.xml
)See Using xmlsq and CryptoSys PKI to decrypt XMLENC documents. This page gives C# code that uses our simple lightweight XML query utility xmlsq to query the XMLENC documents for the relevant information, then uses the CryptoSys PKI cryptographic functions to decrypt the cipher value.
To contact us or comment on this page, please send us a message.
This page first published 7 December 2020. Last updated 7 December 2020