rsa加签验签C#和js以及java互通

news2024/11/24 10:49:16

js实现rsa加签验签

https://github.com/kjur/jsrsasign
11.1.0版本
解压选择需要的版本,这里选择all版本了
在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JS RSA加签验签</title>
</head>
<body>

</body>
<script src="jsrsasign-all-min.js"></script>
<script type="text/javascript">
    // 公钥
    let pk = "-----BEGIN PUBLIC KEY-----\n" +
        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD3XSdz1MnzazBEN5KOfTx0IyVJ\n" +
        "Z5wb57isrCuHDhnYXwtmdhQalgII0fozeeFpMpAvlnmHC1kpW7XVGvZnLx3bWbCE\n" +
        "bf+pMSW4kmQuI+5cxRUJbCl7sdaODBrINgERHPICVC18AJLThEVMHyjuR6Jn4zQm\n" +
        "yYNbReSktY/BrFTvMQIDAQAB\n" +
        "-----END PUBLIC KEY-----";
    // 私钥
    let priK = "-----BEGIN PRIVATE KEY-----\n" +
        "MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAPddJ3PUyfNrMEQ3\n" +
        "ko59PHQjJUlnnBvnuKysK4cOGdhfC2Z2FBqWAgjR+jN54WkykC+WeYcLWSlbtdUa\n" +
        "9mcvHdtZsIRt/6kxJbiSZC4j7lzFFQlsKXux1o4MGsg2AREc8gJULXwAktOERUwf\n" +
        "KO5HomfjNCbJg1tF5KS1j8GsVO8xAgMBAAECgYEA6eG1JMrj63jEmStmMb1txG1a\n" +
        "mu4Q5z2QGgtr2HVXsIIlGEq6tWxyHf7TL4qkuz9onuYKn8n2Eqm44fZtVaBx+5ES\n" +
        "zRpIvlTvaxmVu0HZ1hYAzUw1XyRnXNMKpL5tT4GCjm8+QGPzlGxgXI1sNg8r9Jaw\n" +
        "9zRUYeA6LQR9RIMkHWUCQQD8QojjVoGjtiunoh/N8iplhUszZIavAEvmDIE+kVy+\n" +
        "pA7hvlukLw6JMc7cfTcnHyxDo9iHVIzrWlTuKRq9KWVLAkEA+wgJS2sgtldnCVn6\n" +
        "tJKFVwsHrWhMIU29msPPbNuWUD23BcKE/vehIyFu1ahNA/TiM40PEnzprQ5JfPxU\n" +
        "16S78wJANTfMLTnYy7Lo7sqTLx2BuD0wqjzw9QZ4/KVytsJv8IAn65P/PVn4FRV+\n" +
        "8KEx+3zmF7b/PT2nJRe/hycAzxtmlQJBAMrFwQxEqpXfoAEzx4lY2ZBn/nmaR/SW\n" +
        "4VNEXCbocVC7qT1j1R5HVMgV13uKiTtq8dUGWmhqsi7x3XayNK5ECPUCQQDZaAN6\n" +
        "tvIHApz9OLsXSw0jZirQ6KEYdharXbIVDy1W1sVE3lzLbqLdFp1bxAHQIvsYS5PM\n" +
        "A9veSJh372RLJKkj\n" +
        "-----END PRIVATE KEY-----";

    // 原文
    var src = "好厉害";
    src="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiMTU2MjEzNTc1MDEiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjQ4MDY5NjUyNDQ4NDY3NyIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0";
    // 创建 Signature 对象
    let signature = new KJUR.crypto.Signature({alg: "SHA1withRSA", prvkeypem: priK});    //!这里指定 私钥 pem!
    signature.updateString(src);
    let a = signature.sign();
    let sign = hextob64(a);

    console.log("jsrsasign: \n"+sign);

    // 验签
    // !要重新new 一个Signature, 否则, 取摘要和签名时取得摘要不一样, 导致验签误报失败(原因不明)!
    let signatureVf = new KJUR.crypto.Signature({alg: "SHA1withRSA", prvkeypem: pk});
    signatureVf.updateString(src);
    // !接受的参数是16进制字符串!
    let b = signatureVf.verify(b64tohex(sign));
    console.log("jsrsasign verify: " + b);
</script>
</html>

在这里插入图片描述
也可以换成SHA256withRSA

C# 实现rsa加签验签

新建.net core3.1项目
安装BouncyCastle.Cryptography 2.4.0

RSAHelper

using System;
using System.Security.Cryptography;
using System.Text;
using System.Xml;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;

