前言
接上文java密码学习,继续学习现代密码学
现代密码学
位运算加密
位运算指的是内存中二进制为的操作。
与运算 & 0&0=0,0&1=0,1&0=0,1&1=1
异或运算 ^ 同为假,异为真。0&0=0,0&1=1,1&0=1,1&1=0
非运算 ~ 取反运算,1变0,0变1
或运算 | 0&0=0,0&1=1,1&0=1,1&1=1
举个例子:
99 ^ 2 = 97
转化二进制位进行异或运算
01100011^00000010 = 01100001
java实现异或运算
package javaweb.com.demo;
public class Encryption {
public static void main(String[] args) throws Exception {
int len,i;
char[] newstr;
char[] str = {'a','b','c'};//定义char类型数组
char n = 2;//定义秘钥
len = str.length;
newstr = new char[len];//定义数组
for(i=0;i<len;i++){
newstr[i]=(char) (str[i]^n);
//异或运算后转char类型
}
System.out.println(newstr);
}
}
转化为char类型直接和密码进行运算。
java实现位运算加解密
package javaweb.com.demo;
public class Encryption {
public static void main(String[] args) throws Exception {
String strs = "cseroad";
int key = 10;
//加密
char[] newchr = strs.toCharArray();
for (int i = 0; i < newchr.length; i++) {
newchr[i] = (char) (newchr[i]^key);
}
System.out.println("加密结果如下:"+new String(newchr));
//解密
StringBuilder sb = new StringBuilder();
for (int i = 0; i < newchr.length; i++) {
char oldchr = (char) (newchr[i]^key);
sb.append(oldchr);
}
System.out.println(sb);
}
}
怎么位运算加密的,就怎么位运算解密。
对称加密
对称密码体制分为两种:
一种对明文字节进行运算,称为流加密,也叫序列加密;
一种把明文信息划分不同的组或块后再进行加密,称为分组加密。
如123456789,流加密就是先对1加密,再对2加密,往后依次,最后拼成密文。
块加密是把该数字分成不同的块,如123成块,456成块,789成块,再对不同的块加密,最后形成密文。
DES、AES都是常见的对称加密算法,且属于分组加密。
分组加密总共有五种工作模式:电子密码本模式ECB、密文链接模式CBC、密文反馈模式CFB、输出反馈模式OFB、计数器模式CTR。
AES加密
先看代码
java 实现AES加解密
package javaweb.com.demo;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class Encryption {
public static void main(String[] args) throws Exception {
String input = "cseroad";
//原文
String key = "1234567890abcdef";
//秘钥
String transformation = "AES";
//AES算法
String algorithm = "AES";
//加密类型
//加密方法
String encryptdes = encryptDES(input,key,transformation,algorithm);
System.out.println("AES加密:"+encryptdes);
//解密方法
String decryptdes = decryptDES(encryptdes,key,transformation,algorithm);
System.out.println("AES解密:"+decryptdes);
}
private static String encryptDES(String input,String key,String transformation,String algorithm) throws Exception {
Cipher cipher = Cipher.getInstance(transformation);
//创建加密对象
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm);
//创建加密规则。第一个参数key秘钥,第二个参数加密的类型
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
//加密初始化。第一个参数表示加密模式,第二个参数表示加密规则
byte[] bytes = cipher.doFinal(input.getBytes());
//原文字节数组进行加密
String bs64 = Base64.getEncoder().encodeToString(bytes);
//直接打印出现乱码,采用base64编码后输出
return bs64;
//打印密文
}
private static String decryptDES(String encryptdes, String key, String transformation, String algorithm) throws Exception {
Cipher cipher = Cipher.getInstance(transformation);
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
byte[] old = Base64.getDecoder().decode(encryptdes);
byte[] bytes = cipher.doFinal(old);
return new String(bytes);
}
}
要点
使用Cipher这个类实现对AES、DES的加解密。
AES的秘钥key长度必须是16byte。
算法transformation变量这里是AES,其完整格式应该为AES/ECB/PKCS5Padding,也是默认值。
第一个参数加密算法
- AES,DES,DESede(DES3)和 RSA 四种
第二个参数工作模式,就是上面介绍的那五种。这里再说一下经常使用的ECB、CBC工作模式
- ECB 电子密码本,需要加密的消息按照块密码的块大小分成数块,并对块独立加密。
- CBC 密码块链接,每个明文块先与前一个密文块进行异或后,再进行加密。
第三个参数填充模式,
- Nopadding 不填充,默认值
- PKCS5Padding 填充,加密内容不足8位就填充。
如果要使用CBC加密模式,需要定义一个IvParameterSpec对象,向量模式也可以简单理解为偏移量。
代码如下
package javaweb.com.demo;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class Encryption {
public static void main(String[] args) throws Exception {
String input = "cseroad";
//原文
String key = "1234567890abcdef";
//秘钥
String transformation = "AES/CBC/PKCS5Padding";
//AES算法
String algorithm = "AES";
//加密类型
//加密方法
String encryptdes = encryptDES(input,key,transformation,algorithm);
System.out.println("AES加密:"+encryptdes);
//解密方法
String decryptdes = decryptDES(encryptdes,key,transformation,algorithm);
System.out.println("AES解密:"+decryptdes);
}
private static String encryptDES(String input,String key,String transformation,String algorithm) throws Exception {
Cipher cipher = Cipher.getInstance(transformation);
//创建加密对象
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm);
//创建加密规则。第一个参数key字节,第二个参数加密的类型
IvParameterSpec iv = new IvParameterSpec("9876543210qwerty".getBytes());
//创建IV向量
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec,iv);
//加密初始化。第一个参数表示加密模式,第二个参数表示加密规则
byte[] bytes = cipher.doFinal(input.getBytes("UTF-8"));
//原文字节数组进行加密
String bs64 = Base64.getEncoder().encodeToString(bytes);
//直接打印出现乱码,采用base64编码后输出
return bs64;
//打印密文
}
private static String decryptDES(String encryptdes, String key, String transformation, String algorithm) throws Exception {
Cipher cipher = Cipher.getInstance(transformation);
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm);
IvParameterSpec iv = new IvParameterSpec("9876543210qwerty".getBytes());
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec,iv);
byte[] old = Base64.getDecoder().decode(encryptdes);
byte[] bytes = cipher.doFinal(old);
return new String(bytes);
}
}
要点
iv向量长度必须16个字节。
DES 加密
DES加密和上面的AES大同小异,直接替换AES就可以。需要注意的是DES的秘钥key必须是8位byte。
非对称加密
非对称加密中,加密解密使用不同的秘钥。对外公开的秘钥为公钥,对外保密的秘钥为私钥。
公钥加密,私钥解密;私钥加密,公钥解密。
常见的非对称加密方式:RSA、DSA、ECC
RSA 加密
使用KeyPairGenerator类创建密码对。
package javaweb.com.demo;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
import javax.crypto.Cipher;
public class Encryption {
public static void main(String[] args) throws Exception {
//创建秘钥对
String algorithm = "RSA";
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
//生成秘钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
//生成私钥
PrivateKey privateKey = keyPair.getPrivate();
//生成公钥
PublicKey publicKey = keyPair.getPublic();
//获取私钥自己数组
byte[] privateKeyEncode = privateKey.getEncoded();
//获取公钥自己数组
byte[] publicKeyEncode = publicKey.getEncoded();
//base64编码
String privateKeyEncodeString = Base64.getEncoder().encodeToString(privateKeyEncode);
System.out.println(privateKeyEncodeString);
String publicKeyEncodeString = Base64.getEncoder().encodeToString(publicKeyEncode);
System.out.println(publicKeyEncodeString);
}
}
注意base64编码输出
java 实现私钥加密,公钥解密
package javaweb.com.demo;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
import javax.crypto.Cipher;
public class Encryption {
public static void main(String[] args) throws Exception {
//创建秘钥对
String algorithm = "RSA";
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
//生成秘钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
//生成私钥
PrivateKey privateKey = keyPair.getPrivate();
//生成公钥
PublicKey publicKey = keyPair.getPublic();
//原文
String input = "cseroad";
//创建加密对象
Cipher cipher = Cipher.getInstance(algorithm);
//加密初始化,使用私钥加密
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] bytes = cipher.doFinal(input.getBytes("UTF-8"));
String bs64 = Base64.getEncoder().encodeToString(bytes);
System.out.println("密文"+bs64);
//解密
cipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] bytes1 = cipher.doFinal(bytes);
System.out.println("原文"+new String(bytes1));
}
}
DSA 加密
DSA是一种更高级的验证方式,用作数字签名。不单单只有公钥、私钥,还有数字签名。私钥加密生成数字签名,公钥验证数据及签名。
java实现DSA加密并数字签名的认证,部分代码参考github
package javaweb.com.demo;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Signature;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class Encryption {
private static String PUBLIC_KEY = "MIIBuDCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoDgYUAAoGBAOgR1bl0s3/9tsxVU2tkq5/nfOKZ2z8jT6cLG/axihbSEOAhmXTgPTD3V3bSLtK9O2CIwvR6Ljz70Sl/LUFI0+YBIFGB/jASCJ2av/Im0SItyRDiT+n8GDo6OHc8SyTSwxPv4tVGGy52lqdHO9IqK6QpFZ00LNaPy+5IRfoZrMZh";
private static String PRIVATE_KEY = "MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoEFgIUcwvMm8OVRd1UsTx+q/IgcIUlsns=";
public static void main(String[] args) {
//随机生成成对密钥
genKeyPair();
String sign=sign("cseroad", PRIVATE_KEY);
System.out.println("数字签名:" + sign);
//字符串解码
System.out.println("数字签名校验:" + verify("cseroad",sign,PUBLIC_KEY));
}
/**
* 随机生成密钥对
*/
public static void genKeyPair() {
try {
// KeyPairGenerator类用于生成公钥和私钥对,基于DSA算法生成对象 可以使用RSA算法生成 成对出现就好
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("DSA");
// 初始化密钥对生成器,密钥大小为96-1024位 可自定义随机产生器 SecureRandom
keyPairGen.initialize(1024);
// 生成一个密钥对,保存在keyPair中
KeyPair keyPair = keyPairGen.generateKeyPair();
// 得到私钥
DSAPrivateKey privateKey = (DSAPrivateKey) keyPair.getPrivate();
// 得到公钥
DSAPublicKey publicKey = (DSAPublicKey) keyPair.getPublic();
// 得到公钥字符串
String publicKeyString = new String(Base64.getEncoder().encode(publicKey.getEncoded()));
// 得到私钥字符串
String privateKeyString = new String(Base64.getEncoder().encode((privateKey.getEncoded())));
// 打印公钥和私钥
System.out.println("公匙:" + publicKeyString);
System.out.println("私匙:" + privateKeyString);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 用私钥对信息生成数字签名
*/
public static String sign(String str, String privateKey) {
try {
byte[] bytes = str.getBytes();
//获得DSA公匙
DSAPrivateKey keyFactory = (DSAPrivateKey) KeyFactory.getInstance("DSA").generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey.getBytes())));
Signature signature = Signature.getInstance("DSA");
signature.initSign(keyFactory);
signature.update(bytes);
return new String(Base64.getEncoder().encode(signature.sign()));
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 校验数字签名
*/
public static boolean verify(String str, String sign,String publicKey) {
try {
byte[] bytes = Base64.getDecoder().decode(sign);
DSAPublicKey keyFactory = (DSAPublicKey) KeyFactory.getInstance("DSA").generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey.getBytes())));
Signature signature=Signature.getInstance("DSA");
signature.initVerify(keyFactory);
signature.update(str.getBytes());
return signature.verify(bytes);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
数字证书
数字证书是由权威的CA(Certificate Authority)机构颁布的一种证书。集合了多种密码学算法,用来加解密、身份认证、签名等功能。
非对称加密:对数据加解密
签名算法:确保数据完整性
摘要算法:确保数据没有篡改
我们熟知的HTTPS协议就是应用了数字证书。
数字证书的格式遵循X.509 标准
- 证书的发布机构
- 证书的有效期
- 公钥
- 证书所有者(Subject)
- 签名所使用的算法
- 指纹以及指纹算法
注意证书链的验证是从根证书开始的。抽时间详细解释HTTPS的工作原理。
参考资料
https://www.bilibili.com/video/BV134411T7rq
https://www.bilibili.com/video/BV1ga4y1v7jn
《java 加密与解密的艺术》