非对称加密
整理一下关于加密的用法,方便以后查看。
开发平台算法名称 | 标准签名算法名称 | 备注 |
---|---|---|
RSA2 | SHA256WithRSA | (强烈推荐使用),强制要求RSA密钥的长度至少为2048 |
RSA | SHA1WithRSA | 对RSA密钥的长度不限制,推荐使用2048位以上 |
-
什么是非对称加密?:「非对称加密算法」需要两个密钥:公钥(publickey)和私钥(privatekey)。公钥与私钥是一对,如果用公钥对数据进行加密,只有用对应的私钥才能解密;如果用私钥对数据进行加密,那么只有用对应的公钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。RSA是目前最有影响力的公钥加密算法。。RSA算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。
- 加密与签名:
加密与签名虽然都会使用RSA的一些算法,但是这两个不能混为一谈,是不一样的(主要体现在用途不一样)。加密的作用是为了防止明文内容被人看到,验签是为了防止明文内容被人篡改。 -
公钥和私钥都能用来加密和解密,通常情况下使用公钥加密,私钥解密。私钥和公钥是成对匹配的,完全匹配的时候才能正确的解密出明文,否则不能获取到正确的密文,所以私钥公钥都能用来加密和解密。不过大多数情况下都是用公钥加密然后用私钥解密,这样能保证能保证明文只能被私钥持有者获取到了。反过来私钥加密公钥公钥解密的话,那么所有持有公钥的人都能获取明文了。公钥和私钥是可以相互推导的,根据私钥,你可以很容易的算出公钥,但是根据公钥,你却很难很难算出私钥,在互联网上,私钥一般由服务器掌握,公钥则由客户端使用。根据公钥,理论上需要花费地球上所有的计算机计算数万年才能算出私钥,所以认为是非常安全的。
- openssl:SSL的全名叫做secure socket layer(安全套接字层),最开始是由一家叫网景的互联网公司开发出来,主要是防止信息在互联网上传输的时候不被窃听或者篡改,后来网景公司提交SSL给ISOC组织做标准化,改名为TLS。openssl 是目前最流行的 SSL 密码库工具,其提供了一个通用、健壮、功能完备的工具套件,用以支持SSL/TLS 协议的实现。默认情况下,openssl 输出格式为 PKCS#1-PEM
- 生成公钥,私钥:安装OpenSSL
openssl
# 生成私钥
genrsa -out id_rsa_private 2048
# 生成私钥(把RSA私钥转换成PKCS8格式)
pkcs8 -topk8 -inform PEM -in id_rsa_private -outform pem -nocrypt -out id_rsa_private_pkcs
# 生成公钥
rsa -in id_rsa_private -pubout -out id_rsa_public.pub
DER 和 PEM 编码
DER: 基于二进制的编码。可以用CER或者CRT作为扩展名的的整数。比较合适的说法是“我有一个DER编码的证书”,而不是“我有一个DER证书”。【编码规则参考】
PEM: 基于ASCII(Base64)的编码。OpenSSL 使用 PEM 文件格式存储证书和密钥。【编码规则参考】
PEM 实质上是 base64 编码的二进制内容,再进行增加或裁剪特殊字符-、n、r、begin信息、end信息等,如
-----BEGIN CERTIFICATE-----
内容
-----END CERTIFICATE-----
PKCS(The Public-Key Cryptography Standards) 由美国RSA公司及其合作伙伴制定的一组公钥密码学标准,其中包括证书申请、证书更新、证书作废表发布、扩展证书内容以及数字签名、数字信封的格式等方面的一系列相关协议。版本:现有PCKS1,PCKS3,PCKS5,PKCS6,PKCS7,PKCS8,PKCS9,PKCS10,PKCS11,PKCS12,PKCS13,PKCS14,PKCS15 等 13个版本
常用:PCKS1 和 PCKS8,本文使用 PCKS8 标准
注:由于PKCS内容规范设计二进制,不方便使用,所以一般转成base64 后,用 pem 格式规范显示
- pcks1的pem格式头
//公钥
-----BEGIN RSA PUBLIC KEY-----
MEgCQQCpb53RwojJX+lxHiEMfNmixx+eIV+u+d9e61Ecx8MVBW0tzAoxTpdTKrgN
9hdBqaP6rtQMxOp/4++C5+3zh6D1AgMBAAE=
-----END RSA PUBLIC KEY-----
//私钥
-----BEGIN RSA PRIVATE KEY-----
MIIBPAIBAAJBAKlvndHCiMlf6XEeIQx82aLHH54hX675317rURzHwxUFbS3MCjFO
l1MquA32F0Gpo/qu1AzE6n/j74Ln7fOHoPUCAwEAAQJAJO8tyeHMC4may4uzzJMS
pgcd/0xrHSte48QMBGgPQG95imYXELKQUV2rjzsua9xJP5huQjhe+qrmBpEqrtf7
wQIhANccK0zuFaH4CoIuKsp92YB6OGwndy6UJaiX2RoUw0V5AiEAyaTTkar73r4d
OB2s4Ofj+UkGX/aspp0dM+7V5HathF0CIQCezlYNSmvAEr23U9wVeAmd9x02g4BS
a97Nc6U8wv1SiQIhAKap7ZTA1l1MlaoEHRfnkq5AhVxb7mfoBHMgPPoQfWqhAiEA
npkku9LfP2mlCCB85/zxsUehZOwoMVLlF2dSLWsrURs=
-----END RSA PRIVATE KEY-----
- PCKS8 的pem格式头
//公钥
-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKzY3F21g7UWaTcIk/ltUy4LuuHK5Hhl
9ZpdxDr4+WA7pNwhCzhgBKbWUa+XotUYet841I2mYPJ6+6X32LwotF0CAwEAAQ==
-----END PUBLIC KEY-----
//私钥
-----BEGIN PRIVATE KEY-----
MIIBVgIBADANBgkqhkiG9w0BAQEFAASCAUAwggE8AgEAAkEArNjcXbWDtRZpNwiT
+W1TLgu64crkeGX1ml3EOvj5YDuk3CELOGAEptZRr5ei1Rh63zjUjaZg8nr7pffY
vCi0XQIDAQABAkBUJ3GDHnD1peesJ70D37pz3LosXMBH7jxZ3/U+USH5O8M7ixzt
lpYa+ITcuL0MKxvVyVYw5Pvaui1+Nq9LV17xAiEA5VMCIgtn3WFEOpP+sVhHlqPc
VbTfE3g0RlDfUt/sQ5sCIQDA9AiJ65G3pHJJxYNc9RB/lVav3anFkbgnGKolPw8T
ZwIhAIs7QLpGzoLcgT5HiIVIIf7V8fqfXClH/AUNKOn6RkotAiEAo6HQY/ZMeyqQ
aZOA1aJWPXxOKZKX2R68xxsZn8Ccbl0CIQDck74y8SzLRSBB6JgVZ6NoWkWI5tyj
V8RFCZU9VUinQA==
-----END PRIVATE KEY-----
net,ios(php)中rsa加解密使用的是pkcs1,而java使用的是pkcs8
如果是按1024取模(通常都是1024),pkcs1格式的私钥长度应该是812。如果是pkcs8的格式的密钥长度为861。
示例代码:
- php类文件
<?php
/**
* Class Rsa加密解密类
*/
/*使用示例
$rsa = new Rsa();
$rsa->privateKey = '私钥字符串';
$rsa->encodeByPrivateKey('要加密的字符串');
$rsa->publicKey = '公钥字符串';
$rsa->decodeByPublicKey('要解密的字符串');
*/
class Rsa
{
public $publicKey; // 公钥内容,不要头尾和换行符,只要内容
public $privateKey; // 私钥内容,不要头尾和换行符,只要内容
/**
* 获取完整的私钥
* @return bool|resource
*/
public function getPrivateKey()
{
$pem = "-----BEGIN RSA PRIVATE KEY-----" . PHP_EOL;
$pem .= chunk_split($this->privateKey, 64, PHP_EOL);
$pem .= "-----END RSA PRIVATE KEY-----" . PHP_EOL;
return openssl_pkey_get_private($pem);
}
/**
* 获取完整的公钥
* @return bool|resource
*/
public function getPublicKey()
{
$pem = "-----BEGIN PUBLIC KEY-----" . PHP_EOL;
$pem .= chunk_split($this->publicKey, 64, PHP_EOL);
$pem .= "-----END PUBLIC KEY-----" . PHP_EOL;
return openssl_pkey_get_public($pem);
}
/**
* 私钥加密
* @param string $data 要加密的数据
* @return mixed
*/
public function encodeByPrivateKey($data)
{
$crypto = '';
$length = $this->_getKeyLength() / 8 - 11;
foreach (str_split($data, $length) as $chunk) {
openssl_private_encrypt($chunk, $encrypted, $this->getPrivateKey());
$crypto .= $encrypted;
}
return $this->_base64Encode($crypto);
}
/**
* 公钥解密
* @param string $data 要解密的字符串
* @return mixed
*/
public function decodeByPublicKey($data)
{
$crypto = '';
$length = $this->_getKeyLength() / 8;
foreach (str_split($this->_base64Decode($data), $length) as $chunk) {
openssl_public_decrypt($chunk, $decrypted, $this->getPublicKey());
$crypto .= $decrypted;
}
return $crypto;
}
/**
* 私钥解密
* @param string $data 要解密的字符串
* @return mixed
*/
public function decodeByPrivateKey($data)
{
$crypto = '';
$length = $this->_getKeyLength() / 8;
foreach (str_split($this->_base64Decode($data), $length) as $chunk) {
openssl_private_decrypt($chunk, $decrypted, $this->getPrivateKey());
$crypto .= $decrypted;
}
return $crypto;
}
/**
* 获取密钥长度
* @return mixed
*/
private function _getKeyLength()
{
return openssl_pkey_get_details($this->getPublicKey())['bits'];
}
/**
* @param string $value 待加密字符串
* @return mixed
*/
private function _base64Encode($value) {
$data = base64_encode($value);
return str_replace(['+', '/', '='], ['-', '_', ''], $data);
}
/**
* @param string $value 待解密字符串
* @return bool|string
*/
private function _base64Decode($value) {
$data = str_replace(['-', '_'], ['+', '/'], $value);
if ($mod4 = strlen($data) % 4) {
$data .= substr('====', $mod4);
}
return base64_decode($data);
}
}
- 示例
$rsa1 = new Rsahelper();
$rsa1->publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCLUWG...";
$rsa1->privateKey = "MIICWwIBAAKBgQCLUWGcpISEDgAoTGR5kK/J...";
//$res1->privateKey = "MIICdQIBADANBg...";
$pu_key = $rsa1->getPublicKey();
$pi_key = $rsa1->getPrivateKey();
//print_r($pi_key);die;
$mi = $rsa1->encodeByPrivateKey($mi);
//dump($mi);die;
$rmi = $rsa1->decodeByPublicKey($mi);
java rsa加密
类文件
package com.localhost;
import org.apache.shiro.codec.Base64;
import java.io.ByteArrayOutputStream;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
/**
* RSA工具类
* Class RSAUtils
* @author dbn
*/
class RSAUtils {
/**
* 签名算法
*/
private static final String KEY_ALGORITHM = "RSA";
private static final String SIGNATURE_ALGORITHM = "MD5withRSA";
/**
* 获取公钥的key
*/
private static final String PUBLIC_KEY = "RSAPublicKey";
/**
* 获取私钥的key
*/
private static final String PRIVATE_KEY = "RSAPrivateKey";
/**
* RSA最大加密明文大小
*/
private static final int MAX_ENCRYPT_BLOCK = 117;
/**
* RSA最大解密密文大小
*/
private static final int MAX_DECRYPT_BLOCK = 128;
/**
* 数据编码
*/
private static final String ENCODING = "UTF-8";
/**
* 生成密钥对(公钥和私钥)
* @return 密钥对 Map 对象
* @throws Exception
*/
static Map<String, Object> resetGenKeyPair() throws Exception {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
keyPairGen.initialize(1024);
KeyPair keyPair = keyPairGen.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
Map<String, Object> keyMap = new HashMap<String, Object>(2);
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 获取公钥
* @param keyMap 密钥对 Map 对象
* @return 公钥
* @throws Exception
*/
static String getPublicKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return Base64.encodeToString(key.getEncoded());
}
/**
* 获取私钥
* @param keyMap 密钥对 Map 对象
* @return 私钥
* @throws Exception
*/
static String getPrivateKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return Base64.encodeToString(key.getEncoded());
}
/**
* 私钥加签
* @param data 原始数据
* @param privateKey 私钥
* @return 加密数据
* @throws Exception
*/
static String sign(String data, String privateKey) throws Exception {
byte[] keyBytes = Base64.decode(privateKey);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initSign(privateK);
signature.update(data.getBytes(ENCODING));
return Base64.encodeToString(signature.sign());
}
/**
* 公钥验签
* @param data 原始数据
* @param publicKey 公钥
* @param sign 数据签名
* @return 验签结果
* @throws Exception
*/
static boolean verifySign(String data, String publicKey, String sign) throws Exception {
try {
byte[] keyBytes = Base64.decode(publicKey);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PublicKey publicK = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initVerify(publicK);
signature.update(data.getBytes(ENCODING));
return signature.verify(Base64.decode(sign));
} catch (Exception e) {
throw e;
}
}
/**
* 私钥加密
* @param dataStr 原始数据
* @param privateKey 私钥
* @return 加密数据
* @throws Exception
*/
static String encryptByPrivateKey(String dataStr, String privateKey) throws Exception {
byte[] data = dataStr.getBytes(ENCODING);
byte[] keyBytes = Base64.decode(privateKey);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateK);
int inputLen = data.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
} else {
cache = cipher.doFinal(data, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_ENCRYPT_BLOCK;
}
byte[] encryptedData = out.toByteArray();
out.close();
return Base64.encodeToString(encryptedData);
}
/**
* 公钥解密
* @param encryptedDataStr 加密数据
* @param publicKey 公钥
* @return 解密后数据
* @throws Exception
*/
static String decryptByPublicKey(String encryptedDataStr, String publicKey) throws Exception {
byte[] encryptedData = Base64.decode(encryptedDataStr);
byte[] keyBytes = Base64.decode(publicKey);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicK = keyFactory.generatePublic(x509KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicK);
int inputLen = encryptedData.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
} else {
cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_DECRYPT_BLOCK;
}
byte[] decryptedData = out.toByteArray();
out.close();
return new String(decryptedData, ENCODING);
}
/**
* 公钥加密
* @param dataStr 原始数据
* @param publicKey 公钥
* @return 加密数据
* @throws Exception
*/
static String encryptByPublicKey(String dataStr, String publicKey) throws Exception {
byte[] data = dataStr.getBytes(ENCODING);
byte[] keyBytes = Base64.decode(publicKey);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicK = keyFactory.generatePublic(x509KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicK);
int inputLen = data.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
} else {
cache = cipher.doFinal(data, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_ENCRYPT_BLOCK;
}
byte[] encryptedData = out.toByteArray();
out.close();
return Base64.encodeToString(encryptedData);
}
/**
* 私钥解密
* @param encryptedDataStr 加密数据
* @param privateKey 私钥
* @return 解密后数据
* @throws Exception
*/
static String decryptByPrivateKey(String encryptedDataStr, String privateKey) throws Exception {
byte[] encryptedData = Base64.decode(encryptedDataStr);
byte[] keyBytes = Base64.decode(privateKey);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateK);
int inputLen = encryptedData.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
} else {
cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_DECRYPT_BLOCK;
}
byte[] decryptedData = out.toByteArray();
out.close();
return new String(decryptedData, ENCODING);
}
}
示例:
package com.localhost;
import java.util.Map;
public class Test {
public static void main(String[] args) throws Exception {
// 生成新的公私钥
Map<String, Object> genKeyPair = RSAUtils.resetGenKeyPair();
// 获取公钥
//String publicKey = RSAUtils.getPublicKey(genKeyPair);
//System.out.println("------- 公钥 -------");
//System.out.println(publicKey);
// 获取私钥
//String privateKey = RSAUtils.getPrivateKey(genKeyPair);
//System.out.println("------- 私钥 -------");
//System.out.println(privateKey);
String publicKey = "MIGfMA0GCSqGSIb3DQEBA...";
String privateKey = "MIICdQIBADANBgkqhkiG9w0BAQEFAA...";
// 加密数据
//String data = "{"agencyCustSno":"test1","sno":"1234","tradeDate":"2021-04-07 15:32:31","transAmount":"100","transFee":"2","transFeeRate":"0.02","transType":"PURCHASE"}";
String data = "2";
System.out.println("------- 加密字符串 -------");
System.out.println(data);
// 私钥加签
String sign = RSAUtils.sign(data, privateKey);
// String sign = "JAkJLORVds3LC8zv7eaJ3MCWkIzEuwnrlzs7y7zNXYZ8TXRXGJVFBuJ6bhYK5lZMvbHOANCTQSUBQgDUN9Rra38YJW+MG16DxdDvRZlZ8FszySH+5hPuQNGgKkwzdDhabH7/7VhemXWq9VRpkyHxPSnn69I3vhcmba+lGbq2HM4=";
System.out.println("------- 数据签名 -------");
System.out.println(sign);
// 验证签名
boolean verifySign = RSAUtils.verifySign(data, publicKey, sign);
System.out.println("------- 验证签名 -------");
System.out.println(verifySign);
// 私钥加密
String priEncrypt = RSAUtils.encryptByPrivateKey(data, privateKey);
//String priEncrypt = "XUal_GLSl0iE5bwV5r24TNqzmsPXQDMSQTRww_d1XSf4ddqo5fAIZxnYI0roYTEqOE3Y6tcNVQP6Bsqmj23Nl4Ayt58G9KfInmxn_jV9DFRiBI17araYQZn3ZFQ3a1Ne5HtMcXXRuu9C_cx678IFlH3pNGSh8XHkJCONFKcTGhZiZZbWpeJjonLsMQXYlyJs2fKD5uRg1llKZEzT2W0gweCN1THJ2tRxGUy3YKczH-KFJJ4AbFOziKf5mZUrU9VTzP10l1rpKVS1EK7sij2ytOonVQNrLsJczyzsNZG4ylF3dMBlBHiZ4mrPv5cnl6QcC3o75aF0qNl7OeP1ywtSFA";
System.out.println("------- 私钥加密 -------");
System.out.println(priEncrypt);
// 公钥解密
String pubDecrypt = RSAUtils.decryptByPublicKey(priEncrypt, publicKey);
System.out.println("------- 公钥解密 -------");
System.out.println(pubDecrypt);
// 公钥加密
String pubEncrypt = RSAUtils.encryptByPublicKey(data, publicKey);
System.out.println("------- 公钥加密 -------");
System.out.println(pubEncrypt);
// 私钥解密
String priDecrypt = RSAUtils.decryptByPrivateKey(pubEncrypt, privateKey);
System.out.println("------- 私钥解密 -------");
System.out.println(priDecrypt);
}
}
java 与PHP 互相通信时注意私钥格式转换 与base64编码符号转换