namespace RSACSharpJsStu01
{
    public class RSAHelper
    {
        /// <summary>
        /// 重写FromXmlString方法
        /// </summary>
        /// <param name="xmlString"></param>
        /// <returns></returns>
        public static RSACryptoServiceProvider FromXmlString(string xmlString)
        {
            var rsa = new RSACryptoServiceProvider();
            RSAParameters parameters = new RSAParameters();
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(xmlString);
            if (xmlDoc.DocumentElement.Name.Equals("RSAKeyValue"))
            {
                foreach (XmlNode node in xmlDoc.DocumentElement.ChildNodes)
                {
                    switch (node.Name)
                    {
                        case "Modulus":
                            parameters.Modulus = Convert.FromBase64String(node.InnerText);
                            break;
                        case "Exponent":
                            parameters.Exponent = Convert.FromBase64String(node.InnerText);
                            break;
                        case "P":
                            parameters.P = Convert.FromBase64String(node.InnerText);
                            break;
                        case "Q":
                            parameters.Q = Convert.FromBase64String(node.InnerText);
                            break;
                        case "DP":
                            parameters.DP = Convert.FromBase64String(node.InnerText);
                            break;
                        case "DQ":
                            parameters.DQ = Convert.FromBase64String(node.InnerText);
                            break;
                        case "InverseQ":
                            parameters.InverseQ = Convert.FromBase64String(node.InnerText);
                            break;
                        case "D":
                            parameters.D = Convert.FromBase64String(node.InnerText);
                            break;
                    }
                }
            }
            else
            {
                throw new Exception("Invalid XML RSA key.");
            }

            rsa.ImportParameters(parameters);
            return rsa;
        }

        /// <summary>
        /// RSA公钥,从Java格式转.net格式(不依赖第三方包)
        /// </summary>
        /// <param name="publikKey"></param>
        /// <returns></returns>
        public static string RSAPublicKeyJava2DotNet(string publicKey)
        {
            RsaKeyParameters publicKeyParam =
                (RsaKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(publicKey));
            return string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent></RSAKeyValue>",
                Convert.ToBase64String(publicKeyParam.Modulus.ToByteArrayUnsigned()),
                Convert.ToBase64String(publicKeyParam.Exponent.ToByteArrayUnsigned()));
        }

