一。摘要算法
1》MD5算法(Message Digest Algorithm 5) 可以保证数据传输完整性和一致性 摘要后长度为16字节 摘要信息中不包含原文信息
所有加密结果不可逆(无法解密) 一般在传送文件时 对源文件进行md5 hash 传送到对方后 检测hash值是否相等 如果相等文件传输正确
如果不相等 说明文件被篡改(加入木马)或者未传送完成
其他MD算法 MD2(16字节)
- public static void main(String[] args) throws NoSuchAlgorithmException {
- MessageDigest md=MessageDigest.getInstance("MD5") ;
- String code="hello";
- byte[] bt=md.digest(code.getBytes());
- System.out.println(bt.length);
- }
的摘要算法 摘要的长度为 20字节
其他的SHA 包括 SHA-256(32字节)
- public static void main(String[] args) throws NoSuchAlgorithmException {
- MessageDigest md=MessageDigest.getInstance("SHA") ;//或者SHA-1 SHA1
- String code="hello";
- byte[] bt=md.digest(code.getBytes());
- System.out.println(bt.length);
- }
二。编码和解码
1》16进制 编码 计算机系统使用 2进制 为了编写存储方便一般将2进制 转换为16进制字符串 其中base64也是其中类似转换一种 16进制编码和base64都是
可逆的 一般用于存储
- public static byte[] toByte(String src){
- ByteArrayOutputStream baos=new ByteArrayOutputStream();
- for(int i=0;i<src.length();i=i+2){
- char fchar=src.charAt(i);
- char nchar=src.charAt(i+1);
- byte srcb=0;
- if(fchar=='0'){
- srcb=Byte.parseByte(nchar+"", 16);
- }else{
- srcb=(byte)(Integer.parseInt(fchar+""+nchar, 16));
- }
- baos.write(srcb);
- }
- return baos.toByteArray();
- }
- public static String toHex(byte[] src){
- StringBuffer sb=new StringBuffer();
- for(byte s:src){
- //0XFF表示 8位的 11111111 和它&后 只剩下 8位 其他位都为0
- String result=Integer.toHexString(s&0xFF);
- if(result.length()==1){
- result='0'+result;
- }
- sb.append(result);
- }
- return sb.toString();
- }
2》Base64编码 用于将字节数组和字符串互相转换
- public static void main(String[] args) throws NoSuchAlgorithmException, IOException {
- byte[] src="hello".getBytes();
- //摘要出来的结果为字节数组 存储到数据库不方便
- MessageDigest md=MessageDigest.getInstance("SHA") ;
- byte[] bt=md.digest(src);
- //使用base64转换为字符串方便存储
- BASE64Encoder base=new BASE64Encoder();
- String str=base.encode(bt);
- System.out.println(str);
- //还原成字节数组
- BASE64Decoder de=new BASE64Decoder();
- byte[] bts=de.decodeBuffer(str);
- System.out.println(bt.length==bts.length);
- }
三。对称加密
1》DES算法 (Data Encryptin Standard) 是对称加密算法的一种 使用秘钥加解密 秘钥必须是56字节
概念解释:
秘钥 :用于加密和解密的钥匙 秘钥可以使用 getEncoded方法 获取byte[] 存储在文件系统中
公钥和私钥:用于非对称加密的钥匙 公钥加密 私钥解密 私钥一般用于解密所以私钥一般存储在密钥库中
口令:一般是自定义的字符串 可以通过口令和盐生成秘钥
- /**
- * 生成56字节的秘钥
- */
- public static SecretKey genKey(int len) throws NoSuchAlgorithmException{
- KeyGenerator kg=KeyGenerator.getInstance("DES");
- kg.init(len);
- return kg.generateKey();
- }
- public static void main(String[] args) throws Exception {
- //SecretKey sk=new SecretKeySpec(kl.getBytes(), "DES");
- SecretKey sk=genKey(57);
- //---------加密
- String password="tiger";
- Cipher cipher=Cipher.getInstance("DES");
- cipher.init(Cipher.ENCRYPT_MODE, sk);
- //被加密之后获取的字节数组
- byte[] mcontent=cipher.doFinal(password.getBytes());
- //---------解密
- Cipher cipher1=Cipher.getInstance("DES");
- cipher1.init(Cipher.DECRYPT_MODE, sk);
- System.out.println(new String(cipher1.doFinal(mcontent)));
- }
2》AES算法 (Advanced Encryptin Standard 高级加密标准) 是对称加密算法一种升级 因为 56位秘钥 在计算机系统性能越来越高的前提下 56位很容易被
破解 所以 AES将秘钥的长度提高到128, 192 or 256 必须是这三个数 128默认可以使用 192和256由于美国限制 需要相关授权 否则抛出异常
- public static final String AL="AES";
- /**
- * 生成56字节的秘钥
- */
- public static SecretKey genKey(int len) throws NoSuchAlgorithmException{
- KeyGenerator kg=KeyGenerator.getInstance(AL);
- kg.init(len);
- return kg.generateKey();
- }
- public static void main(String[] args) throws Exception {
- //SecretKey sk=new SecretKeySpec(kl.getBytes(), "DES");
- SecretKey sk=genKey(128);
- //---------加密
- String password="tiger";
- Cipher cipher=Cipher.getInstance(AL);
- cipher.init(Cipher.ENCRYPT_MODE, sk);
- //被加密之后获取的字节数组
- byte[] mcontent=cipher.doFinal(password.getBytes());
- //---------解密
- Cipher cipher1=Cipher.getInstance(AL);
- cipher1.init(Cipher.DECRYPT_MODE, sk);
- System.out.println(new String(cipher1.doFinal(mcontent)));
- }
3》PBE算法(Password Base Encryption) 基于自定义口令的加解密算法 定义口令 同时还必须定义 盐和 使用盐混淆的次数
加解密过程中 该三个参数都必须一致
- //盐 用于将明文进行多次混淆
- static byte[] salt = new byte[8];
- static Random r = new Random();
- static int saltCount=100;
- static{
- r.nextBytes(salt);
- }
- public static final String AL="PBEWithMD5AndDES";
- /**
- * 生成自定义口令的秘钥
- */
- public static SecretKey genKey(String kl) throws Exception{
- char[] klChar=kl.toCharArray();
- PBEKeySpec pbe=new PBEKeySpec(klChar);
- SecretKeyFactory skf=SecretKeyFactory.getInstance(AL);
- return skf.generateSecret(pbe);
- }
- /**
- * 使用口令和盐进行加密
- */
- public static byte[] encrypt(SecretKey key,byte[] src) throws Exception{
- Cipher cipher=Cipher.getInstance(AL);
- //使用口令 盐(100次混淆)
- PBEParameterSpec parameter=new PBEParameterSpec(salt, saltCount);
- cipher.init(Cipher.ENCRYPT_MODE, key,parameter);
- //被加密之后获取的字节数组
- byte[] mcontent=cipher.doFinal(src);
- return mcontent;
- }
- /**
- * 使用口令和盐进行解密 盐和口令和混淆的次数都必须和加密之前一致
- */
- public static byte[] decrypt(SecretKey key,byte[] src) throws Exception{
- Cipher cipher=Cipher.getInstance(AL);
- //使用口令 盐(100次混淆)
- PBEParameterSpec parameter=new PBEParameterSpec(salt, saltCount);
- cipher.init(Cipher.DECRYPT_MODE, key,parameter);
- //被加密之后获取的字节数组
- byte[] mcontent=cipher.doFinal(src);
- return mcontent;
- }
- public static void main(String[] args) throws Exception {
- //SecretKey sk=new SecretKeySpec(kl.getBytes(), "DES");
- SecretKey sk=genKey("123456");
- //---------加密
- String password="tiger";
- byte[] mw=encrypt(sk, password.getBytes());
- //---------解密
- System.out.println(new String(decrypt(sk, mw)));
- }
四。非对称加密
1》DH算法 是一种对称加密到非对称加密的过度算法 使用DH算法生成密钥对 使用对称加密算法获取秘钥后 进行加解密 双方必须都存在公钥和私钥
模型分析
我们以消息传递模型为例,甲方作为发送者,乙方作为接受者,分述甲乙双方如何构建密钥、交互密钥和加密数据。
首先,甲乙双方需要在收发消息前构建自己的密钥对,如图1所示。
甲乙双方构建密钥需要经过以下几个步骤:
1)由消息发送的一方构建密钥,这里由甲方构建密钥。
2)由构建密钥的一方向对方公布其公钥,这里由甲方向乙方发布公钥。
3)由消息接收的一方通过对方公钥构建自身密钥,这里由乙方使用甲方公钥构建乙方密钥。
4)由消息接收的一方向对方公布其公钥,这里由乙方向甲方公布公钥。
这里要注意的是,乙方构建自己密钥对的时候需要使用甲方公钥作为参数这是很关键的一点,如果缺少了这一环节则无法确保甲乙双方获得同一个密钥,消息加密更无从谈起。
其次,假设甲乙双方事先约定好了用于数据加密的对称加密算法(如AES算法),并构建本地密钥(即对称加密算法中的密钥),如图2所示。
![](https://i-blog.csdnimg.cn/blog_migrate/0eb99c5433a52b557c0c294c481ec69f.png)
甲方需要使用自己的私钥和乙方的公钥才能构建自己的本地密钥,乙方需要使用自己的私钥和甲方的公钥才能构建自己的本地密钥。
虽然甲乙双方使用了不同的密钥来构建本地密钥,但是甲乙两方得到的密钥其实是一致的,后面的demo可以证明,也正是基于此,甲乙双方才能顺利地进行加密消息的传送。
最后,甲乙双方构建了本地密钥后,可按照基于对称加密算法的消息传递模型完成消息传递。如图4所示。
![](https://i-blog.csdnimg.cn/blog_migrate/694ee8c74f4dea3b27106b20ad5deb46.png)
作为对称加密体制向非对称加密体制的一种过渡,DH算法仅仅比一般的对称加密算法多了密钥对的构建和本地密钥的构建这两项操作,而真正的数据加密/解密操作仍由对称加密算法完成。
测试代码:
- public static void main(String[] args) throws Exception {
- // 生成DH密钥对
- KeyPairGenerator kpg=KeyPairGenerator.getInstance("DH");
- kpg.initialize(512);
- KeyPair kp=kpg.generateKeyPair();
- PublicKey pk=kp.getPublic();
- PrivateKey prk=kp.getPrivate();
- //实例化
- KeyAgreement keyAgree = KeyAgreement.getInstance("DH");
- //初始化
- keyAgree.init(prk);
- keyAgree.doPhase(pk, true);
- //生成本地密钥
- SecretKey secretKey = keyAgree.generateSecret("DES");
- //还是使用对称方式加解密
- //使用本地秘钥加密
- Cipher cip=Cipher.getInstance("DES");
- cip.init(Cipher.ENCRYPT_MODE, secretKey);
- byte[] mw=cip.doFinal("test".getBytes());
- //使用本地秘钥解密
- Cipher cip1=Cipher.getInstance("DES");
- cip1.init(Cipher.DECRYPT_MODE, secretKey);
- System.out.println(new String(cip1.doFinal(mw)));
- }
dh算法过程:
- package alao;
- import java.security.KeyPair;
- import java.security.KeyPairGenerator;
- import java.security.NoSuchAlgorithmException;
- import java.security.PrivateKey;
- import java.security.PublicKey;
- import java.util.Base64;
- import javax.crypto.Cipher;
- import javax.crypto.KeyAgreement;
- import javax.crypto.SecretKey;
- import javax.crypto.interfaces.DHPublicKey;
- import javax.crypto.spec.DHParameterSpec;
- public class DH {
- public static KeyPair genernateKeyPair() throws NoSuchAlgorithmException {
- KeyPairGenerator kpg=KeyPairGenerator.getInstance("DH");
- kpg.initialize(512);
- KeyPair kp=kpg.generateKeyPair();
- return kp;
- }
- public static byte[] encrypt(SecretKey secretKey,byte[] data) throws Exception
- {
- // 数据加密
- Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
- cipher.init(Cipher.ENCRYPT_MODE, secretKey);
- return cipher.doFinal(data);
- }
- public static byte[] decrypt(SecretKey secretKey,byte[] data) throws Exception
- {
- // 数据加密
- Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
- cipher.init(Cipher.DECRYPT_MODE, secretKey);
- return cipher.doFinal(data);
- }
- public static void main(String[] args) throws Exception {
- //生成A用户的公私钥对
- KeyPair apair=genernateKeyPair();
- PublicKey apubKey=apair.getPublic();
- System.out.println("B接受到A的公钥:"+Base64.getEncoder().encodeToString(apubKey.getEncoded()));
- //生成B用户的公私钥对
- //A和B协商生成对象加密的秘钥过程
- //1 A发送公钥给B 模拟获取变量 传送b过程 getEncode获取byte数组后 转换base64 传送
- DHParameterSpec dhParameterSpec = ((DHPublicKey)apair.getPublic()).getParams();
- KeyPairGenerator kpg=KeyPairGenerator.getInstance("DH");
- kpg.initialize(dhParameterSpec);//传入A的公钥
- KeyPair bpair=kpg.generateKeyPair();
- //2 B通过A的公钥和自己的公私钥 生成秘钥
- //实例化
- KeyAgreement keyAgree = KeyAgreement.getInstance("DH");
- //传入B的私钥和A的公钥
- keyAgree.init(bpair.getPrivate());
- keyAgree.doPhase(apair.getPublic(), true);
- //生成本地密钥
- SecretKey secretKey = keyAgree.generateSecret("DES");
- System.out.println("此时A生成了秘钥:"+Base64.getEncoder().encodeToString(secretKey.getEncoded()));
- //4 A通过B的公钥和自己的公私钥 生成秘钥
- //实例化
- keyAgree = KeyAgreement.getInstance("DH");
- //传入A的私钥和B的公钥
- keyAgree.init(apair.getPrivate());
- keyAgree.doPhase(bpair.getPublic(), true);
- //生成本地密钥
- SecretKey secretKey1 = keyAgree.generateSecret("DES");
- System.out.println("此时B生成了秘钥:"+Base64.getEncoder().encodeToString(secretKey1.getEncoded()));
- byte[] bbb=encrypt(secretKey, "hello".getBytes());
- System.out.println(new String(decrypt(secretKey1, bbb)));
- }
- }
2》RSA算法 目前影响力最大的非对称加密算法 一般公钥对外公开 加密后传送给服务器 服务器使用独有的私钥解密(当然也可以私钥加密 公钥解密 一般不这样 因为谁都有公钥都能解密 加密就没有意义了) 加密的数据在传输过程是 无法破解的 秘钥对初始化大小必须是64的倍数 实际值 只能在512-1024中
- public static void main(String[] args) throws Exception {
- // 生成RSA密钥对
- KeyPairGenerator kpg=KeyPairGenerator.getInstance("RSA");
- kpg.initialize(512);
- KeyPair kp=kpg.generateKeyPair();
- PublicKey pk=kp.getPublic();
- PrivateKey prk=kp.getPrivate();
- //公钥加密
- Cipher cip=Cipher.getInstance("RSA");
- cip.init(Cipher.ENCRYPT_MODE, pk);
- byte[] mw=cip.doFinal("test".getBytes());
- //私钥解密
- Cipher cip1=Cipher.getInstance("RSA");
- cip1.init(Cipher.DECRYPT_MODE, prk);
- System.out.println(new String(cip1.doFinal(mw)));
- }
RSA算法中 一般公钥和私钥都都可以调用getEncoded()转换为byte数组 使用base64编码后 存储在文件中 方便公钥分发 读取文件后需要转换对应
的公钥和私钥的方法为:
- /**
- * 读取公钥字节数组转换为对象
- * @throws Exception
- */
- public PublicKey getPub(byte[] bt) throws Exception{
- X509EncodedKeySpec x=new X509EncodedKeySpec(bt);
- KeyFactory fac=KeyFactory.getInstance("RSA");
- return fac.generatePublic(x);
- }
- /**
- * 读取私钥字节数组转换为对象
- * @throws Exception
- */
- public PrivateKey getPri(byte[] bt) throws Exception{
- PKCS8EncodedKeySpec x=new PKCS8EncodedKeySpec(bt);
- KeyFactory fac=KeyFactory.getInstance("RSA");
- return fac.generatePrivate(x);
- }
五。数据签名(DSA)
签名是非对称加密技术和摘要技术的综合运用用户A将明文和使用私钥加密的明文摘要一起发送给用户B 用户B使用公钥解密出摘要 然后使用相同的摘要
算法将明文摘要 将两个摘要字符串比较如果相等 则表明内容没有被篡改
原理实现过程如下
- public static void main(String[] args) throws Exception {
- //获取公钥和私钥
- KeyPairGenerator kpg=KeyPairGenerator.getInstance("RSA");
- kpg.initialize(512);
- KeyPair kp=kpg.generateKeyPair();
- PublicKey pk=kp.getPublic();
- PrivateKey prk=kp.getPrivate();
- //使用私钥签名
- String message="hello my name is jiaozi";
- //返回的byte就可以进行传输
- byte[] srcByte=sign(message.getBytes(),prk);
- //假设这里模拟篡改数据 肯定会出现异常 或者检验不通过
- srcByte[9]=10;
- //存在公钥的用户 接受到该srcByte 就可以验证是否被篡改
- System.out.println(verify(srcByte,pk));
- }
- /**
- * 签名过程
- */
- public static byte[] sign(byte[] content,PrivateKey pk) throws Exception{
- //对明文进行摘要
- MessageDigest md=MessageDigest.getInstance("MD5");
- byte[] zy=md.digest(content);
- //对摘要进行加密
- Cipher cp=Cipher.getInstance("RSA");
- cp.init(Cipher.ENCRYPT_MODE, pk);
- byte[] enZy=cp.doFinal(zy);
- //要一起传送的数据 双方约定好使用Map
- Map map=new HashMap();
- map.put("content", content);
- map.put("enZy", enZy);
- //传输过程使用byte数组 这里使用序列化将对象打包转换为字节数组
- ByteArrayOutputStream baos=new ByteArrayOutputStream();
- ObjectOutputStream oos=new ObjectOutputStream(baos);
- oos.writeObject(map);
- oos.close();
- return baos.toByteArray();
- }
- /**
- * 验证签名过程
- */
- public static boolean verify(byte[] content,PublicKey pk) throws Exception{
- //将获取的数据转换为Map
- ByteArrayInputStream baos=new ByteArrayInputStream(content);
- ObjectInputStream oos=new ObjectInputStream(baos);
- Map map=(Map)oos.readObject();
- oos.close();
- //获取到明文和加密的摘要信息
- byte[] srcContent=(byte[])map.get("content");
- byte[] enZy=(byte[])map.get("enZy");
- //使用相同的摘要算法 将明文摘要
- MessageDigest md=MessageDigest.getInstance("MD5");
- byte[] contentZy=md.digest(srcContent);
- //将加密的摘要解密
- Cipher cp=Cipher.getInstance("RSA");
- cp.init(Cipher.DECRYPT_MODE, pk);
- byte[] zy=cp.doFinal(enZy);
- BASE64Encoder bas=new BASE64Encoder();
- if(bas.encode(contentZy).equals(bas.encode(zy))){
- return true;
- }
- return false;
- }
- public static void main(String[] args) throws Exception {
- //获取公钥和私钥
- KeyPairGenerator kpg=KeyPairGenerator.getInstance("RSA");
- kpg.initialize(512);
- KeyPair kp=kpg.generateKeyPair();
- PublicKey pk=kp.getPublic();
- PrivateKey prk=kp.getPrivate();
- //使用私钥签名
- String message="hello my name is jiaozi";
- //返回的byte和明文就可以进行传输
- byte[] hash=sign(message.getBytes(),prk);
- //存在公钥的用户 接受到该srcByte 就可以验证是否被篡改
- System.out.println(verify(message.getBytes(),hash,pk));
- }
- /**
- * 签名过程 返回的是加密的摘要
- */
- public static byte[] sign(byte[] content,PrivateKey pk) throws Exception{
- Signature si=Signature.getInstance("MD5WithRSA");//或者使用SHA1WithRSA
- si.initSign(pk);
- si.update(content);
- return si.sign();
- }
- /**
- * 验证签名过程 content表示原文可能获取原文的hash hash表示sign函数返回的加密摘要
- */
- public static boolean verify(byte[] content,byte[] hash,PublicKey pk) throws Exception{
- Signature si=Signature.getInstance("MD5WithRSA");//或者使用SHA1WithRSA
- si.initVerify(pk);
- si.update(content);
- return si.verify(hash);
- }