/* $Id:  XmlEnc_DecryptXml.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_DecryptXml
    {
        static void Main(string[] args)
        {
            string cipherValue;
            string s;

            Console.WriteLine("PKI Version={0}", General.Version());

            /////////////////////////////////
            // Decrypt block cipher CBC mode
            /////////////////////////////////

            // FILE: encrypt-data-aes128-cbc.xml
            cipherValue = "QMpxhXq1DtBeyC9KfSaMQWrEtefe+e935gF/x62spvmL6IW0XeS0W4Kk31OgWzN0";
            s = xmlenc_decrypt_cbc_data(cipherValue, "6162636465666768696A6B6C6D6E6F70", CipherAlgorithm.Aes128);
            Console.WriteLine("PT='{0}'", s);
            Debug.Assert(s.Length > 0);

            // Same again as a one-liner
            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);

            // FILE: encrypt-data-aes192-cbc-kw-aes256.xml
            cipherValue = "50lv94d/DFJirJXYOUXaBlrO+7gIXpx8cqH+G2xvE4mueoIxmGs8RH7FBXwjuMgf";
            s = xmlenc_decrypt_cbc_data(cipherValue, "F65CD2C48C1DA5565ED0AAB2D042713373C74C8E69332F2F", CipherAlgorithm.Aes192);
            Console.WriteLine("PT='{0}'", s);
            Debug.Assert(s.Length > 0);

            // FILE: encrypt-element-aes128-cbc-rsa-1_5.xml
            cipherValue = @"0wkECpTy60/FDwbVM4zgd9qJVjR4h0q4PLm5pyyIxAuhbEh0art03yEikmbWBt2H
                7qOk2G9iufUdwwqNPuZV5Qw5Rg2FMvTx234lDERGn5p+hhjOTcss5JF9QDzgdiec
                KABX3vbCESi/f3uwQ8BYDT+6SnxTR+xtcNv5xhbUCIFk/TaenSWx6p6fntTwTl1e
                lpwnI0EtM1yf4a9tBiH9PNd36BUv2rvSi4cZvJqSB3ZKvGtuwwyRzOzlzl259d1u
                QuoYysTBEAHw/WIop8eAexU9PUv7UbTkQAQag1yStda+GepVdpXEpu4hcxXQcvfs
                9AQgkAgh4JKrnY4Bhz2B/e4CHHfbEedDOi+FVYlZuLn0CzrKMnM+1nUmqxJVWHz7
                hytidpuqNRw3gcMkYvgH6g==";
            s = xmlenc_decrypt_cbc_data(cipherValue, "493185CE8177AC09C4DE6C7C75524B16", CipherAlgorithm.Aes128);
            Console.WriteLine("PT='{0}'", s);
            Debug.Assert(s.Length > 0);

            // FILE: encrypt-element-aes256-cbc-carried-kw-aes256.xml
            cipherValue = @"pdDtiyd7XQ/BFEEN0PMJuHnLUfCY+bJlsW+q04OiKSPnRd4/dS1tjaTfj5dPpGXe
                cY3fJvRsq9QP1CJiwyEC/EQ1zSLbzwOtZ+NtxtsFgYvPBJ9t86ZcXIjlErQ85z3L
                wnb8rSHpE9tu4tJ1rjgf2i6NCbdFnSMXLSDgLEs48+gkX0cJCmKxzRaSE4cV0OSl
                hBWND4EYzX1M679VlSYrI0de+lSPO3Vx+y/TuZ5Vo+uu9+YP+ce0LRkx2BicjjsP
                QO9sp+yjHPNDIV1Z7VHsDIWqqmBaNQo3GuzF5WzWgaXTKnPv/IgUQn+1t3EtgHyb
                JhnfR/1em16z/Zaf9Uy1Lfd//yfEJ9BCjqwe1UjwN6ytu1v2BHd+8bVjD2o+Dg8V
                7ayOLlkWOTOLvtJMPOXPqw==";
            s = xmlenc_decrypt_cbc_data(cipherValue, "41559545888C22BF367F7936E6A9FFB6D810B2AB42BF70220F2168DB9B503DC6", CipherAlgorithm.Aes256);
            Console.WriteLine("PT='{0}'", s);
            Debug.Assert(s.Length > 0);


            /////////////////////////////////////
            // Decrypt block cipher AES-GCM mode
            /////////////////////////////////////

            // FILE: encrypt-data-aes256-gcm.xml
            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);

            // Same again as a one-liner
            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));
            Console.WriteLine("PT='{0}'", s);
            Debug.Assert(s.Length > 0);

            // FILE: root-bob-oaep-gcm.xml
            cipherValue = "f4K4iAry8y7hz2mKl3Xpqnx5lVPTKkeKhM7PmGJITVE5pk4NZjFak1ZaKpgvoi3ooUP+sxUArXnNG/GrLYQ=";
            s = xmlenc_decrypt_gcm(cipherValue, "E2493529B46E23907FC77AB0C4DD74BE", AeadAlgorithm.Aes_128_Gcm);
            Console.WriteLine("PT='{0}'", s);
            Debug.Assert(s.Length > 0);

            // FILE: root-oaep-256-gcm-x2.xml
            cipherValue = "e2kl8SXHPvyy/qbzY2Rk46+6vWtTLgt4rSL+1IMkdJqBQvpqaMPFeKa4k4yxsa4XauwZZYdOIfwj13sFvMA=";
            s = xmlenc_decrypt_gcm(cipherValue, "F45F70F9645F73B3CB88DBD17EBA881B", AeadAlgorithm.Aes_128_Gcm);
            Console.WriteLine("PT='{0}'", s);
            Debug.Assert(s.Length > 0);


            /////////////////////////////////////
            // Decrypt key wrap 
            /////////////////////////////////////

            // FILE: encrypt-data-aes192-cbc-kw-aes256.xml
            cipherValue = "4AAgyi3M7xNdBimbQZKdGJLn3/cS4Yv8QKuA01+gUnY=";
            s = xmlenc_decrypt_kw_keyhex(cipherValue, "6162636465666768696A6B6C6D6E6F707172737475767778797A303132333435", CipherAlgorithm.Aes256);
            Console.WriteLine("CEK=0x{0}", s);
            Debug.Assert(s.Length > 0);

            // Same again as a one-liner
            s = Cnv.ToHex(Cipher.KeyUnwrap(
                Cnv.FromBase64("4AAgyi3M7xNdBimbQZKdGJLn3/cS4Yv8QKuA01+gUnY="),
                Cnv.FromHex("6162636465666768696A6B6C6D6E6F707172737475767778797A303132333435"), CipherAlgorithm.Aes256));
            Console.WriteLine("CEK=0x{0}", s);
            Debug.Assert(s.Length > 0);

            // FILE: encrypt-element-aes256-cbc-carried-kw-aes256.xml
            cipherValue = "bsL63D0hPN6EOyzdgfEmKsAAvoJiGM+Wp9a9KZM92IKdl7s3YSntRg==";
            s = xmlenc_decrypt_kw_keyhex(cipherValue, "6162636465666768696A6B6C6D6E6F707172737475767778797A303132333435", CipherAlgorithm.Aes256);
            Console.WriteLine("CEK=0x{0}", s);
            Debug.Assert(s.Length > 0);


            /////////////////////////////////////
            // Decrypt key rsa-1_5 
            /////////////////////////////////////
            // FILE: encrypt-element-aes128-cbc-rsa-1_5.xml
            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);

            // Same again as a one-liner
            s = Cnv.ToHex(Rsa.Decrypt(Cnv.FromBase64(@"heZshNX5m7arS3OmR72+8WNCMMpznxE41dLWkgd6XJpzl+IN2xuijAf4YPEjjJmZ
                nt9PlO3/hiHl0Cvpg5vMR6AhvL49BvCz9JCeMG6x3MHBiKbRNhyEq2rX7o1GdJhC
                5cm35Q/ZDKV9DHG8jWmPcOb8yKU9NYo2LJKDb3YHOJY="), "rsa-w3c.p8", ""));
            Console.WriteLine("CEK=0x{0}", s);
            Debug.Assert(s.Length > 0);

            /////////////////////////////////////
            // Decrypt key rsa-oaep 
            /////////////////////////////////////
            // FILE: root-bob-oaep-gcm.xml
            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);

            // FILE: root-oaep-256-gcm-x2.xml
            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);
        }

        /// <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);
        }
        /// <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);
        }

        /// <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);
        }

        /// <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);
        }
        
        /// <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);
        }

    }
}