• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

RSA非对称加密简介

武飞扬头像
ccor2002
帮助1

RSA非对称加解密算法在实际开发中广泛使用,简单就目前前后端加解密常用方式小结一下。

RSA是什么:RSA公钥加密算法是1977年由Ron Rivest、Adi Shamirh和LenAdleman在(美国麻省理工学院)开发的。RSA取名来自开发他们三者的名字。RSA是目前最有影响力的公钥加密算法,它能够抵抗到目前为止已知的所有密码攻击,已被ISO推荐为公钥数据加密标准。目前该加密方式广泛用于网上银行、数字签名等场合。RSA算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。

OpenSSL是什么:众多的密码算法、公钥基础设施标准以及SSL协议,或许这些有趣的功能会让你产生实现所有这些算法和标准的想法。果真如此,在对你表示敬佩的同时,还是忍不住提醒你:这是一个令人望而生畏的过程。这个工作不再是简单的读懂几本密码学专著和协议文档那么简单,而是要理解所有这些算法、标准和协议文档的每一个细节,并用你可能很熟悉的C语言字符一个一个去实现这些定义和过程。我们不知道你将需要多少时间来完成这项有趣而可怕的工作,但肯定不是一年两年的问题。OpenSSL就是由Eric A. Young和Tim J. Hudson两位绝世大好人自1995年就开始编写的集合众多安全算法的算法集合。通过命令或者开发库,我们可以轻松实现标准的公开算法应用。

生成密钥

1、生成ras私钥

$ openssl genrsa -out private_key.pem 1024

输出

Generating RSA private key, 1024 bit long modulus (2 primes)

e is 65537 (0x010001)

2、生成公钥

$ openssl rsa -in private_key.pem -pubout -out public_key.pem

输出

writing RSA key

3、把私钥转为PKCS#8编码,因为JDK的加密组件JCE中需要读取PKCS#8格式

openssl pkcs8 -topk8 -in private_key.pem -out pkcs8_private_key.pem -nocrypt

生成的证书pem文件内容(以下是私钥的):

-----BEGIN RSA PRIVATE KEY-----
 BASE64 ENCODED DATA
-----END RSA PRIVATE KEY-----

Java部分

使用JDK的JCE组件(Java Cryptography Extension)

 import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;

public class RSAUtils {
		/**
     * 加密方法
     * @param publicKeyText 公钥文本串
     * @param source 源数据
     * @return
     * @throws Exception
     */
    public static String encrypt(String publicKeyText, String source) throws Exception {

        java.security.spec.X509EncodedKeySpec x509KeySpec = new java.security.spec.X509EncodedKeySpec(java.util.Base64.getDecoder().decode(publicKeyText));
        // RSA算法
        java.security.KeyFactory keyFactory = java.security.KeyFactory.getInstance("RSA");
        // 取公钥匙对象
        java.security.PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
        // 得到Cipher对象来实现对源数据的RSA加密
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] b = source.getBytes();
        /** 执行加密操作 */
        byte[] b1 = cipher.doFinal(b);
        return Base64.getEncoder().encodeToString(b1);

    }

    /**
     * 解密算法
     * @param privateKeyText 私钥文本串
     * @param cryptoText 密文
     * @return
     * @throws Exception
     */
    public static String decrypt(String privateKeyText, String cryptoText) throws Exception {
        Base64.Decoder decoder = java.util.Base64.getDecoder();

        PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(decoder.decode(privateKeyText));
        KeyFactory keyf = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyf.generatePrivate(priPKCS8);

        /** 得到Cipher对象对已用公钥加密的数据进行RSA解密 */
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] b1 = decoder.decode(cryptoText);

        /** 执行解密操作 */
        byte[] b = cipher.doFinal(b1);
        return new String(b);
    }

    public static void main(String[] args) throws Exception {
        //#priKeyText
        // 不需要头尾的注释行 -----BEGIN RSA PRIVATE KEY-----
        // 注意这里使用的是PCSK#8格式的私钥
        String priKeyText =
                "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAM RE49WU9V/jnmg"  
                "z3Snhi2sqnwXyLmpv7gQ/VLQ66FgdJw ONi//rXY0Y7tyOW7H5vgrhM6BBmixhBc"  
                "HoGaJwsOL6G0nIs9B9 sGMJQqupPOvGl51G/Svd5tBFT pf8eawjhBCt2KXC5/9J"  
                "StgvrKriz6UP9Gxf9XCDAmKdHu9BAgMBAAECgYBiR8giK3cAdShJ8Nmp25SN1d7X"  
                "aQ S0YxEpE6 Qsv/GkO8oz2Za7c39VSsP2zLUgHSdd0Wn3bHiKoNnwCWzx3CpKEx"  
                "UoQxXcAmVkZOksYsEDN6mb9cdDFHGxtNLsuA0HSE0VkvZwVSAU7O6W5v0mAt25Ei"  
                "eHywZqXapS/UYcmSXQJBAPTNrpIc Ir90ulhzJUDhXB4ip60Mq8yU83DGng7A9Cd"  
                "Nwa/U5rLmdXPPzCg/X4iNvw4QDtpz5QiQKVI5PcJK38CQQDZD2cJ7zPGCFVKGvck"  
                "oGqAb02/sujIZB1LpWTC gxni7sUhMeh6lCNGnbrI10brtUeYG7sQiOHhcsFIUkI"  
                "3EU/AkBqiDe4DjN7CQqH1a aCXZAg7SPbAvCgjxGBum1 LioSEJyBUD01DWV/iKZ"  
                "nvWxtmIHUNNjB6D17hID270IZE0xAkAkGC2YN1Cys5wLMKS8UHilVtzk7clL2WoN"  
                "tDknOxFo/pFjfhj8UcyHSklQHJdVDSOfY4XOf8kMtjDmb3yHnqldAkEAjC3FHa/8"  
                "3uuRgH8QMk3hQWSCLcI/b7ccdE1xUU7SJ9PSkMx6zhe0n pPIHsaPSdzk0Tk03w "  
                "o6yDfIfIH6td4A==";

        //#pubKeyText
        String pubKeyText = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDPkROPVlPVf455oM90p4YtrKp8"  
                "F8i5qb 4EP1S0OuhYHScPjjYv/612NGO7cjlux b4K4TOgQZosYQXB6BmicLDi h"  
                "tJyLPQffrBjCUKrqTzrxpedRv0r3ebQRU/qX/HmsI4QQrdilwuf/SUrYL6yq4s l"  
                "D/RsX/VwgwJinR7vQQIDAQAB";
        String s = encrypt(pubKeyText, "1231");
        System.out.println(s);
        String a = decrypt(priKeyText, s);
        System.out.println(a);
    }
}
学新通

