• 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("
    ", "").Replace("
    ", "");
        }
        else { return pemKey; }
    }
    

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

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

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

    密钥转换(java)

    • 公钥:X.509 pem,Java:X509EncodedKeySpec
    • 私钥:PKCS#8 pem,Java:PKCS8EncodedKeySpec
    /**
     * C#私钥转换成java私钥
     */
    public static String privateKeyFromXml(String privateKeyXml) {
    	privateKeyXml = privateKeyXml.replaceAll("
    ", "").replaceAll("
    ", "");
    
    	KeyFactory keyFactory;
    	try {
    		String modulusXml = privateKeyXml.substring(privateKeyXml.indexOf("<Modulus>") + 9, privateKeyXml.indexOf("</Modulus>"));
    		BigInteger modulus = new BigInteger(1, Base64.getDecoder().decode(modulusXml));
    
    		String publicExponentXml = privateKeyXml.substring(privateKeyXml.indexOf("<Exponent>") + 10, privateKeyXml.indexOf("</Exponent>"));
    		BigInteger publicExponent = new BigInteger(1, Base64.getDecoder().decode(publicExponentXml));
    
    		String privateExponentXml = privateKeyXml.substring(privateKeyXml.indexOf("<D>") + 3, privateKeyXml.indexOf("</D>"));
    		BigInteger privateExponent = new BigInteger(1, Base64.getDecoder().decode(privateExponentXml));
    
    		String primePXml = privateKeyXml.substring(privateKeyXml.indexOf("<P>") + 3, privateKeyXml.indexOf("</P>"));
    		BigInteger primeP = new BigInteger(1, Base64.getDecoder().decode(primePXml));
    
    		String primeQXml = privateKeyXml.substring(privateKeyXml.indexOf("<Q>") + 3, privateKeyXml.indexOf("</Q>"));
    		BigInteger primeQ = new BigInteger(1, Base64.getDecoder().decode(primeQXml));
    
    		String primeExponentPXml = privateKeyXml.substring(privateKeyXml.indexOf("<DP>") + 4, privateKeyXml.indexOf("</DP>"));
    		BigInteger primeExponentP = new BigInteger(1, Base64.getDecoder().decode(primeExponentPXml));
    
    		String primeExponentQXml = privateKeyXml.substring(privateKeyXml.indexOf("<DQ>") + 4, privateKeyXml.indexOf("</DQ>"));
    		BigInteger primeExponentQ = new BigInteger(1, Base64.getDecoder().decode(primeExponentQXml));
    
    		String crtCoefficientXml = privateKeyXml.substring(privateKeyXml.indexOf("<InverseQ>") + 10, privateKeyXml.indexOf("</InverseQ>"));
    		BigInteger crtCoefficient = new BigInteger(1, Base64.getDecoder().decode(crtCoefficientXml));
    
    		RSAPrivateCrtKeySpec rsaPriKey = new RSAPrivateCrtKeySpec(modulus, publicExponent, privateExponent, primeP, primeQ, primeExponentP, primeExponentQ, crtCoefficient);
    
    		keyFactory = KeyFactory.getInstance("RSA");
    		PrivateKey privateKey = keyFactory.generatePrivate(rsaPriKey);
    		byte[] bytes = Base64.getEncoder().encode(privateKey.getEncoded());
    		return new String(bytes, Charset.forName("utf-8"));
    	} catch (Exception e) {
    		System.err.println(e.toString());
    	}
    	return null;
    }
    
    /**
     * C#公钥转换成java公钥
     */
    public static String publicKeyFromXml(String publicKeyXml) {
    	KeyFactory keyFactory;
    	publicKeyXml = publicKeyXml.replaceAll("
    ", "").replaceAll("
    ", "");
    	try {
    		String modulusXml = publicKeyXml.substring(publicKeyXml.indexOf("<Modulus>") + 9, publicKeyXml.indexOf("</Modulus>"));
    		BigInteger modulus = new BigInteger(1, Base64.getDecoder().decode(modulusXml));
    
    		String exponentXml = publicKeyXml.substring(publicKeyXml.indexOf("<Exponent>") + 10, publicKeyXml.indexOf("</Exponent>"));
    		BigInteger publicExponent = new BigInteger(1, Base64.getDecoder().decode(exponentXml));
    
    		RSAPublicKeySpec rsaPubKey = new RSAPublicKeySpec(modulus, publicExponent);
    		keyFactory = KeyFactory.getInstance("RSA");
    		PublicKey publicKey = keyFactory.generatePublic(rsaPubKey);
    		byte[] bytes = Base64.getEncoder().encode(publicKey.getEncoded());
    		return new String(bytes, Charset.forName("utf-8"));
    	} catch (Exception e) {
    		System.err.println(e.toString());
    		return null;
    	}
    }
    
    /**
     * java私钥转换成C#私钥
     */
    public static String privateKeyToXml(RSAPrivateCrtKey privateKey) {
    
    	String modulusBase64 = Base64.getEncoder().encodeToString(privateKey.getModulus().toByteArray());
    	String modulus = modulusBase64.replace("
    ", "").replace("
    ", "");
    
    	String exponentBase64 = Base64.getEncoder().encodeToString(privateKey.getPublicExponent().toByteArray());
    	String exponent = exponentBase64.replace("
    ", "").replace("
    ", "");
    
    	String pBase64 = Base64.getEncoder().encodeToString(privateKey.getPrimeP().toByteArray());
    	String p = pBase64.replace("
    ", "").replace("
    ", "");
    
    	String qBase64 = Base64.getEncoder().encodeToString(privateKey.getPrimeQ().toByteArray());
    	String q = qBase64.replace("
    ", "").replace("
    ", "");
    
    	String dpBase64 = Base64.getEncoder().encodeToString(privateKey.getPrimeExponentP().toByteArray());
    	String dp = dpBase64.replace("
    ", "").replace("
    ", "");
    
    	String dqBase64 = Base64.getEncoder().encodeToString(privateKey.getPrimeExponentQ().toByteArray());
    	String dq = dqBase64.replace("
    ", "").replace("
    ", "");
    
    	String dBase64 = Base64.getEncoder().encodeToString(privateKey.getPrivateExponent().toByteArray());
    	String d = dBase64.replace("
    ", "").replace("
    ", "");
    	
    	String inverseQBase64 = Base64.getEncoder().encodeToString(privateKey.getCrtCoefficient().toByteArray());
    	String inverseQ = inverseQBase64.replace("
    ", "").replace("
    ", "");
    
    	StringBuilder stringBuilder = new StringBuilder();
    	stringBuilder.append("<RSAKeyValue>
    ");
    	stringBuilder.append("<Modulus>").append(modulus).append("</Modulus>
    ");
    	stringBuilder.append("<Exponent>").append(exponent).append("</Exponent>
    ");
    	stringBuilder.append("<P>").append(p).append("</P>
    ");
    	stringBuilder.append("<Q>").append(q).append("</Q>
    ");
    	stringBuilder.append("<DP>").append(dp).append("</DP>
    ");
    	stringBuilder.append("<DQ>").append(dq).append("</DQ>
    ");
    	stringBuilder.append("<InverseQ>").append(inverseQ).append("</InverseQ>
    ");
    	stringBuilder.append("<D>").append(d).append("</D>
    ");
    	stringBuilder.append("</RSAKeyValue>");
    	return stringBuilder.toString();
    }
    
    /**
     * java公钥转换成C#公钥
     */
    public static String publicKeyToXml(RSAPublicKey publicKey) {
    	String modulusBase64 = Base64.getEncoder().encodeToString(publicKey.getModulus().toByteArray());
    	String modulus = modulusBase64.replace("
    ", "").replace("
    ", "");
    
    	String exponentBase64 = Base64.getEncoder().encodeToString(publicKey.getPublicExponent().toByteArray());
    	String exponent = exponentBase64.replace("
    ", "").replace("
    ", "");
    
    	StringBuilder stringBuilder = new StringBuilder();
    	stringBuilder.append("<RSAKeyValue>
    ");
    	stringBuilder.append("<Modulus>").append(modulus).append("</Modulus>
    ");
    	stringBuilder.append("<Exponent>").append(exponent).append("</Exponent>
    ");
    	stringBuilder.append("</RSAKeyValue>");
    	return stringBuilder.toString();
    }
    

    详见:C#-Java密钥转换

    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进行非对称加解密有时间可以研究下。

  • 相关阅读:
    AtCoder Beginner Contest 064 D
    ZOJ 3956 Course Selection System [01背包]
    理解01背包
    模块(二)
    内置函数+递归+模块使用
    函数进阶
    使用markdown编辑器
    函数进阶(二)
    函数进阶(一)
    函数基础
  • 原文地址:https://www.cnblogs.com/wjcx-sqh/p/11580450.html
Copyright © 2020-2023  润新知