Java 密码学习(下)

前言

接上文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

转化为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);
    }
    
}
image.png

要点
使用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);
    }
    
}
image.png

要点
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));
        
    } 
    
}
image.png

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;
        }
    }

}
image.png

数字证书

数字证书是由权威的CA(Certificate Authority)机构颁布的一种证书。集合了多种密码学算法,用来加解密、身份认证、签名等功能。
非对称加密:对数据加解密
签名算法:确保数据完整性
摘要算法:确保数据没有篡改

我们熟知的HTTPS协议就是应用了数字证书。

数字证书的格式遵循X.509 标准

  • 证书的发布机构
  • 证书的有效期
  • 公钥
  • 证书所有者(Subject)
  • 签名所使用的算法
  • 指纹以及指纹算法
image.png

注意证书链的验证是从根证书开始的。抽时间详细解释HTTPS的工作原理。

参考资料

https://www.bilibili.com/video/BV134411T7rq
https://www.bilibili.com/video/BV1ga4y1v7jn
《java 加密与解密的艺术》

你可能感兴趣的:(Java 密码学习(下))