目录
前言
最近在工作中需要对接一些项目的接口。这些接口对数据的保密性要求比较高。经常会用到一些加密算法。
刚开始非常头疼,不知道这些的方法的用法。因为一些原因,项目的文档也很简陋与过时。demo中也没有注释。只能只能自己慢慢摸索。
最后发现了解并掌握这些知识还是很有用的。毕竟不能在同一个问题上跌倒两次,所以专门对网上的资料做一下整理并实践。
一 基本概念
先阅读:java加密算法入门(一)-算法概念及单向加密
本文小结:
- 先
区分
什么是编码、什么摘要、什么是加密。 - BASE64是编码规则,可以解码出明文。主要为了
传递安全
- MD5、SHA以及HMAC
是摘要
,任何数据加密后只会产生唯一的一个加密串,通常用来校验数据在传 输过程中是否被修改
。 - 其中HMAC算法
有一个密钥(其他几种不需要密钥)
,增强了数据传输过程中的安全性,强化了算法外的不可控因素。 - 摘要并
不是无法破解
,但比较难
二 对称加密
参考:java加密算法入门(二)-对称加密详解
本文小结:
- DES是最基本的算法,出身比较好美国军方通过IBM来实现的(
可以被破解,不推荐
) - 3DES由于DES有漏洞不安全,所以,产生了3DES,应用更加的广泛,不过
效率不高
,所以才有了后来的AES - AES是应用更加
广泛的
一种对称加密算法,安全性更高 - PBE本质是其他对称加密算法一种综合,加入了盐(随机字符串)防止密码的暴力破解
- 上面的加密过程中,
密钥的使用准备工作往往有两步
.先是由KeyGenerator产生密钥,然后将密钥转化
成不同加密算法需要的格式
对称加密双方都有同样的密钥
。密钥本身是字节数组
,可以转成base64、hash等形式保存成文本
PBE的概念不太清楚,继续参照:
对称加密算法-PBE算法
三 非对称加密
加解密过程分别
由公钥和私钥来进行.实际场景中往往由是双向加密,既有两组非对称加密
参考:java加密算法入门(三)-非对称加密详解
本文总结
DH算法
过程就是:
- 发送发产生
密钥对
,并把公钥发给接受方 - 接收方
根据发送方的公钥
,产生自己的密钥对;并把自己的公钥发给发送方。 - 两边根据同一种算法将对方的公钥和自己的私钥产生一个本地密钥(是
对称加密
算法的密钥,两者产生的本地密钥相同
),然后使用对称加密算法进行加解密
小结
DH算法本质还是用了同一个密钥对数据进行加密,在加解密的过程中并没有使用新的算法
RSA算法
既能用于数据加密也能用于数字签名的算法,这里出现了一个新的概念数字签名
,支持公钥加密、私钥解密.私钥加密、公钥解密
。
数字签名可以对传输过来的数据进行校验
。确保数据在传输工程中不被修改。
使用过程
- 甲乙双方都有自己的密钥对,分别保存自己的密钥,然后将公钥发布。
- 双方在传输数据前,分别用对方的
公钥加密
。这样就只有对方
才可以用私钥解密
查看信息。 - 但上面过程
有个问题
?公钥是公开的,如果用人篡改
了数据,然后用重新公钥加密的数据来代替。那么就很危险 - 这时数字签名的作用就出来。发送方用自己的私钥对发送数据进行
签名(数据摘要)
,接收方需要用发送方的公钥对签名进行解密,最后对数据进行签名比对
- 这时候问题又来了?
在网络环境下(你要是人手动获取密钥,比如u盘拷贝,肯定没这问题啊
),双方如何保证自己手上的公钥就是对方的呢
?这个就是需要CA来进行验证
,这也是HTTPS的原理.有点扯远了。
ElGamal算法
只提供公钥加密,私钥解密,不提供私钥加密,公钥解密
四 证书的简单使用
参考自:1
因原文没有目录,看起来不太哦方便,所以自己整理了一下。我只看了原文的小部分,并没有把原文的全文内容整理出来
证书的制作
生成keyStroe文件
D:\idea201924workspace\encryption\src\main\resources>keytool -genkey -validity 36000 -alias www.zhuyc.com -keyalg RSA -keystore .\zhuyc.keystore
输入密钥库口令: #我输入:koulin
密钥库口令太短 - 至少必须为 6 个字符
输入密钥库口令:
再次输入新口令:
它们不匹配。请重试
输入密钥库口令:
再次输入新口令:
您的名字与姓氏是什么?
[Unknown]: zhuyc
您的组织单位名称是什么?
[Unknown]: zhuyc
您的组织名称是什么?
[Unknown]: zhuyc
您所在的城市或区域名称是什么?
[Unknown]: hz
您所在的省/市/自治区名称是什么?
[Unknown]: zj
该单位的双字母国家/地区代码是什么?
[Unknown]: cn
CN=zhuyc, OU=zhuyc, O=zhuyc, L=hz, ST=zj, C=cn是否正确?
[否]: y
输入 <www.zhuyc.com> 的密钥口令
(如果和密钥库口令相同, 按回车): #我先是输入password,后来运行测试代码时报错。所以建议和前面密码一致
再次输入新口令:
其中
- genkey表示生成密钥
- validity指定证书有效期,这里是36000天
- alias指定别名,这里是www.zlex.org
- keyalg指定算法,这里是RSA
- keystore指定存储位置,
生成自签名证书
D:\idea201924workspace\encryption\src\main\resources>keytool -export -keystore .\zhuyc.keystore -alias www.zhuyc.com -file .\zhuyc.cer -rfc
输入密钥库口令:
存储在文件 <.\zhuyc.cer> 中的证书
测试代码
Coder
package com.zyc.https;
import java.security.MessageDigest;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
/**
* 基础加密组件
*
* @author 梁栋
* @version 1.0
* @since 1.0
*/
public abstract class Coder {
public static final String KEY_SHA = "SHA";
public static final String KEY_MD5 = "MD5";
/**
* MAC算法可选以下多种算法
*
* <pre>
* HmacMD5
* HmacSHA1
* HmacSHA256
* HmacSHA384
* HmacSHA512
* </pre>
*/
public static final String KEY_MAC = "HmacMD5";
/**
* BASE64解密
*
* @param key
* @return
* @throws Exception
*/
public static byte[] decryptBASE64(String key) throws Exception {
return (new BASE64Decoder()).decodeBuffer(key);
}
/**
* BASE64加密
*
* @param key
* @return
* @throws Exception
*/
public static String encryptBASE64(byte[] key) throws Exception {
return (new BASE64Encoder()).encodeBuffer(key);
}
/**
* MD5加密
*
* @param data
* @return
* @throws Exception
*/
public static byte[] encryptMD5(byte[] data) throws Exception {
MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);
md5.update(data);
return md5.digest();
}
/**
* SHA加密
*
* @param data
* @return
* @throws Exception
*/
public static byte[] encryptSHA(byte[] data) throws Exception {
MessageDigest sha = MessageDigest.getInstance(KEY_SHA);
sha.update(data);
return sha.digest();
}
/**
* 初始化HMAC密钥
*
* @return
* @throws Exception
*/
public static String initMacKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_MAC);
SecretKey secretKey = keyGenerator.generateKey();
return encryptBASE64(secretKey.getEncoded());
}
/**
* HMAC加密
*
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] encryptHMAC(byte[] data, String key) throws Exception {
SecretKey secretKey = new SecretKeySpec(decryptBASE64(key), KEY_MAC);
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);
return mac.doFinal(data);
}
}
CertificateCoder
package com.zyc.https;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Date;
import javax.crypto.Cipher;
/**
* 证书组件
*
* @author 梁栋
* @version 1.0
* @since 1.0
*/
public abstract class CertificateCoder extends Coder {
/**
* Java密钥库(Java Key Store,JKS)KEY_STORE
*/
public static final String KEY_STORE = "JKS";
public static final String X509 = "X.509";
/**
* 由KeyStore获得私钥
*
* @param keyStorePath
* @param alias
* @param password
* @return
* @throws Exception
*/
private static PrivateKey getPrivateKey(String keyStorePath, String alias,
String password) throws Exception {
KeyStore ks = getKeyStore(keyStorePath, password);
PrivateKey key = (PrivateKey) ks.getKey(alias, password.toCharArray());
return key;
}
/**
* 由Certificate获得公钥
*
* @param certificatePath
* @return
* @throws Exception
*/
private static PublicKey getPublicKey(String certificatePath)
throws Exception {
Certificate certificate = getCertificate(certificatePath);
PublicKey key = certificate.getPublicKey();
return key;
}
/**
* 获得Certificate
*
* @param certificatePath
* @return
* @throws Exception
*/
private static Certificate getCertificate(String certificatePath)
throws Exception {
CertificateFactory certificateFactory = CertificateFactory
.getInstance(X509);
FileInputStream in = new FileInputStream(certificatePath);
Certificate certificate = certificateFactory.generateCertificate(in);
in.close();
return certificate;
}
/**
* 获得Certificate
*
* @param keyStorePath
* @param alias
* @param password
* @return
* @throws Exception
*/
private static Certificate getCertificate(String keyStorePath,
String alias, String password) throws Exception {
KeyStore ks = getKeyStore(keyStorePath, password);
Certificate certificate = ks.getCertificate(alias);
return certificate;
}
/**
* 获得KeyStore
*
* @param keyStorePath
* @param password
* @return
* @throws Exception
*/
private static KeyStore getKeyStore(String keyStorePath, String password)
throws Exception {
FileInputStream is = new FileInputStream(keyStorePath);
KeyStore ks = KeyStore.getInstance(KEY_STORE);
ks.load(is, password.toCharArray());
is.close();
return ks;
}
/**
* 私钥加密
*
* @param data
* @param keyStorePath
* @param alias
* @param password
* @return
* @throws Exception
*/
public static byte[] encryptByPrivateKey(byte[] data, String keyStorePath,
String alias, String password) throws Exception {
// 取得私钥
PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);
// 对数据加密
Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 私钥解密
*
* @param data
* @param keyStorePath
* @param alias
* @param password
* @return
* @throws Exception
*/
public static byte[] decryptByPrivateKey(byte[] data, String keyStorePath,
String alias, String password) throws Exception {
// 取得私钥
PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);
// 对数据加密
Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 公钥加密
*
* @param data
* @param certificatePath
* @return
* @throws Exception
*/
public static byte[] encryptByPublicKey(byte[] data, String certificatePath)
throws Exception {
// 取得公钥
PublicKey publicKey = getPublicKey(certificatePath);
// 对数据加密
Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 公钥解密
*
* @param data
* @param certificatePath
* @return
* @throws Exception
*/
public static byte[] decryptByPublicKey(byte[] data, String certificatePath)
throws Exception {
// 取得公钥
PublicKey publicKey = getPublicKey(certificatePath);
// 对数据加密
Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return cipher.doFinal(data);
}
/**
* 验证Certificate
*
* @param certificatePath
* @return
*/
public static boolean verifyCertificate(String certificatePath) {
return verifyCertificate(new Date(), certificatePath);
}
/**
* 验证Certificate是否过期或无效
*
* @param date
* @param certificatePath
* @return
*/
public static boolean verifyCertificate(Date date, String certificatePath) {
boolean status = true;
try {
// 取得证书
Certificate certificate = getCertificate(certificatePath);
// 验证证书是否过期或无效
status = verifyCertificate(date, certificate);
} catch (Exception e) {
status = false;
}
return status;
}
/**
* 验证证书是否过期或无效
*
* @param date
* @param certificate
* @return
*/
private static boolean verifyCertificate(Date date, Certificate certificate) {
boolean status = true;
try {
X509Certificate x509Certificate = (X509Certificate) certificate;
x509Certificate.checkValidity(date);
} catch (Exception e) {
status = false;
}
return status;
}
/**
* 签名
*
* @param keyStorePath
* @param alias
* @param password
*
* @return
* @throws Exception
*/
public static String sign(byte[] sign, String keyStorePath, String alias,
String password) throws Exception {
// 获得证书
X509Certificate x509Certificate = (X509Certificate) getCertificate(
keyStorePath, alias, password);
// 获取私钥
KeyStore ks = getKeyStore(keyStorePath, password);
// 取得私钥
PrivateKey privateKey = (PrivateKey) ks.getKey(alias, password
.toCharArray());
// 构建签名
Signature signature = Signature.getInstance(x509Certificate
.getSigAlgName());
signature.initSign(privateKey);
signature.update(sign);
return encryptBASE64(signature.sign());
}
/**
* 验证签名
*
* @param data
* @param sign
* @param certificatePath
* @return
* @throws Exception
*/
public static boolean verify(byte[] data, String sign,
String certificatePath) throws Exception {
// 获得证书
X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath);
// 获得公钥
PublicKey publicKey = x509Certificate.getPublicKey();
// 构建签名
Signature signature = Signature.getInstance(x509Certificate
.getSigAlgName());
signature.initVerify(publicKey);
signature.update(data);
return signature.verify(decryptBASE64(sign));
}
/**
* 验证Certificate
*
* @param keyStorePath
* @param alias
* @param password
* @return
*/
public static boolean verifyCertificate(Date date, String keyStorePath,
String alias, String password) {
boolean status = true;
try {
Certificate certificate = getCertificate(keyStorePath, alias,
password);
status = verifyCertificate(date, certificate);
} catch (Exception e) {
status = false;
}
return status;
}
/**
* 验证Certificate
*
* @param keyStorePath
* @param alias
* @param password
* @return
*/
public static boolean verifyCertificate(String keyStorePath, String alias,
String password) {
return verifyCertificate(new Date(), keyStorePath, alias, password);
}
}
测试类
package com.zyc.https;
import static org.junit.Assert.*;
import org.junit.Test;
/**
*
* @author 梁栋
* @version 1.0
* @since 1.0
*/
public class CertificateCoderTest {
private String password = "koulin";
private String alias = "www.zhuyc.com";
private String certificatePath = "D:\\idea201924workspace\\encryption\\src\\main\\resources\\zhuyc.cer";
private String keyStorePath = "D:\\idea201924workspace\\encryption\\src\\main\\resources\\zhuyc.keystore";
@Test
public void test() throws Exception {
System.err.println("公钥加密——私钥解密");
String inputStr = "Ceritifcate";
byte[] data = inputStr.getBytes();
byte[] encrypt = CertificateCoder.encryptByPublicKey(data,
certificatePath);
byte[] decrypt = CertificateCoder.decryptByPrivateKey(encrypt,
keyStorePath, alias, password);
String outputStr = new String(decrypt);
System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);
// 验证数据一致
assertArrayEquals(data, decrypt);
// 验证证书有效
assertTrue(CertificateCoder.verifyCertificate(certificatePath));
}
@Test
public void testSign() throws Exception {
System.err.println("私钥加密——公钥解密");
String inputStr = "sign";
byte[] data = inputStr.getBytes();
byte[] encodedData = CertificateCoder.encryptByPrivateKey(data,
keyStorePath, alias, password);
byte[] decodedData = CertificateCoder.decryptByPublicKey(encodedData,
certificatePath);
String outputStr = new String(decodedData);
System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);
assertEquals(inputStr, outputStr);
System.err.println("私钥签名——公钥验证签名");
// 产生签名
String sign = CertificateCoder.sign(encodedData, keyStorePath, alias,
password);
System.err.println("签名:\r" + sign);
// 验证签名
boolean status = CertificateCoder.verify(encodedData, sign,
certificatePath);
System.err.println("状态:\r" + status);
assertTrue(status);
}
}