        /// <summary>        
        /// RSA签名验证
        /// </summary> 
        /// <param name="encryptSource">签名</param>
        /// <param name="c">验证的字符串</param>
        /// <param name="publicKey">公钥</param>
        /// <returns>是否相同,true验证成功,false验证失败。</returns>
        public static bool VerifySignature(string encryptSource, string compareString, string publicKey)
        {
            try
            {
                //.net core2.2及其以下版本使用,重写FromXmlString(string)方法
                //using RSACryptoServiceProvider rsa = FromXmlString(RSAPublicKeyJava2DotNet(publicKey));
                var rsa = new RSACryptoServiceProvider();
                rsa.FromXmlString(RSAPublicKeyJava2DotNet(publicKey)); //.net core3.0直接使用,不需要重写
                byte[] signature = Convert.FromBase64String(encryptSource);
                //SHA1Managed sha1 = new SHA1Managed();
                SHA256Managed sha256 = new SHA256Managed();
               
                RSAPKCS1SignatureDeformatter df = new RSAPKCS1SignatureDeformatter(rsa);
                df.SetHashAlgorithm("SHA256");
                byte[] compareByte = sha256.ComputeHash(Encoding.UTF8.GetBytes(compareString));

                return df.VerifySignature(compareByte, signature);
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        /// RSA私钥,从Java格式转.net格式(不依赖第三方包)
        /// </summary>
        /// <param name="privateKey">私钥</param>
        /// <returns></returns>
        public static string RSAPrivateKeyJava2DotNet(string privateKey)
        {
            RsaPrivateCrtKeyParameters privateKeyParam =
                (RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(Convert.FromBase64String(privateKey));
            return string.Format(
                "<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent><P>{2}</P><Q>{3}</Q><DP>{4}</DP><DQ>{5}</DQ><InverseQ>{6}</InverseQ><D>{7}</D></RSAKeyValue>",
                Convert.ToBase64String(privateKeyParam.Modulus.ToByteArrayUnsigned()),
                Convert.ToBase64String(privateKeyParam.PublicExponent.ToByteArrayUnsigned()),
                Convert.ToBase64String(privateKeyParam.P.ToByteArrayUnsigned()),
                Convert.ToBase64String(privateKeyParam.Q.ToByteArrayUnsigned()),
                Convert.ToBase64String(privateKeyParam.DP.ToByteArrayUnsigned()),
                Convert.ToBase64String(privateKeyParam.DQ.ToByteArrayUnsigned()),
                Convert.ToBase64String(privateKeyParam.QInv.ToByteArrayUnsigned()),
                Convert.ToBase64String(privateKeyParam.Exponent.ToByteArrayUnsigned()));
        }

        /// <summary>
        /// 私钥签名
        /// </summary>
        /// <param name="contentForSign"></param>
        /// <param name="privateKey"></param>
        /// <returns></returns>
        public static string Sign(string contentForSign, string privateKey)
        {
            var netKey = RSAPrivateKeyJava2DotNet(privateKey); //转换成适用于.net的私钥

            //var rsa = FromXmlString(netKey); //.net core2.2及其以下版本使用,重写FromXmlString(string)方法

            var rsa = new RSACryptoServiceProvider();
            rsa.FromXmlString(netKey); //.net core3.0直接使用,不需要重写

            var rsaClear = new RSACryptoServiceProvider();
            var paras = rsa.ExportParameters(true);
            rsaClear.ImportParameters(paras); //签名返回
            //var sha1 = new SHA1CryptoServiceProvider();
            using (var sha256 = new SHA256CryptoServiceProvider())
            {
                var signData = rsa.SignData(Encoding.UTF8.GetBytes(contentForSign), sha256);
                return Convert.ToBase64String(signData);
            }
        }
    }
}

接口如下

using System.Text;
using Microsoft.AspNetCore.Mvc;

namespace RSACSharpJsStu01.Controllers
{
    [ApiController]
    public class IndexController : ControllerBase
    {
        // 公钥
        private string pk = //"-----BEGIN PUBLIC KEY-----\n" +
                "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD3XSdz1MnzazBEN5KOfTx0IyVJ\n" +
                "Z5wb57isrCuHDhnYXwtmdhQalgII0fozeeFpMpAvlnmHC1kpW7XVGvZnLx3bWbCE\n" +
                "bf+pMSW4kmQuI+5cxRUJbCl7sdaODBrINgERHPICVC18AJLThEVMHyjuR6Jn4zQm\n" +
                "yYNbReSktY/BrFTvMQIDAQAB\n"
            //+"-----END PUBLIC KEY-----"
            ;

        // 私钥
        private string priK = //"-----BEGIN PRIVATE KEY-----\n" +
                "MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAPddJ3PUyfNrMEQ3\n" +
                "ko59PHQjJUlnnBvnuKysK4cOGdhfC2Z2FBqWAgjR+jN54WkykC+WeYcLWSlbtdUa\n" +
                "9mcvHdtZsIRt/6kxJbiSZC4j7lzFFQlsKXux1o4MGsg2AREc8gJULXwAktOERUwf\n" +
                "KO5HomfjNCbJg1tF5KS1j8GsVO8xAgMBAAECgYEA6eG1JMrj63jEmStmMb1txG1a\n" +
                "mu4Q5z2QGgtr2HVXsIIlGEq6tWxyHf7TL4qkuz9onuYKn8n2Eqm44fZtVaBx+5ES\n" +
                "zRpIvlTvaxmVu0HZ1hYAzUw1XyRnXNMKpL5tT4GCjm8+QGPzlGxgXI1sNg8r9Jaw\n" +
                "9zRUYeA6LQR9RIMkHWUCQQD8QojjVoGjtiunoh/N8iplhUszZIavAEvmDIE+kVy+\n" +
                "pA7hvlukLw6JMc7cfTcnHyxDo9iHVIzrWlTuKRq9KWVLAkEA+wgJS2sgtldnCVn6\n" +
                "tJKFVwsHrWhMIU29msPPbNuWUD23BcKE/vehIyFu1ahNA/TiM40PEnzprQ5JfPxU\n" +
                "16S78wJANTfMLTnYy7Lo7sqTLx2BuD0wqjzw9QZ4/KVytsJv8IAn65P/PVn4FRV+\n" +
                "8KEx+3zmF7b/PT2nJRe/hycAzxtmlQJBAMrFwQxEqpXfoAEzx4lY2ZBn/nmaR/SW\n" +
                "4VNEXCbocVC7qT1j1R5HVMgV13uKiTtq8dUGWmhqsi7x3XayNK5ECPUCQQDZaAN6\n" +
                "tvIHApz9OLsXSw0jZirQ6KEYdharXbIVDy1W1sVE3lzLbqLdFp1bxAHQIvsYS5PM\n" +
                "A9veSJh372RLJKkj\n"
            //+"-----END PRIVATE KEY-----"
            ;

        [HttpGet("/rsa")]
        public string Get()
        {
            var src = "好厉害";
            src =
                "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiMTU2MjEzNTc1MDEiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjQ4MDY5NjUyNDQ4NDY3NyIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0";
            var sign = RSAHelper.Sign(src, priK);
            StringBuilder result = new StringBuilder();
            result.Append("加签结果:");
            result.Append(sign);
            var isSuccess = RSAHelper.VerifySignature(sign,src, pk);
            result.Append("验签结果:");
            result.Append(isSuccess + "");
            return result.ToString();
        }
    }
}

在这里插入图片描述
转化为RsaPemHelper

 public class PemHelper
    {
        static private Regex _PEMCode = new Regex(@"--+.+?--+|\s+");
        static private byte[] _SeqOID = new byte[] { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
        static private byte[] _Ver = new byte[] { 0x02, 0x01, 0x00 };

        /// <summary>
        /// 用PEM格式密钥对创建RSA,支持PKCS#1、PKCS#8格式的PEM
        /// </summary>
        public static RSACryptoServiceProvider FromPEM(string pem)
        {
            var rsaParams = new CspParameters();
            rsaParams.Flags = CspProviderFlags.UseMachineKeyStore;
            var rsa = new RSACryptoServiceProvider(rsaParams);

            var param = new RSAParameters();

            var base64 = _PEMCode.Replace(pem, "");
            var data = RSAUnitHelper.Base64DecodeBytes(base64);
            if (data == null)
            {
                throw new Exception("PEM内容无效");
            }
            var idx = 0;

            //读取长度
            Func<byte, int> readLen = (first) => {
                if (data[idx] == first)
                {
                    idx++;
                    if (data[idx] == 0x81)
                    {
                        idx++;
                        return data[idx++];
                    }
                    else if (data[idx] == 0x82)
                    {
                        idx++;
                        return (((int)data[idx++]) << 8) + data[idx++];
                    }
                    else if (data[idx] < 0x80)
                    {
                        return data[idx++];
                    }
                }
                throw new Exception("PEM未能提取到数据");
            };
            //读取块数据
            Func<byte[]> readBlock = () => {
                var len = readLen(0x02);
                if (data[idx] == 0x00)
                {
                    idx++;
                    len--;
                }
                var val = data.sub(idx, len);
                idx += len;
                return val;
            };
            //比较data从idx位置开始是否是byts内容
            Func<byte[], bool> eq = (byts) => {
                for (var i = 0; i < byts.Length; i++, idx++)
                {
                    if (idx >= data.Length)
                    {
                        return false;
                    }
                    if (byts[i] != data[idx])
                    {
                        return false;
                    }
                }
                return true;
            };




            if (pem.Contains("PUBLIC KEY"))
            {
                /****使用公钥****/
                //读取数据总长度
                readLen(0x30);
                if (!eq(_SeqOID))
                {
                    throw new Exception("PEM未知格式");
                }
                //读取1长度
                readLen(0x03);
                idx++;//跳过0x00
                      //读取2长度
                readLen(0x30);

                //Modulus
                param.Modulus = readBlock();

                //Exponent
                param.Exponent = readBlock();
            }
            else if (pem.Contains("PRIVATE KEY"))
            {
                /****使用私钥****/
                //读取数据总长度
                readLen(0x30);

                //读取版本号
                if (!eq(_Ver))
                {
                    throw new Exception("PEM未知版本");
                }

                //检测PKCS8
                var idx2 = idx;
                if (eq(_SeqOID))
                {
                    //读取1长度
                    readLen(0x04);
                    //读取2长度
                    readLen(0x30);

                    //读取版本号
                    if (!eq(_Ver))
                    {
                        throw new Exception("PEM版本无效");
                    }
                }
                else
                {
                    idx = idx2;
                }

                //读取数据
                param.Modulus = readBlock();
                param.Exponent = readBlock();
                param.D = readBlock();
                param.P = readBlock();
                param.Q = readBlock();
                param.DP = readBlock();
                param.DQ = readBlock();
                param.InverseQ = readBlock();
            }
            else
            {
                throw new Exception("pem需要BEGIN END标头");
            }

            rsa.ImportParameters(param);
            return rsa;
        }

        /// <summary>
        /// 将RSA中的密钥对转换成PEM格式,usePKCS8=false时返回PKCS#1格式,否则返回PKCS#8格式,如果convertToPublic含私钥的RSA将只返回公钥,仅含公钥的RSA不受影响
        /// </summary>
        public static string ToPEM(RSACryptoServiceProvider rsa, bool convertToPublic, bool usePKCS8)
        {
            var ms = new MemoryStream();
            //写入一个长度字节码
            Action<int> writeLenByte = (len) => {
                if (len < 0x80)
                {
                    ms.WriteByte((byte)len);
                }
                else if (len <= 0xff)
                {
                    ms.WriteByte(0x81);
                    ms.WriteByte((byte)len);
                }
                else
                {
                    ms.WriteByte(0x82);
                    ms.WriteByte((byte)(len >> 8 & 0xff));
                    ms.WriteByte((byte)(len & 0xff));
                }
            };
            //写入一块数据
            Action<byte[]> writeBlock = (byts) => {
                var addZero = (byts[0] >> 4) >= 0x8;
                ms.WriteByte(0x02);
                var len = byts.Length + (addZero ? 1 : 0);
                writeLenByte(len);

                if (addZero)
                {
                    ms.WriteByte(0x00);
                }
                ms.Write(byts, 0, byts.Length);
            };
            //根据后续内容长度写入长度数据
            Func<int, byte[], byte[]> writeLen = (index, byts) => {
                var len = byts.Length - index;

                ms.SetLength(0);
                ms.Write(byts, 0, index);
                writeLenByte(len);
                ms.Write(byts, index, len);

                return ms.ToArray();
            };


            if (rsa.PublicOnly || convertToPublic)
            {
                /****生成公钥****/
                var param = rsa.ExportParameters(false);


                //写入总字节数,不含本段长度,额外需要24字节的头,后续计算好填入
                ms.WriteByte(0x30);
                var index1 = (int)ms.Length;

                //固定内容
                // encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"
                ms.writeAll(_SeqOID);

                //从0x00开始的后续长度
                ms.WriteByte(0x03);
                var index2 = (int)ms.Length;
                ms.WriteByte(0x00);

                //后续内容长度
                ms.WriteByte(0x30);
                var index3 = (int)ms.Length;

                //写入Modulus
                writeBlock(param.Modulus);

                //写入Exponent
                writeBlock(param.Exponent);


                //计算空缺的长度
                var byts = ms.ToArray();

                byts = writeLen(index3, byts);
                byts = writeLen(index2, byts);
                byts = writeLen(index1, byts);


                return "-----BEGIN PUBLIC KEY-----\n" + RSAUnitHelper.TextBreak(RSAUnitHelper.Base64EncodeBytes(byts), 64) + "\n-----END PUBLIC KEY-----";
            }
            else
            {
                /****生成私钥****/
                var param = rsa.ExportParameters(true);

                //写入总字节数,后续写入
                ms.WriteByte(0x30);
                int index1 = (int)ms.Length;

                //写入版本号
                ms.writeAll(_Ver);

                //PKCS8 多一段数据
                int index2 = -1, index3 = -1;
                if (usePKCS8)
                {
                    //固定内容
                    ms.writeAll(_SeqOID);

                    //后续内容长度
                    ms.WriteByte(0x04);
                    index2 = (int)ms.Length;

                    //后续内容长度
                    ms.WriteByte(0x30);
                    index3 = (int)ms.Length;

                    //写入版本号
                    ms.writeAll(_Ver);
                }

                //写入数据
                writeBlock(param.Modulus);
                writeBlock(param.Exponent);
                writeBlock(param.D);
                writeBlock(param.P);
                writeBlock(param.Q);
                writeBlock(param.DP);
                writeBlock(param.DQ);
                writeBlock(param.InverseQ);


                //计算空缺的长度
                var byts = ms.ToArray();

                if (index2 != -1)
                {
                    byts = writeLen(index3, byts);
                    byts = writeLen(index2, byts);
                }
                byts = writeLen(index1, byts);


                var flag = " PRIVATE KEY";
                if (!usePKCS8)
                {
                    flag = " RSA" + flag;
                }
                return "-----BEGIN" + flag + "-----\n" + RSAUnitHelper.TextBreak(RSAUnitHelper.Base64EncodeBytes(byts), 64) + "\n-----END" + flag + "-----";
            }
        }
    }

如果有部分密钥加密解密报错可以使用PemHelper.FromPEM替代FromXmlString
可以看到js加签和C#加签结果一样,所以验签也是通过的

java代码实现

SHA256withRSAUtil.java

package com.wujialiang;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class SHA256withRSAUtil {

    private static final String RSA = "RSA";
    private static final String SHA256WITHRSA = "SHA256withRSA";

    /**
     * 生成RSA密钥
     * @return
     * @throws NoSuchAlgorithmException
     */
    public static KeyPair generateKeyPair() throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA);
        keyPairGenerator.initialize(2048, new SecureRandom());
        return keyPairGenerator.genKeyPair();
    }

    /**
     * 获取私钥
     * @param base64
     * @return
     */
    public static PrivateKey getPrivateKey(String base64) throws Exception {
        // 解码Base64字符串回字节数组
        byte[] encodedPrivateKey = Base64.getDecoder().decode(base64);

        // 使用PKCS8EncodedKeySpec和KeyFactory来加载私钥
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedPrivateKey);
        KeyFactory kf = KeyFactory.getInstance(RSA);
        PrivateKey privateKey = kf.generatePrivate(keySpec);
        return privateKey;
    }