需要注意的是很多示例中还是使用sun.misc.BASE64Decoder 工具,尽量使用最新的java.util.Base64 ,并且注意去掉头尾的"-----BEGIN ……" 行,中间的Base64编码部分的字符去掉回车换行部分。或者需要改用java.util.Base64#getMimeDecoder进行解码。

公钥的格式都是X.509格式的,但私钥默认存储的格式为PKCS#1,可以借助Bouncy Castle转化PCS#1格式为PKCS#8格式,从而获取私钥。

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.70</version>
</dependency>
// 使用PCSK#1格式的私钥
RSAPrivateKeyStructure asn1PrivKey = new RSAPrivateKeyStructure((ASN1Sequence) ASN1Sequence.fromByteArray(priKeyData));
RSAPrivateKeySpec rsaPrivKeySpec = new RSAPrivateKeySpec(asn1PrivKey.getModulus(), asn1PrivKey.getPrivateExponent());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey priKey = keyFactory.generatePrivate(rsaPrivKeySpec);

Javascript部分

目前多数使用的组件是 jsencrypt ,注意的是改组件对私钥的格式PKCS#1和PKCS#8都支持,本示例的私钥是PKCS#1的,与Java部分的示例为同一对公死钥,可以互相加解密。

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>test</title>
	<script src="https://cdn.bootcdn.net/ajax/libs/jsencrypt/3.2.1/jsencrypt.js"></script>
	<script type="text/javascript">
		var encrypt = new JSEncrypt();
		var pubKey = `
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDPkROPVlPVf455oM90p4YtrKp8
F8i5qb 4EP1S0OuhYHScPjjYv/612NGO7cjlux b4K4TOgQZosYQXB6BmicLDi h
tJyLPQffrBjCUKrqTzrxpedRv0r3ebQRU/qX/HmsI4QQrdilwuf/SUrYL6yq4s l
D/RsX/VwgwJinR7vQQIDAQAB
-----END PUBLIC KEY-----`
		var priKey = `
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDPkROPVlPVf455oM90p4YtrKp8F8i5qb 4EP1S0OuhYHScPjjY
v/612NGO7cjlux b4K4TOgQZosYQXB6BmicLDi htJyLPQffrBjCUKrqTzrxpedR
v0r3ebQRU/qX/HmsI4QQrdilwuf/SUrYL6yq4s lD/RsX/VwgwJinR7vQQIDAQAB
AoGAYkfIIit3AHUoSfDZqduUjdXe12kPktGMRKROvkLL/xpDvKM9mWu3N/VUrD9s
y1IB0nXdFp92x4iqDZ8Als8dwqShMVKEMV3AJlZGTpLGLBAzepm/XHQxRxsbTS7L
gNB0hNFZL2cFUgFOzulub9JgLduRInh8sGal2qUv1GHJkl0CQQD0za6SHPiK/dLp
YcyVA4VweIqetDKvMlPNwxp4OwPQnTcGv1Oay5nVzz8woP1 Ijb8OEA7ac UIkCl
SOT3CSt/AkEA2Q9nCe8zxghVShr3JKBqgG9Nv7LoyGQdS6VkwvoMZ4u7FITHoepQ
jRp26yNdG67VHmBu7EIjh4XLBSFJCNxFPwJAaog3uA4zewkKh9Wvmgl2QIO0j2wL
woI8Rgbptfi4qEhCcgVA9NQ1lf4imZ71sbZiB1DTYweg9e4SA9u9CGRNMQJAJBgt
mDdQsrOcCzCkvFB4pVbc5O3JS9lqDbQ5JzsRaP6RY34Y/FHMh0pJUByXVQ0jn2OF
zn/JDLYw5m98h56pXQJBAIwtxR2v/N7rkYB/EDJN4UFkgi3CP2 3HHRNcVFO0ifT
0pDMes4XtJ/qTyB7Gj0nc5NE5NN8PqOsg3yHyB rXeA=
-----END RSA PRIVATE KEY-----`
			// 加密
			encrypt.setPublicKey(pubKey)
			s = encrypt.encrypt('1231231312')
			console.log( s)
			// 解密
			encrypt.setPrivateKey(priKey)
			console.log(encrypt.decrypt(s))
	</script>
