using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Signers;
using Org.BouncyCastle.Security;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace WebXPaySample1
{
    class Program
    {
        static void Main(string[] args)
        {
            // Public key provided by WEBXPAY
            var publicKey =
                          @"-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9l2HykxDIDVZeyDPJU4pA0imf
3nWsvyJgb3zTsnN8B0mFX6u5squ5NQcnQ03L8uQ56b4/isHBgiyKwfMr4cpEpCTY
/t1WSdJ5EokCI/F7hCM7aSSSY85S7IYOiC6pKR4WbaOYMvAMKn5gCobEPtosmPLz
gh8Lo3b8UsjPq2W26QIDAQAB
-----END PUBLIC KEY-----";
            // Your unique order number
            var orderNumber = Guid.NewGuid().ToString();
            // Amount you want to charge
            var amount = 100;


            // "encPaymentString" variable hold your encrypted payment information that you can submit with additional details using an HTML form
            // please see the HTML sample on how to submit this string alongside with additional information to make a payment.
            var encPaymentString = EncryptPayments(publicKey, orderNumber, amount);

            Console.WriteLine($"Your encrypted string for the order number: {orderNumber} & amount: {amount}");
            Console.WriteLine("");
            Console.WriteLine($"{encPaymentString}");
            Console.ReadLine();
        }


        // Encrypt payment information
        // This method has a dependency to a Nuget package: "Portable.BouncyCastle"
        public static string EncryptPayments(string merchantPublicKey, string orderNumber, double amount)
        {
            try
            {
                Asn1Object obj = Asn1Object.FromByteArray(Convert.FromBase64String(merchantPublicKey
                    .Replace("-----BEGIN PUBLIC KEY-----", "")
                    .Replace("-----END PUBLIC KEY-----", "")
                ));

                DerSequence publicKeySequence = (DerSequence)obj;

                DerBitString encodedPublicKey = (DerBitString)publicKeySequence[1];
                DerSequence publicKey = (DerSequence)Asn1Object.FromByteArray(encodedPublicKey.GetBytes());

                DerInteger modulus = (DerInteger)publicKey[0];
                DerInteger exponent = (DerInteger)publicKey[1];

                RsaKeyParameters keyParameters =
                    new RsaKeyParameters(false, modulus.PositiveValue, exponent.PositiveValue);
                RSAParameters parameters = DotNetUtilities.ToRSAParameters(keyParameters);
                RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
                rsa.ImportParameters(parameters);

                var order = orderNumber + "|" + amount;

                byte[] dataToEncrypt = Encoding.UTF8.GetBytes(order);
                byte[] encryptedData = rsa.Encrypt(dataToEncrypt, true);

                var paymentEnc = Convert.ToBase64String(encryptedData);

                return paymentEnc;
            }
            catch (Exception)
            {
                // TODO: handle exception appropriately
                return null;
            }
        }

        // After payment finish. pass the returned payment and signature values along side with public key to verify payment
        private static bool Verify(string payment, string signature, string publicKey)
        {
            var pubKeyFixed = publicKey
                    .Replace("-----BEGIN PUBLIC KEY-----", "")
                    .Replace("-----END PUBLIC KEY-----", "");
            var payment64 = Convert.FromBase64String(payment);
            var signature64 = Convert.FromBase64String(signature);
            byte[] rawPublicKey = Convert.FromBase64String(pubKeyFixed);

            AsymmetricKeyParameter aKey = PublicKeyFactory.CreateKey(rawPublicKey);
            RsaKeyParameters rKey = (RsaKeyParameters)aKey;

            PssSigner pss = new PssSigner(new RsaEngine(), new Sha1Digest(), 20);
            pss.Init(false, rKey);
            pss.BlockUpdate(payment64, 0, payment.Length);

            var result = pss.VerifySignature(signature64);
            return result;
        }
    }



}