    /**
     * 获取私钥
     * @param base64
     * @return
     */
    public static PublicKey getPublicKey(String base64) throws Exception {
        // 解码Base64字符串回字节数组
        byte[] encodedPrivateKey = Base64.getDecoder().decode(base64);

        // 使用PKCS8EncodedKeySpec和KeyFactory来加载私钥
        //PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedPrivateKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encodedPrivateKey);
        KeyFactory kf = KeyFactory.getInstance(RSA);
        PublicKey publicKey = kf.generatePublic(keySpec);
        return publicKey;
    }

    /**
     * 使用私钥对明文进行签名
     *
     * @param plainText  明文
     * @param privateKey 私钥
     * @return 签名结果
     * @throws Exception 签名异常
     */
    public static String sign(String plainText, PrivateKey privateKey) throws Exception {
        Signature signature = Signature.getInstance(SHA256WITHRSA);
        signature.initSign(privateKey);
        signature.update(plainText.getBytes());
        return Base64.getEncoder().encodeToString(signature.sign());
    }


    /**
     * 使用公钥对签名进行验证
     *
     * @param plainText 明文
     * @param signature 签名结果
     * @param publicKey 公钥
     * @return 验证结果,true表示验证通过,false表示验证失败
     * @throws Exception 验证异常
     */
    public static boolean verify(String plainText, String signature, PublicKey publicKey) throws Exception {
        Signature signatureInstance = Signature.getInstance(SHA256WITHRSA);
        signatureInstance.initVerify(publicKey);
        signatureInstance.update(plainText.getBytes());
        byte[] decodedSignature = Base64.getDecoder().decode(signature);
        return signatureInstance.verify(decodedSignature);
    }
}

