using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using Microsoft.SqlServer.Server;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;

public partial class RS256Signer
{
    [SqlProcedure]
    public static void RS256Sign(SqlString InData, SqlString secret, out SqlString signature)
    {
        string Data = GetValue(InData);
        string Key = GetValue(secret);
        signature = SqlString.Null;

        
        ASCIIEncoding ByteConverter = new ASCIIEncoding();

        RSA rsa = RSA.Create();

        rsa.ImportParameters(ImportPrivateKey(Key));

        byte[] buffer = ByteConverter.GetBytes(Data);

        byte[] sign = rsa.SignData(buffer, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);  // Pss Pkcs1

        signature = Convert.ToBase64String(sign);

    }

    private static string GetValue(SqlString sqlValue)
    {
        return sqlValue.IsNull ? null : sqlValue.Value;
    }

    private static int? GetValue(SqlInt32 sqlValue)
    {
        return sqlValue.IsNull ? null : (int?)sqlValue.Value;
    }
    private static byte[] GetValue(SqlBinary sqlValue)
    {
        return sqlValue.IsNull ? null : sqlValue.Value;
    }

    public static RSAParameters ImportPrivateKey(string pem)
    {

        PemReader pr = new PemReader(new StringReader(pem));
        RsaPrivateCrtKeyParameters privKey = (RsaPrivateCrtKeyParameters)pr.ReadObject();
        RSAParameters rp = new RSAParameters();
        rp.Modulus = privKey.Modulus.ToByteArrayUnsigned();
        rp.Exponent = privKey.PublicExponent.ToByteArrayUnsigned();
        rp.P = privKey.P.ToByteArrayUnsigned();
        rp.Q = privKey.Q.ToByteArrayUnsigned();
        rp.D = ConvertRSAParametersField(privKey.Exponent, rp.Modulus.Length);
        rp.DP = ConvertRSAParametersField(privKey.DP, rp.P.Length);
        rp.DQ = ConvertRSAParametersField(privKey.DQ, rp.Q.Length);
        rp.InverseQ = ConvertRSAParametersField(privKey.QInv, rp.Q.Length);

        return rp;
    }

    private static byte[] ConvertRSAParametersField(BigInteger n, int size)
    {
        byte[] bs = n.ToByteArrayUnsigned();
        if (bs.Length == size)
            return bs;
        if (bs.Length > size)
            throw new ArgumentException("Specified size too small", "size");
        byte[] padded = new byte[size];
        Array.Copy(bs, 0, padded, size - bs.Length, bs.Length);
        return padded;
    }

}
