.net 实现签名验签
阅读原文时间:2021年08月10日阅读:1

本人被要求实现.net的签名验签,还是个.net菜鸡,来分享下采坑过程

依然,签名验签使用的证书格式依然是pem,有关使用openssl将.p12和der转pem的命令请转到php实现签名验签

.net不像php只要将pem文件读出来就可以签名验签,需要解析pem文件的主体内容,获取参数装载特定类,具体看一下代码:

//有些函数我也不知道是干嘛的,反正.net的实现要这样,采百家之长,签的了名,验的了签就对了
//自己去单元测试下
//pem文件是我使用的是绝对路径
public class Signature
{
//签名,返回256位字节数组
public static byte[] Sign(string content,string path,string code)
{
RSACryptoServiceProvider rsaCsp = RSAProviderPrivate(path);
byte[] dataBytes = Encoding.UTF8.GetBytes(content);
byte[] signatureBytes = rsaCsp.SignData(dataBytes, code);
return signatureBytes;
}

    //验签  
    public static Boolean Verify(string content, byte\[\] signContent, string path, string code)  
    {  
        byte\[\] dataBytes = Encoding.UTF8.GetBytes(content);  
        byte\[\] publicPemBytes = LoadCertificateFile(path, "PUBLIC KEY");

        RSACryptoServiceProvider rsaPub = CreateRsaProviderFromPublicKey(publicPemBytes);  
        Boolean flag= rsaPub.VerifyData(dataBytes,code, signContent);  
        return flag;  
    }

    //加载公私钥证书文件  
    private static byte\[\] LoadCertificateFile(String filePath,string type)  
    {  
        using (System.IO.FileStream fs = System.IO.File.OpenRead(filePath))  
        {  
            byte\[\] data = new byte\[fs.Length\];  
            byte\[\] res = null;  
            fs.Read(data, 0, data.Length);  
            if (data\[0\] != 0x30)  
            {  
                res = GetPem(type, data);  
            }  
            return res;  
        }  
    }

    private static RSACryptoServiceProvider RSAProviderPrivate(String filePath)  
    {  
        byte\[\] res = LoadCertificateFile(filePath, "RSA PRIVATE KEY");

        try  
        {  
            RSACryptoServiceProvider rsa = DecodeRSAPrivateKey(res);  
            return rsa;  
        }  
        catch (Exception e)  
        {  
            Console.WriteLine(e);  
        }  
        return null;

    }

    //获取pem文件的主体内容,并转换成字节数组  
    private static byte\[\] GetPem(String type, byte\[\] data)  
    {  
        string pem = Encoding.UTF8.GetString(data);  
        string header = String.Format("-----BEGIN {0}-----\\\\n", type);  
        string footer = String.Format("-----END {0}-----", type);  
        int start = pem.IndexOf(header) + header.Length;  
        int end = pem.IndexOf(footer, start);  
        string base64 = pem.Substring(start, (end - start));  
        return Convert.FromBase64String(base64);  
    }

    private static bool CompareBytearrays(byte\[\] a, byte\[\] b)  
    {  
        if (a.Length != b.Length)  
            return false;  
        int i = 0;  
        foreach (byte c in a)  
        {  
            if (c != b\[i\])  
                return false;  
            i++;  
        }  
        return true;  
    }