使用

package com.wujialiang;

/**
 * Hello world!
 */
public class App {
    public static void main(String[] args) {
        // 生成RSA密钥对
//        KeyPair keyPair = generateKeyPair();
//        PrivateKey privateKey = keyPair.getPrivate();
//        PublicKey publicKey = keyPair.getPublic();
        // 公钥
        String pk = //"-----BEGIN PUBLIC KEY-----\n" +
                "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD3XSdz1MnzazBEN5KOfTx0IyVJ\n" +
                        "Z5wb57isrCuHDhnYXwtmdhQalgII0fozeeFpMpAvlnmHC1kpW7XVGvZnLx3bWbCE\n" +
                        "bf+pMSW4kmQuI+5cxRUJbCl7sdaODBrINgERHPICVC18AJLThEVMHyjuR6Jn4zQm\n" +
                        "yYNbReSktY/BrFTvMQIDAQAB\n"
                //+"-----END PUBLIC KEY-----"
                ;

        // 私钥
        String priK = //"-----BEGIN PRIVATE KEY-----\n" +
                "MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAPddJ3PUyfNrMEQ3\n" +
                        "ko59PHQjJUlnnBvnuKysK4cOGdhfC2Z2FBqWAgjR+jN54WkykC+WeYcLWSlbtdUa\n" +
                        "9mcvHdtZsIRt/6kxJbiSZC4j7lzFFQlsKXux1o4MGsg2AREc8gJULXwAktOERUwf\n" +
                        "KO5HomfjNCbJg1tF5KS1j8GsVO8xAgMBAAECgYEA6eG1JMrj63jEmStmMb1txG1a\n" +
                        "mu4Q5z2QGgtr2HVXsIIlGEq6tWxyHf7TL4qkuz9onuYKn8n2Eqm44fZtVaBx+5ES\n" +
                        "zRpIvlTvaxmVu0HZ1hYAzUw1XyRnXNMKpL5tT4GCjm8+QGPzlGxgXI1sNg8r9Jaw\n" +
                        "9zRUYeA6LQR9RIMkHWUCQQD8QojjVoGjtiunoh/N8iplhUszZIavAEvmDIE+kVy+\n" +
                        "pA7hvlukLw6JMc7cfTcnHyxDo9iHVIzrWlTuKRq9KWVLAkEA+wgJS2sgtldnCVn6\n" +
                        "tJKFVwsHrWhMIU29msPPbNuWUD23BcKE/vehIyFu1ahNA/TiM40PEnzprQ5JfPxU\n" +
                        "16S78wJANTfMLTnYy7Lo7sqTLx2BuD0wqjzw9QZ4/KVytsJv8IAn65P/PVn4FRV+\n" +
                        "8KEx+3zmF7b/PT2nJRe/hycAzxtmlQJBAMrFwQxEqpXfoAEzx4lY2ZBn/nmaR/SW\n" +
                        "4VNEXCbocVC7qT1j1R5HVMgV13uKiTtq8dUGWmhqsi7x3XayNK5ECPUCQQDZaAN6\n" +
                        "tvIHApz9OLsXSw0jZirQ6KEYdharXbIVDy1W1sVE3lzLbqLdFp1bxAHQIvsYS5PM\n" +
                        "A9veSJh372RLJKkj\n"
                //+"-----END PRIVATE KEY-----"
                ;
        // 明文
        String src = "好厉害";
        src =
                "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiMTU2MjEzNTc1MDEiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjQ4MDY5NjUyNDQ4NDY3NyIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd3MvMjAwOC8wNi9pZGVudGl0";


        //私钥签名
        String c = null;
        try {
            c = SHA256withRSAUtil.sign(src, SHA256withRSAUtil.getPrivateKey(priK.replace("\n","")));
            System.out.println(c);

            //公钥验签(true表示验证通过,false表示验证失败)
            System.out.println("验证结果:" + SHA256withRSAUtil.verify(src, c, SHA256withRSAUtil.getPublicKey(pk.replace("\n",""))));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

在这里插入图片描述

可以看到java加签和C#、js加签结果一样,所以验签也是通过的

参考

rsa js和java互通
C# rsa加签验签

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1821495.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Altium】AD-Fill、Region、Polygon之间的区别

【更多软件使用问题请点击亿道电子官方网站】 1、 文档目标 Fill、Polygon、Region介绍&#xff0c;了解三者的区别。 2、 知识点 正片层、负片层&#xff0c;以及AD叠层管理中的设置。 3、软硬件环境 1&#xff09;、无关 2&#xff09;、无关 3&#xff09;、无关 4、…

动作识别综合指南

本文将概述当前动作识别&#xff08;action recognition&#xff09;的方法和途径。 为了展示动作识别任务的复杂性&#xff0c;我想举这个例子&#xff1a; 你能明白我在这里做什么吗&#xff1f;我想不能。至少你不会确定答案。我正在钻孔。 你能弄清楚我接下来要做什么吗&…

10. 安全性

这里写自定义目录标题 第10章 安全性10.1 安全性通用场景10.2 安全性策略不安全状态避免替代预测模型 不安全状态检测超时时间戳条件监测健全性检查比较 抑制冗余限制后果屏障 恢复 10.3基于策略的安全问卷10.4 安全性的模式10.5 扩展阅读10.6 问题讨论 第10章 安全性 吉尔斯&a…

GaN VCSEL:工艺革新引领精准波长控制新纪元

日本工程师们凭借精湛的技艺&#xff0c;开创了一种革命性的生产工艺&#xff0c;让VCSEL的制造达到了前所未有的高效与精准。这一成果由名城大学与国家先进工业科学技术研究所的精英们联手铸就&#xff0c;将氮化镓基VCSELs的商业化进程推向了新的高峰。它们将有望成为自适应前…

ArcGIS for js 4.x FeatureLayer 点选查询

示例&#xff1a; 代码如下&#xff1a; <template><view id"mapView"></view></template><script setup> import "arcgis/core/assets/esri/themes/light/main.css"; import Map from "arcgis/core/Map.js"; im…

【AI基础】第五步:纯天然保姆喂饭级-安装并运行chatglm3-6b

类似于 【AI基础】第三步&#xff1a;纯天然保姆喂饭级-安装并运行chatglm2-6b&#xff0c;有一些细节不一样。 此系列文章列表&#xff1a; 【AI基础】第一步&#xff1a;安装python开发环境-windows篇_下载安装ai环境python 【AI基础】第一步&#xff1a;安装python开发环境-…

五分钟看完WWDC24

大家好&#xff0c;我是小编阿文。欢迎您关注我们&#xff0c;经常分享有关Android出海&#xff0c;iOS出海&#xff0c;App市场政策实时更新&#xff0c;互金市场投放策略&#xff0c;最新互金新闻资讯等文章&#xff0c;期待与您共航世界之海。 北京时间6月11日凌晨1点&…

SylixOS下UDP组播测试程序

SylixOS下UDP组播测试 测试效果截图如下: udp组播发送测试程序。 /********************************************************************************************************* ** ** 中国软件开源组织 ** ** …

华为wlan实验

分为三步&#xff1a;1、网络互通&#xff0c;2、AP上线&#xff0c;3、wlan业务 1、网络互通 crow-sw: vlan batch 20 100 dhcp enable int vlan 20 ip add 192.168.20.1 24 dhcp select interfaceinterface GigabitEthernet0/0/2port link-type accessport default vlan 100…

构建 LLM 应用为什么需要文本加载器,langchain 中如何使用文本加载器?

构建 LLM 应用为什么需要文本加载器&#xff0c;langchain 中如何使用文本加载器&#xff1f; 上一篇文章中 [使用langchain搭建本地知识库系统(新) 我们构建一个 RAG 的本地应用&#xff0c;我们使用到了网页的文本加载器用来动态获取网页的数据。 在不同的应用场景中需要使…

【Spine学习07】之跑步动作制作思路总结

前几节试着做了待机和走路动画 现在开始尝试做跑步动作 注意跑步动作和走路一样 暂时不需要使用IK约束但是会用到塞贝尔曲线&#xff08;模拟裙子飞起动效&#xff09; 第一步&#xff1a; 先将人物整体斜放置&#xff08;因为人跑步的时候&#xff0c;身体前倾&#xff09; …

速度与激情:解锁8款免费文件传输利器,让大数据秒传成为可能

以下是8个免费高速文件传输工具的推荐&#xff0c;这些工具可以帮助您彻底告别数据线&#xff0c;使文件传输更加便捷和高效&#xff1a; 1、百度网盘 特点&#xff1a;云存储和共享应用&#xff0c;支持多种形式的文件存储和分享&#xff0c;提供大容量的免费存储空间。 适用…

力扣每日一题(2024-06-14)2786. 访问数组中的位置使分数最大

参考官方题解2786. 访问数组中的位置使分数最大 - 力扣&#xff08;LeetCode&#xff09; 问题描述 给定一个下标从 0 开始的整数数组 nums 和一个正整数 x。你一开始在数组的第 0 个位置&#xff0c;你可以移动到满足 i < j 的任意位置 j。如果你访问的位置 i&#xff0c…

毕业生季,你的校园卡开始注销了吗?

​ 毕业季&#xff0c;好多朋友们已经走出校园了&#xff0c;换了一个新的城市接着工作了&#xff0c;那么&#xff0c;之前办理的校园卡你都是怎么处理的&#xff1f; 其实&#xff0c;校园卡就是为了方便校园生活而推出的一种卡&#xff0c;在学校期间可能比较优惠&#xff…

企业薪酬体系的搭建

随着企业的逐步发展&#xff0c;其人力资源管理方面的问题也逐渐显露出来&#xff0c;诸如职责不清、相互推卸责任、员工工作积极性较低等问题&#xff0c;这些管理上的问题导致产品质量不断下降&#xff0c;客户投诉率也不断上升&#xff0c;且优秀人员的流失率也有增加的趋势…

沃沃阀门×蓝卓 | 再度携手!数字化车间项目启动会顺利召开

6月13日&#xff0c;蓝卓与沃沃阀门数字化车间项目正式启动&#xff0c;依托蓝卓supOS工业操作系统&#xff0c;打造统一数字化底座&#xff0c;助推沃沃阀门物料自动配送、产销高效协同、设备全面管理、车间可视化管理等目标实现。 丽水莲都区经信局副局长李军舫、区经信局信…

Arco-design <a-range-picker> 快捷键日期点击后不关闭面板

需求&#xff1a;点击快捷日期&#xff0c;面板预览日期&#xff0c;点击确定后触发事件 思路&#xff1a;手动控制面板开启和关闭&#xff0c;点击input时开启&#xff0c;点击面板确定或除input和面板的其他位置时关闭面板 <a-range-pickerid"input"v-model&quo…

学习理解 CompletableFuture

学习理解 CompletableFuture CompletableFuture 是 Java 8 引入的一个强大的工具&#xff0c;它使得编写异步和并行代码变得更加容易。 CompletableFuture 是 java.util.concurrent 包的一部分&#xff0c;是 Future 的扩展&#xff0c;它不仅允许你手动完成计算&#xff0c;还…

锂电池航空运输报告办理 UN38.3电池报告

锂电池航空运输报告办理 锂电池航空运输报告&#xff0c;通常指的是UN38.3测试报告&#xff0c;这是联合国针对危险品运输制定的《联合国危险物品运输试验和标准手册》的第3部分38.3款的要求。UN38.3测试报告证明锂电池在运输过程中的安全性&#xff0c;包括高度模拟、高低温循…

Liquibase(Oracle SQLcl集成版)简明示例

本文使用的是Oracle SQLcl中集成的Liquibase&#xff0c;而非开源版Liquibase。 Liquibase的快速入门可以参见Liquibase Core Concepts。需要了解一下概念&#xff1a; Change log&#xff1a;基于文本的更改日志文件按顺序列出对数据库所做的所有更改Change set&#xff1a;…