非对称加密
- 具体算法看这位大神详解 https://blog.csdn.net/jijianshuai/article/details/80582187
- 常用用途
api接口数据传输等
- 加密解密用法
公钥暴露在网络上,私钥在本地保存,凡是能拿到公钥的人都可以加密信息,但是使用公钥加密的数据,无法再次使用公钥解开,所以一般采用公钥加密私钥解密的方式
- 签名/验签的用法
一般采用私钥进行签名,然后把签名和明文数据一起传送给拥有公钥的一方,拥有公钥的一方,可以使用公钥验证此签名是否为对应私钥签名,以防止数据篡改
代码
import javax.crypto.Cipher;
import java.io.*;
import java.security.*;
import java.security.interfaces.RSAKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
/**
* <p>
* Utils for RSA Encrypt
* <ul>
* <li>Use private key to decrypt {@link #decrypt(Key, byte[])} data;</li>
* <li>Use public key to encrypt {@link #encrypt(Key, byte[])} data;</li>
* <li>Use sign {@link #sign(byte[], PrivateKey)} to generate a signature;</li>
* <li>Use the verify {@link #verify(byte[], PublicKey, String)} to valuation if has be hijacked;</li>
* </ul>
* </p>
*
* @author Wilton Jia
* @date 2020-09-29
* @since 1.0
*/
public class RSAEncrypt {
/**
* array of byte data, for transform strings
*/
private static final char[] HEX_CHAR = { '0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
/**
* Algorithm of Encrypt
*/
private final static String KEY_RSA = "RSA";
/**
* Algorithm of Signature
*/
private final static String KEY_RSA_SIGNATURE = "MD5withRSA";
/**
* Public key name
*/
private final static String publicKeyName = "publicKey";
/**
* Private key name
*/
private final static String privateKeyName = "privateKey";
/**
* Generate a pair of random keys
* @param filePath location of key files
*/
public static Map<String, String> genKeyPair(String filePath) throws Exception {
// get KeyGenerator
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
// Init the KeyGenerator, size of key :96-1024 bit
keyPairGen.initialize(2048);
// key pair
KeyPair keyPair = keyPairGen.generateKeyPair();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
// key string
Base64.Encoder base64Encoder = Base64.getEncoder();
String publicKeyString = base64Encoder.encodeToString(publicKey.getEncoded());
String privateKeyString = base64Encoder.encodeToString(privateKey.getEncoded());
String uuid = UUIDGenerator.generateWithout_();
// Result for file path
Map<String, String> pairs = new HashMap<>(2);
String publicKeyFilePath = filePath + File.separator + uuid+ "_publicKey.pem";
String privateKeyFilePath = filePath + File.separator + uuid+ "_privateKey.pem";
pairs.put(publicKeyName,publicKeyFilePath);
pairs.put(privateKeyName,privateKeyFilePath);
// Write files to local
FileWriter publicKeyWriter = new FileWriter(publicKeyFilePath);
FileWriter privateKeyWriter = new FileWriter(privateKeyFilePath);
BufferedWriter publicKeyBufferWriter = new BufferedWriter(publicKeyWriter);
BufferedWriter privateKeyBufferWriter = new BufferedWriter(privateKeyWriter);
//Close streams
publicKeyBufferWriter.write(publicKeyString);
privateKeyBufferWriter.write(privateKeyString);
publicKeyBufferWriter.flush();
publicKeyBufferWriter.close();
publicKeyWriter.close();
privateKeyBufferWriter.flush();
privateKeyBufferWriter.close();
privateKeyWriter.close();
return pairs;
}
/**
* Load a key from local file, first step is get the file byte array,
* then transform the byte array to a RSAKey,
* @param path the pem file path or
* @param type Implements of RSAKey, RSAPublicKey or RSAPrivateKey
* @throws IOException
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
* @return {@code RSAPublicKey, RSAPrivateKey}
*/
public static RSAKey loadKeyFromFile(String path, Class<? extends RSAKey> type) throws IOException,
NoSuchAlgorithmException, InvalidKeySpecException {
//load the key string from file
File file = new File(path);
FileInputStream fileInputStream = new FileInputStream(path);
DataInputStream dis = new DataInputStream(fileInputStream);
byte[] keyBytes = new byte[(int)file.length()];
dis.readFully(keyBytes);
dis.close();
fileInputStream.close();
// parse the key string to a key
KeyFactory keyFactory = KeyFactory.getInstance(KEY_RSA);
EncodedKeySpec keySpec = null;
// while need a public key
keyBytes = Base64.getDecoder().decode(keyBytes);
if(type.getName().equals(RSAPublicKey.class.getName())){
keySpec = new X509EncodedKeySpec(keyBytes);
return (RSAPublicKey)keyFactory.generatePublic(keySpec);
}else if(type.getName().equals(RSAPrivateKey.class.getName())){
keySpec = new PKCS8EncodedKeySpec(keyBytes);
return (RSAPrivateKey)keyFactory.generatePrivate(keySpec);
}
return null;
}
/**
* Use the key to encrypt some data
* @param key publicKey or privateKey
* @param plainTextData clear data array
* @return encrypted data bytes
* @throws Exception
*/
public static byte[] encrypt(Key key, byte[] plainTextData) throws Exception {
if (key == null) {
throw new Exception("key could not be null");
}
Cipher cipher = Cipher.getInstance(KEY_RSA);
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(plainTextData);
}
/**
* Use key to decrypt data
* @param key public key or private key
* @param cipherData encrypt data
* @return clear data array
* @throws Exception
*/
public static byte[] decrypt(Key key, byte[] cipherData) throws Exception {
if (key == null) {
throw new Exception("private key could not be empty or null");
}
Cipher cipher = Cipher.getInstance(KEY_RSA);;
cipher.init(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(cipherData);
}
/**
* Use Private key to sign
* <p>
* could be a public key, but not suggest, cause you should write your sign by your self
* and this method provide EncodedKeySpec was a PKCS8EncodedKeySpec, if you want to use a public key to sign,
* you should change {@code PKCS8EncodedKeySpec pkcs = new PKCS8EncodedKeySpec(bytes)}
* to {@code X509EncodedKeySpec pkcs = new X509EncodedKeySpec(bytes)}
* </p>
* @param data clear data
* @param privateKey a key encoded by Base64
* @throws Exception
*/
public static String sign(byte[] data, PrivateKey privateKey) throws Exception {
// create the real Signature Object
Signature signature = Signature.getInstance(KEY_RSA_SIGNATURE);
signature.initSign(privateKey);
signature.update(data);
return encryptBase64(signature.sign());
}
/**
* Verify the Sign context
*
* @param data encrypted data
* @param publicKey a base64 key
* @param sign
* @return if success return {@code true} else return {@code false}
*/
public static boolean verify(byte[] data, PublicKey publicKey, String sign) {
boolean flag = false;
try {
// to verify
Signature signature = Signature.getInstance(KEY_RSA_SIGNATURE);
signature.initVerify(publicKey);
signature.update(data);
flag = signature.verify(decryptBase64(sign));
} catch (Exception e) {
e.printStackTrace();
}
return flag;
}
/**
* Parse a byte array to a hex String
* @param data input byte array
* @return hex String
*/
public static String byteArrayToString(byte[] data) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < data.length; i++) {
// get high four bit as a index ,then parse to hex, be careful to forget no sign left move
stringBuilder.append(HEX_CHAR[(data[i] & 0xf0) >>> 4]);
// get the lower four bit as a index ,get hex sign
stringBuilder.append(HEX_CHAR[(data[i] & 0x0f)]);
if (i < data.length - 1) {
stringBuilder.append(' ');
}
}
return stringBuilder.toString();
}
/**
* BASE64 decode
* @param key base64 string needs to decode
* @return clear data array
*/
public static byte[] decryptBase64(String key) throws Exception {
return Base64.getDecoder().decode(key);
}
/**
* BASE64 encode
* @param key data array need to encode
* @return a string
*/
public static String encryptBase64(byte[] key) throws Exception {
return Base64.getEncoder().encodeToString(key);
}
}
测试
- 生成密钥
@Test
public void test1() throws Exception {
Map<String, String> keyPair = RSAEncrypt.genKeyPair(path);
for (String s : keyPair.keySet()) {
System.out.printf("%s: %s", s, keyPair.get(s));
//4bfda50f21e540f19ab394218e62ccb7_privateKey.pem
//4bfda50f21e540f19ab394218e62ccb7_publicKey.pem
}
}
- 公钥加密/私钥解密
@Test
public void test2() throws Exception {
String publicKey = "4bfda50f21e540f19ab394218e62ccb7_publicKey.pem";
RSAPublicKey rsaPublicKey = (RSAPublicKey)RSAEncrypt.loadKeyFromFile(path + publicKey, RSAPublicKey.class);
byte[] encrypt = RSAEncrypt.encrypt(rsaPublicKey, "123456ABc".getBytes());
System.out.println(new String(Base64Utils.encode(encrypt)));
String privateKey = "4bfda50f21e540f19ab394218e62ccb7_privateKey.pem";
RSAPrivateKey key = (RSAPrivateKey)RSAEncrypt.loadKeyFromFile(path + privateKey, RSAPrivateKey.class);
byte[] decrypt = RSAEncrypt.decrypt(key, encrypt);
System.out.println(new String(decrypt));
}
- 私钥签名/公钥验签
@Test
public void test4() throws Exception {
String privateKey = "4bfda50f21e540f19ab394218e62ccb7_privateKey.pem";
RSAPrivateKey key = (RSAPrivateKey)RSAEncrypt.loadKeyFromFile(path + privateKey, RSAPrivateKey.class);
String sign = RSAEncrypt.sign("123456AbCd".getBytes(), key);
System.out.println(sign);
String publicKey = "4bfda50f21e540f19ab394218e62ccb7_publicKey.pem";
RSAPublicKey rsaPublicKey = (RSAPublicKey)RSAEncrypt.loadKeyFromFile(path + publicKey, RSAPublicKey.class);
boolean verify = RSAEncrypt.verify("123456AbCd1".getBytes(), rsaPublicKey, sign);
System.out.println(verify);
}
本人自己整理的工具类集合
geeking-common-utils
欢迎各位大神将自己的工具类贡献,省去每次找一个工具类的烦恼