    private static RSACryptoServiceProvider DecodeRSAPrivateKey(byte\[\] privkey)  
    {  
        byte\[\] MODULUS, E, D, P, Q, DP, DQ, IQ;

        // --------- Set up stream to decode the asn.1 encoded RSA private key ------  
        MemoryStream mem = new MemoryStream(privkey);  
        BinaryReader binr = new BinaryReader(mem);  //wrap Memory Stream with BinaryReader for easy reading  
        byte bt = 0;  
        ushort twobytes = 0;  
        int elems = 0;  
        try  
        {  
            twobytes = binr.ReadUInt16();  
            if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)  
                binr.ReadByte();    //advance 1 byte  
            else if (twobytes == 0x8230)  
                binr.ReadInt16();    //advance 2 bytes  
            else  
                return null;

            twobytes = binr.ReadUInt16();  
            if (twobytes != 0x0102) //version number  
                return null;  
            bt = binr.ReadByte();  
            if (bt != 0x00)  
                return null;

            //------ all private key components are Integer sequences ----  
            elems = GetIntegerSize(binr);  
            MODULUS = binr.ReadBytes(elems);

            elems = GetIntegerSize(binr);  
            E = binr.ReadBytes(elems);

            elems = GetIntegerSize(binr);  
            D = binr.ReadBytes(elems);

            elems = GetIntegerSize(binr);  
            P = binr.ReadBytes(elems);

            elems = GetIntegerSize(binr);  
            Q = binr.ReadBytes(elems);

            elems = GetIntegerSize(binr);  
            DP = binr.ReadBytes(elems);

            elems = GetIntegerSize(binr);  
            DQ = binr.ReadBytes(elems);

            elems = GetIntegerSize(binr);  
            IQ = binr.ReadBytes(elems);

            // ------- create RSACryptoServiceProvider instance and initialize with public key -----  
            CspParameters CspParameters = new CspParameters();  
            CspParameters.Flags = CspProviderFlags.UseMachineKeyStore;  
            RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(1024, CspParameters);  
            RSAParameters RSAparams = new RSAParameters();  
            RSAparams.Modulus = MODULUS;  
            RSAparams.Exponent = E;  
            RSAparams.D = D;  
            RSAparams.P = P;  
            RSAparams.Q = Q;  
            RSAparams.DP = DP;  
            RSAparams.DQ = DQ;  
            RSAparams.InverseQ = IQ;  
            RSA.ImportParameters(RSAparams);  
            return RSA;  
        }  
        catch (Exception e)  
        {  
            Console.WriteLine(e);  
            return null;  
        }  
        finally  
        {  
            binr.Close();  
        }  
    }

    private static int GetIntegerSize(BinaryReader binr)  
    {  
        byte bt = 0;  
        byte lowbyte = 0x00;  
        byte highbyte = 0x00;  
        int count = 0;  
        bt = binr.ReadByte();  
        if (bt != 0x02)        //expect integer  
            return 0;  
        bt = binr.ReadByte();

        if (bt == 0x81)  
            count = binr.ReadByte();    // data size in next byte  
        else  
            if (bt == 0x82)  
        {  
            highbyte = binr.ReadByte();    // data size in next 2 bytes  
            lowbyte = binr.ReadByte();  
            byte\[\] modint = { lowbyte, highbyte, 0x00, 0x00 };  
            count = BitConverter.ToInt32(modint, 0);  
        }  
        else  
        {  
            count = bt;        // we already have the data size  
        }

        while (binr.ReadByte() == 0x00)  
        {    //remove high order zeros in data  
            count -= 1;  
        }  
        binr.BaseStream.Seek(-1, SeekOrigin.Current);        //last ReadByte wasn't a removed zero, so back up a byte  
        return count;  
    }

    private static RSAParameters ConvertFromPublicKey(byte\[\] publicPemBytes)  
    {

        byte\[\] keyData = publicPemBytes;  
        if (keyData.Length < 162)  
        {  
            throw new ArgumentException("pem file content is incorrect.");  
        }  
        byte\[\] pemModulus = new byte\[128\];  
        byte\[\] pemPublicExponent = new byte\[3\];  
        Array.Copy(keyData, 29, pemModulus, 0, 128);  
        Array.Copy(keyData, 159, pemPublicExponent, 0, 3);  
        RSAParameters para = new RSAParameters();  
        para.Modulus = pemModulus;  
        para.Exponent = pemPublicExponent;  
        return para;  
    }

    public static RSACryptoServiceProvider CreateRsaProviderFromPublicKey(byte\[\] publicKeyBytes)  
    {  
        byte\[\] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };  
        byte\[\] x509key;  
        byte\[\] seq = new byte\[15\];  
        int x509size;

        x509key = publicKeyBytes;  
        x509size = x509key.Length;

        using (MemoryStream mem = new MemoryStream(x509key))  
        {  
            using (BinaryReader binr = new BinaryReader(mem))  
            {  
                byte bt = 0;  
                ushort twobytes = 0;

                twobytes = binr.ReadUInt16();  
                if (twobytes == 0x8130)  
                    binr.ReadByte();  
                else if (twobytes == 0x8230)  
                    binr.ReadInt16();  
                else  
                    return null;

                seq = binr.ReadBytes(15);  
                if (!CompareBytearrays(seq, SeqOID))  
                    return null;

                twobytes = binr.ReadUInt16();  
                if (twobytes == 0x8103)  
                    binr.ReadByte();  
                else if (twobytes == 0x8203)  
                    binr.ReadInt16();  
                else  
                    return null;

                bt = binr.ReadByte();  
                if (bt != 0x00)  
                    return null;

                twobytes = binr.ReadUInt16();  
                if (twobytes == 0x8130)  
                    binr.ReadByte();  
                else if (twobytes == 0x8230)  
                    binr.ReadInt16();  
                else  
                    return null;

                twobytes = binr.ReadUInt16();  
                byte lowbyte = 0x00;  
                byte highbyte = 0x00;

                if (twobytes == 0x8102)  
                    lowbyte = binr.ReadByte();  
                else if (twobytes == 0x8202)  
                {  
                    highbyte = binr.ReadByte();  
                    lowbyte = binr.ReadByte();  
                }  
                else  
                    return null;  
                byte\[\] modint = { lowbyte, highbyte, 0x00, 0x00 };  
                int modsize = BitConverter.ToInt32(modint, 0);

                int firstbyte = binr.PeekChar();  
                if (firstbyte == 0x00)  
                {  
                    binr.ReadByte();  
                    modsize -= 1;  
                }

                byte\[\] modulus = binr.ReadBytes(modsize);

                if (binr.ReadByte() != 0x02)  
                    return null;  
                int expbytes = (int)binr.ReadByte();  
                byte\[\] exponent = binr.ReadBytes(expbytes);

                RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();  
                RSAParameters RSAKeyInfo = new RSAParameters();  
                RSAKeyInfo.Modulus = modulus;  
                RSAKeyInfo.Exponent = exponent;  
                RSA.ImportParameters(RSAKeyInfo);

                return RSA;  
            }

        }  
    }

}