</head>
<body>
</body>
</html>
学新通

在前端的工程中可能会使用npm安装

npm install jsencrypt 

使用时

import JSEncrypt from 'jsencrypt'

// 加密
function encrypt(pubKey, srcText) {
  let encryptor = new JSEncrypt() // 新建JSEncrypt对象
	encryptor.setPublicKey(publicKey) // 设置公钥
	// 对需要加密的数据进行加密
	return encryptor.encrypt(srcText)
}

// 解密
function decrypt(priKey, cryptoText){
  let decrypt = new JSEncrypt();
	// 设置私钥
	decrypt.setPrivateKey(privateKey);
	return decrypt.decrypt(cryptoText);
}

学新通

补充知识

.PEM:用ASCLL(BASE64)编码的证书;

PEM格式通常用于数字证书认证机构(Certificate Authorities,CA),扩展名为**.pem, .crt, .cer, and .key**。内容为Base64编码的ASCII码文件,有类似"-----BEGIN CERTIFICATE-----" 和 "-----END CERTIFICATE-----"的头尾标记。服务器认证证书,中级认证证书和私钥都可以储存为PEM格式(认证证书其实就是公钥)。Apache和类似的服务器使用PEM格式证书。

.DER

DER格式与PEM不同之处在于其使用二进制而不是Base64编码的ASCII。扩展名为**.der**,但也经常使用**.cer**用作扩展名,所有类型的认证证书和私钥都可以存储为DER格式。Java使其典型使用平台。

转换方式

可以使用OpenSSL命令行工具在不同证书格式之间的转换

PEM to DER

 openssl x509 -outform der -in certificate.pem -out certificate.der

DER to PEM

  openssl x509 -inform der -in certificate.cer -out certificate.pem

.CER:存放公钥,没有私钥;

.CER文件就是一个X.509证书,可以直接查看

openssl x509 -in test.cer -text

有些情况下,不支持直接查看.CER文件,需要先将其转换为.PEM文件

openssl x509 -in test.cer -out test.pem
openssl x509 -in test.pem -text

.PFX:存放公钥和私钥

.PFX文件是一个PKCS#12格式的文件,不支持直接 查看,需要先将其转换为.PEM格式的文件

openssl x509 -in test.pfx -out test.pem -nodes
openssl x509 -in test.pem -text

PEM to PFX

  openssl pkcs12 -export -out certificate.pfx -inkey privateKey.key -in certificate.crt -certfile CACert.crt

(pem 后缀的证书都是base64编码;der 后缀的证书都是二进制格式;crt .cer 后缀的文件都是证书文件(编码方式不一定,有可能是.pem,也有可能是.der);.pfx 主要用于windows平台,浏览器可以使用,也是包含证书和私钥,获取私钥需要密码才可以)

PFX to PEM

  openssl pkcs12 -in certificate.pfx -out certificate.cer -nodes

(PFX转PEM后certificate.cer文件包含认证证书和私钥,需要把它们分开存储才能使用。)

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgchjbc
系列文章
更多 icon
同类精品
更多 icon
继续加载