SHA256WithRSA

上文中了解到SHA和RSA,工作中恰好用到扩展应用:SHA256WithRSA,本文总结下学习过程,备忘の
再提供另外一种方法,实现Java版pem密钥和.Net版xml密钥相互转换的方法

密钥转换

准备:引入BouncyCastle.Crypto.dll

  • RSA密钥:Pem --> XML
public static string RSAKeyPemToXml(string pemKey, bool isPrivateKey)
{
    string rsaKey = string.Empty;
    object pemObject = null;
    RSAParameters rsaPara = new RSAParameters();
    using (var sReader = new StringReader(pemKey)) {
        var pemReader = new Org.BouncyCastle.OpenSsl.PemReader(sReader);
        pemObject = pemReader.ReadObject();//(AsymmetricCipherKeyPair)
    }
            
    if (isPrivateKey)//RSA私钥
    {
        RsaPrivateCrtKeyParameters key = (RsaPrivateCrtKeyParameters)((AsymmetricCipherKeyPair)pemObject).Private;
        rsaPara = new RSAParameters {
            Modulus = key.Modulus.ToByteArrayUnsigned(),
            Exponent = key.PublicExponent.ToByteArrayUnsigned(),
            D = key.Exponent.ToByteArrayUnsigned(),
            P = key.P.ToByteArrayUnsigned(),
            Q = key.Q.ToByteArrayUnsigned(),
            DP = key.DP.ToByteArrayUnsigned(),
            DQ = key.DQ.ToByteArrayUnsigned(),
            InverseQ = key.QInv.ToByteArrayUnsigned(),  };
    }
    else//RSA公钥
    {
        RsaKeyParameters key = (RsaKeyParameters)pemObject;
        rsaPara = new RSAParameters {
            Modulus = key.Modulus.ToByteArrayUnsigned(),
            Exponent = key.Exponent.ToByteArrayUnsigned(),  };
    }

    RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
    rsa.ImportParameters(rsaPara);
    using (StringWriter sw = new StringWriter()) {
        sw.Write(rsa.ToXmlString(isPrivateKey ? true : false));
        rsaKey = sw.ToString();
    }
    return rsaKey;
}
  • RSA密钥:XML --> Pem
public static string RSAKeyXmlToPem(string RSAKeyXml, bool isPrivateKey, bool replacefix)
{
    string pemKey = string.Empty;
    var rsa = new RSACryptoServiceProvider();
    rsa.FromXmlString(RSAKeyXml);

    RSAParameters rsaPara = new RSAParameters();
    RsaKeyParameters key = null;
    //RSA私钥
    if (isPrivateKey)  {
        rsaPara = rsa.ExportParameters(true);
        key = new RsaPrivateCrtKeyParameters(
            new BigInteger(1, rsaPara.Modulus), new BigInteger(1, rsaPara.Exponent), new BigInteger(1, rsaPara.D),
            new BigInteger(1, rsaPara.P), new BigInteger(1, rsaPara.Q), new BigInteger(1, rsaPara.DP), new BigInteger(1, rsaPara.DQ),
            new BigInteger(1, rsaPara.InverseQ));
    }
    //RSA公钥
    else  {
        rsaPara = rsa.ExportParameters(false);
        key = new RsaKeyParameters(false,
            new BigInteger(1, rsaPara.Modulus),  new BigInteger(1, rsaPara.Exponent));
    }

    using (TextWriter sw = new StringWriter()) {
        var pemWriter = new Org.BouncyCastle.OpenSsl.PemWriter(sw);
        pemWriter.WriteObject(key);
        pemWriter.Writer.Flush();
        pemKey = sw.ToString();
    }

    if (replacefix)  {
        //去掉证书的头部和尾部
        pemKey = isPrivateKey ? pemKey.Replace("-----BEGIN RSA PRIVATE KEY-----", "").Replace("-----END RSA PRIVATE KEY-----", "") :
            pemKey.Replace("-----BEGIN PUBLIC KEY-----", "").Replace("-----END PUBLIC KEY-----", "");
        return pemKey.Replace("\n", "").Replace("\r", "");
    }
    else { return pemKey; }
}

注意,调用RSAKeyPemToXml()方法时,pemKey必须格式正确(带前后缀且换行),否则调用报错。

-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----

此外,调用RSAKeyXmlToPem()方法做私钥转换时,结果与原Pem密钥不一致,慎用。

pfx证书

  • PFX证书:由Public Key Cryptography Standards #12,PKCS#12标准定义,包含公钥和私钥的二进制格式的证书形式,以pfx作为证书文件后缀名
  • CER证书:证书中没有私钥,DER编码二进制格式的证书文件/BASE64编码格式的证书文件,以cer作为证书文件后缀名

综上所述:pfx证书文件中比cer文件中多了私钥。
通过pfx证书实现数据签名和验签

public static string Sign(string dataForSign, string priKeyFile, string keyPwd)
{
    var rsa = GetPrivateKey(priKeyFile, keyPwd);

    // Create a new RSACryptoServiceProvider
    var rsaClear = new RSACryptoServiceProvider();
    // Export RSA parameters from 'rsa' and import them into 'rsaClear'
    var paras = rsa.ExportParameters(true);
    rsaClear.ImportParameters(paras);

    using (var sha256 = new SHA256CryptoServiceProvider()) {
        var signData = rsaClear.SignData(Encoding.UTF8.GetBytes(dataForSign), sha256);
        return BytesToHex(signData);
    }
}
public bool VerifySign(string dataForSign, string signedData, string pubKeyFile)
{
    var rsa = GetPublicKey(pubKeyFile);
    using (var sha256 = new SHA256CryptoServiceProvider()) {
        return rsa.VerifyData(Encoding.UTF8.GetBytes(dataForSign), sha256, HexToBytes(signedData));
    }   
}

其中,从.pfx证书中提取公钥和私钥的方法

private static RSACryptoServiceProvider GetPrivateKey(string priKeyFile, string keyPwd) {
    var pc = new X509Certificate2(priKeyFile, keyPwd,
        X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);          
    return (RSACryptoServiceProvider)pc.PrivateKey; //return cert.PrivateKey.ToXmlString(true);
}

private static RSACryptoServiceProvider GetPublicKey(string pubKeyFile) {
    var pc = new X509Certificate2(pubKeyFile);
    return (RSACryptoServiceProvider)pc.PublicKey.Key; //return cert.PublicKey.Key.ToXmlString(false);
}

具体参见:.NET版SHA256WithRSA签名验签,java版本参见:java版SHA256withRSA
关于如何生成数字证书,仅供参考:方法1方法2
该文C#创建数字证书并导出为pfx,并使用pfx进行非对称加解密有时间可以研究下。

转载于:https://www.cnblogs.com/wjcx-sqh/p/11580450.html