一、Base64
Base64用于网络中传输的数据进行编码,严格意义上属于编码的格式,有64个字符的对应的编码,Base64就是将内容按照该格式进行编码。可以对数据编码和解码,是可逆的,安全度较低。
Base64可以使用JDk中自带的类实现,还可以使用Bouncy Castle(简称bc)或Commons Codec(简称cc)实现。
1.1 JDK自带类实现
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class Test {
public static void main(String[] args) {
// 原始数据
String str = "Hello World";
// 加密
Base64.Encoder encoder = Base64.getEncoder();
String encoderStr = encoder.encodeToString(str.getBytes("utf-8"));
System.out.println("encoder: " + encoderStr);
// 解密
Base64.Decoder decoder = Base64.getDecoder();
String decoderStr = new String(decoder.decode(encoderStr), "utf-8");
System.out.println("decoder: " + decoderStr);
}
}
二、摘要算法
摘要算法主要分为MD,SHA和Hmac算法,摘要算法其实是用于效验数据完整性的,我们在下载某些文件时,会有MD5和SHA1值提供我们效验下载的文件是否完整,可以用于根据数据生成其唯一的摘要值,无法根据摘要值知道原数据,属于不可逆的。
2.1 MD
算法 | 摘要长度 | 实现方 |
---|---|---|
MD2 | 128 | JDK |
MD5 | 128 | JDK |
JDK有MD2和MD5实现,使用的是MessageDigest类。(JDK没有MD4的实现)
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Test {
public static void main(String[] args) throws NoSuchAlgorithmException {
// 原始字符
String data = "Hello World!";
// 编码
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(data.getBytes());
// 转化为16进制编码
BigInteger bigInteger = new BigInteger(1, digest);
System.out.println("JDK MD5: " + bigInteger.toString(16));
}
}
2.2 SHA
安全摘要算法,常用的有如下几种:
算法 | 摘要长度 | 实现方 |
---|---|---|
SHA-1 | 160 | JDK |
SHA-224 | 224 | Bouncy Castle |
SHA2561 | 256 | JDK |
SHA-384 | 384 | JDK |
SHA-512 | 512 | JDK |
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Test {
public static void main(String[] args) throws NoSuchAlgorithmException {
// 原始字符
String data = "Hello World!";
// SHA-1 编码
MessageDigest md = MessageDigest.getInstance("SHA");
byte[] digest = md.digest(data.getBytes());
BigInteger bigInteger = new BigInteger(1, digest);
System.out.println("JDK MD5: " + bigInteger.toString(16));
}
}
2.3 Hmac
Hmac是一种含有密钥的摘要算法,也有简称mac,密钥不同摘要也不同。常用的有如下几种:
算法 | 摘要长度 | 实现方 |
---|---|---|
HmacMD2 | 128 | Bouncy Castle |
HmacMD4 | 128 | Bouncy Castle |
HmacMD5 | 128 | JDK |
HmacSHA1 | 160 | JDK |
HmacSHA224 | 224 | Bouncy Castle |
HmacSHA256 | 256 | JDK |
HmacSHA384 | 384 | JDK |
HmacSHA512 | 512 | JDK |
public class Test {
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
// 原始字符
String data = "Hello World!";
// // 生成秘钥,也可以自定义秘钥
// KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacMD5");
// SecretKey secretKey = keyGenerator.generateKey(); //产生密钥
// byte[] key = secretKey.getEncoded(); //获得密钥(默认生成)
byte[] key = new byte[]{'a','a','a','a','a','a','a','a','a','a'}; // 手动生成密钥(十位)
SecretKey secretKey2 = new SecretKeySpec(key, "HmacMD5"); // 还原密钥
Mac mac = Mac.getInstance(secretKey2.getAlgorithm()); // 实例化mac
mac.init(secretKey2); // 初始化mac
byte[] hmacMD5Bytes = mac.doFinal(data.getBytes()); // 生成摘要
BigInteger bigInteger = new BigInteger(1, hmacMD5Bytes);
System.out.println("jdk hmacMD5: " + bigInteger.toString(16));
}
}
三、对称加密
严格意义上的加密算法,分为对称和非对称加密算法,所谓对称是说发送方和接收方的密钥是一样的。因为密钥一样所以安全性跟非对称比较来说就不太安全了
对称加密算法主要分为:DES , 3DES(3重DES) , AES(想要替代DES) , PBE(基于口令的对称算法)
3.1 DES
密钥长度 | 默认 | 工作方式 | 填充方式 | 实现方 |
---|---|---|---|---|
56 | 56 | ECB、CBC、PCBC、CTR、CTS、CFB、CFB8到128、OFB、OFB8到128 | NoPadding、PKCS5Padding、ISO10126Padding | JDK |
64 | 56 | ECB、CBC、PCBC、CTR、CTS、CFB、CFB8到128、OFB、OFB8到128 | PKCS7Padding、ISO1012d2Padding、X932Padding、ZeroBytePadding | Bouncy Castle |
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import java.math.BigInteger;
public class Test {
public static void main(String[] args) throws Exception {
// 原始字符
String data = "Hello World!";
//生成key
KeyGenerator keyGenerator=KeyGenerator.getInstance("DES");
keyGenerator.init(56); //指定key长度,同时也是密钥长度(56位)
SecretKey secretKey = keyGenerator.generateKey(); //生成key的材料
byte[] key = secretKey.getEncoded(); //生成key
// key转换成密钥(自己指定字节数组时需要转换)
DESKeySpec desKeySpec=new DESKeySpec(key);
SecretKeyFactory factory=SecretKeyFactory.getInstance("DES");
SecretKey key2 = factory.generateSecret(desKeySpec); //转换后的密钥
// 加密
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); //算法类型/工作方式/填充方式
cipher.init(Cipher.ENCRYPT_MODE, key2); //指定为加密模式
byte[] result = cipher.doFinal(data.getBytes());
BigInteger bigInteger = new BigInteger(1, result);
System.out.println("jdkDES加密: "+ bigInteger.toString(16)); //转换为十六进制
// 解密
cipher.init(Cipher.DECRYPT_MODE, key2); //相同密钥,指定为解密模式
result = cipher.doFinal(result); //根据加密内容解密
System.out.println("jdkDES解密: " + new String(result)); //转换字符串
}
}
3.2 3DES
密钥长度 | 默认 | 工作方式 | 填充方式 | 实现方 |
---|---|---|---|---|
112、168 | 168 | ECB、CBC、PCBC、CTR、CTS、CFB、CFB8到128、OFB、OFB8到128 | NoPadding、PKCS5Padding、ISO10126Padding | JDK |
128、192 | 168 | ECB、CBC、PCBC、CTR、CTS、CFB、CFB8到128、OFB、OFB8到128 | PKCS7Padding、ISO1012d2Padding、X932Padding、ZeroBytePadding | Bouncy Castle |
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import java.math.BigInteger;
import java.security.SecureRandom;
public class Test {
public static void main(String[] args) throws Exception {
// 原始字符
String data = "Hello World!";
// 生成key
KeyGenerator keyGenerator=KeyGenerator.getInstance("DESede");
keyGenerator.init(112); //3DES需要112 or 168位
keyGenerator.init(new SecureRandom()); //或者使用这种方式默认长度,无需指定长度
SecretKey secretKey = keyGenerator.generateKey(); //生成key的材料
byte[] key = secretKey.getEncoded(); //生成key
// key转换成密钥(自己指定字节数组时需要转换)
DESedeKeySpec desKeySpec=new DESedeKeySpec(key);
SecretKeyFactory factory=SecretKeyFactory.getInstance("DESede");
SecretKey key2 = factory.generateSecret(desKeySpec); //转换后的密钥
//加密
Cipher cipher=Cipher.getInstance("DESede/ECB/PKCS5Padding"); //算法类型/工作方式/填充方式
cipher.init(Cipher.ENCRYPT_MODE, key2); //指定为加密模式
byte[] result = cipher.doFinal(data.getBytes());
BigInteger bigInteger = new BigInteger(1, result);
System.out.println("jdk3DES加密: " + bigInteger.toString(16)); //转换为十六进制
//解密
cipher.init(Cipher.DECRYPT_MODE, key2); //相同密钥,指定为解密模式
result = cipher.doFinal(result); //根据加密内容解密
System.out.println("jdk3DES解密: " + new String(result)); //转换字符串
}
}
3.3 AES
密钥长度 | 默认 | 工作方式 | 填充方式 | 实现方 |
---|---|---|---|---|
112、192、256 | 128 | ECB、CBC、PCBC、CTR、CTS、CFB、CFB8到128、OFB、OFB8到128 | NoPadding、PKCS5Padding、ISO10126Padding | JDK |
112、192、256 | 128 | ECB、CBC、PCBC、CTR、CTS、CFB、CFB8到128、OFB、OFB8到128 | PKCS7Padding、ZeroBytePadding | Bouncy Castle |
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.security.Key;
import java.security.SecureRandom;
public class Test {
public static void main(String[] args) throws Exception {
// 原始字符
String data = "Hello World!";
//生成key
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(new SecureRandom());
SecretKey secretKey = keyGenerator.generateKey();
byte[] key1 = secretKey.getEncoded();
//key转换为密钥
Key key2 = new SecretKeySpec(key1, "AES");
//加密
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5padding");
cipher.init(Cipher.ENCRYPT_MODE, key2);
byte[] result = cipher.doFinal(data.getBytes());
BigInteger bigInteger = new BigInteger(1, result);
System.out.println("jdkAES加密: " + bigInteger.toString(16)); //转换为十六进制
//解密
cipher.init(Cipher.DECRYPT_MODE, key2);
result = cipher.doFinal(result);
System.out.println("jdkAES解密: "+new String(result)); //转换字符串
}
}
3.4 PBE
基于口令的对称加密算法(它其实是对之前的算法的包装,比如说MD5和DES,我这里就是的是对MD5和DES包装的PBE算法,还有其他类型的PBE),口令就是我们俗话说的密码,PBE中有一个salt(盐)的概念,盐就是干扰码
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import java.security.Key;
import java.security.SecureRandom;
public class Test {
public static void main(String[] args) throws Exception {
// 原始字符
String data = "Hello World!";
//初始化盐
SecureRandom random=new SecureRandom();
byte[] salt = random.generateSeed(8); // 指定为8位的盐 (盐就是干扰码,通过添加干扰码增加安全)
// 口令和密钥
String password = "TEST"; // 口令
PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray());
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEWITHMD5andDES");
Key key = factory.generateSecret(pbeKeySpec); // 密钥
PBEParameterSpec pbeParameterSpec = new PBEParameterSpec(salt, 100); // 参数规范,第一个参数是盐,第二个是迭代次数(经过散列函数多次迭代)
Cipher cipher = Cipher.getInstance("PBEWITHMD5andDES");
//加密
cipher.init(Cipher.ENCRYPT_MODE, key, pbeParameterSpec);
byte[] result = cipher.doFinal(data.getBytes());
System.out.println("jdk PBE加密: "+ Base64.getEncoder().encodeToString(result));
//解密
cipher.init(Cipher.DECRYPT_MODE, key, pbeParameterSpec);
result = cipher.doFinal(result);
System.out.println("jdk PBE解密: "+ new String(result));
}
}
四、非对称加密
非对称算法就是发送方和接收方的密钥是不一样的,非对称相对于对称来说,有公钥和私钥的概念,基本上公钥是公开的,比如会在网络上传输,而私钥安全性要求就要高很多了,因为私钥是要保密的
基本的非对称算法有DH,RSA,ELGamal算法
4.1 DH
基于交换交换的非对称算法,发送方需要得到接收方的key构建本地密钥,而接收方也需要得到发送方的key构建自己本地的密钥。
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
import java.security.*;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Objects;
public class Test {
public static void main(String[] args) throws Exception {
// 原始字符
String data = "Hello World!";
//初始化发送方密钥
KeyPairGenerator senderKeyPairGenerator=KeyPairGenerator.getInstance("DH");
senderKeyPairGenerator.initialize(512); //密钥长度
KeyPair senderKeyPair = senderKeyPairGenerator.generateKeyPair();
byte[] senderPublicKeyEnc = senderKeyPair.getPublic().getEncoded(); //发送方key,需传递给接收方(网络,文件)
//初始化接收方密钥
KeyFactory factory=KeyFactory.getInstance("DH");
X509EncodedKeySpec x509EncodedKeySpec=new X509EncodedKeySpec(senderPublicKeyEnc); //根据从发送方得到的key解析
PublicKey receiverPublicKey=factory.generatePublic(x509EncodedKeySpec);
DHParameterSpec dhParameterSpec=((DHPublicKey)receiverPublicKey).getParams();
KeyPairGenerator receiverKeyPairGenerator=KeyPairGenerator.getInstance("DH");
receiverKeyPairGenerator.initialize(dhParameterSpec);
KeyPair receiverKeyPair = receiverKeyPairGenerator.generateKeyPair();
PrivateKey receiverPrivateKey = receiverKeyPair.getPrivate();
byte[] receiverPublicKeyEnc = receiverKeyPair.getPublic().getEncoded();
//密钥构建
KeyAgreement receiverKeyAgreement=KeyAgreement.getInstance("DH");
receiverKeyAgreement.init(receiverPrivateKey);
receiverKeyAgreement.doPhase(receiverPublicKey, true);
SecretKey receiverDESKey=receiverKeyAgreement.generateSecret("DES"); //发送发密钥(公钥)
KeyFactory senderKeyFactory=KeyFactory.getInstance("DH");
x509EncodedKeySpec=new X509EncodedKeySpec(receiverPublicKeyEnc);
PublicKey senderPublicKey=senderKeyFactory.generatePublic(x509EncodedKeySpec);
KeyAgreement senderKeyAgreement=KeyAgreement.getInstance("DH");
senderKeyAgreement.init(senderKeyPair.getPrivate());
senderKeyAgreement.doPhase(senderPublicKey, true);
SecretKey senderDESKey=senderKeyAgreement.generateSecret("DES"); //接收方密钥(私钥)
if(Objects.equals(receiverDESKey, senderDESKey)){
System.out.println("双方密钥相同");
}
//加密
Cipher cipher=Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, senderDESKey);
byte[] result = cipher.doFinal(data.getBytes());
String encode = Base64.getEncoder().encodeToString(result);
System.out.println("jdk DH加密: "+ encode);
//解密
cipher.init(Cipher.DECRYPT_MODE, receiverDESKey);
result=cipher.doFinal(result);
System.out.println("jdk DH解密: " + new String(result));
}
}
4.2 RSA
RSA相较于DH算法的实现简单,适用范围较广,公钥和私钥的创建较简单,而且支持公钥加密,私钥解密或者是私钥加密,公钥解密两种方式。
import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class Test {
public static void main(String[] args) throws Exception {
// 原始字符
String data = "Hello World!";
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(512);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
RSAPublicKey rsaPublicKey=(RSAPublicKey) keyPair.getPublic(); //公钥
RSAPrivateKey rsaPrivateKey=(RSAPrivateKey) keyPair.getPrivate(); //私钥
System.out.println("public key:"+ Base64.getEncoder().encodeToString(rsaPublicKey.getEncoded()));
System.out.println("private key:" + Base64.getEncoder().encodeToString(rsaPrivateKey.getEncoded()));
//私钥加密,公钥解密--加密
PKCS8EncodedKeySpec pkcs8EncodedKeySpec=new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded());
KeyFactory keyFactory=KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Cipher cipher=Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] result = cipher.doFinal(data.getBytes());
System.out.println("RSA私钥加密,公钥解密--加密:"+Base64.getEncoder().encodeToString(result));
//私钥加密,公钥解密--解密
X509EncodedKeySpec x509EncodedKeySpec=new X509EncodedKeySpec(rsaPublicKey.getEncoded());
keyFactory=KeyFactory.getInstance("RSA");
PublicKey publicKey=keyFactory.generatePublic(x509EncodedKeySpec);
cipher=Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE,publicKey);
result = cipher.doFinal(result);
System.out.println("RSA私钥加密,公钥解密--解密:"+new String(result));
//公钥加密,私钥解密--加密
x509EncodedKeySpec=new X509EncodedKeySpec(rsaPublicKey.getEncoded());
keyFactory=KeyFactory.getInstance("RSA");
publicKey=keyFactory.generatePublic(x509EncodedKeySpec);
cipher=Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE,publicKey);
result = cipher.doFinal(data.getBytes());
System.out.println("RSA公钥加密,私钥解密--加密:"+Base64.getEncoder().encodeToString(result));
//公钥加密,私钥解密--解密
pkcs8EncodedKeySpec=new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded());
keyFactory=KeyFactory.getInstance("RSA");
privateKey =keyFactory.generatePrivate(pkcs8EncodedKeySpec);
cipher=Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE,privateKey);
result=cipher.doFinal(result);
System.out.println("RSA公钥加密,私钥解密--解密:"+new String(result));
}
}
五、keyGenerator,KeyPairGenerator,SecretKeyFactory的区别
Java加密的常用的加密算法类型有三种:
- 【1】单向加密:也就是不可逆的加密,例如MD5,SHA,HMAC
- 【2】对称加密:也就是加密方和解密方利用同一个秘钥对数据进行加密和解密,例如DES,PBE等等
- 【3】非对称加密:非对称加密分为公钥和秘钥,二者是非对称的,例如用私钥加密的内容需要使用公钥来>解密,使用公钥加密的内容需要用私钥来解密,DSA,RSA…
而keyGenerator,KeyPairGenerator,SecretKeyFactory的三种使用方法刚好和这三种加密算法类型对上:
- 【1】keyGenerator:秘钥生成器,也就是更具算法类型随机生成一个秘钥,例如HMAC,所以这个大部分用在非可逆的算法中
- 【2】SecretKeyFactory:秘密秘钥工厂,言外之意就是需要根据一个秘密(password)去生成一个秘钥,例如DES,PBE,所以大部分使用在对称加密中
- 【3】KeyPairGenerator:秘钥对生成器,也就是可以生成一对秘钥,也就是公钥和私钥,所以大部分使用在非对称加密中