Skip to content

Latest commit

 

History

History
658 lines (513 loc) · 21.4 KB

安全密码学.md

File metadata and controls

658 lines (513 loc) · 21.4 KB

安全密码学

一、密码学概述

1、简要介绍

1)、密码学:密码学是一门主要研究编制密码和破译密码的学科。

2)、密码学的主要目的:通俗的来说,研究如何隐藏信息并且把信息传递出去的一个学科。

2、密码学的历史

  • 古典密码学
  • 近代密码学
  • 现代密码学

二、古典密码学

1、替换法

就是使用固定的信息,将原文替换成密文。

替换法的加密方式包括两种:

  • 单表替换

    • 原文和密文用的是同一张表

    • 例如:我对 hepingfly 进行加密(单表方式进行加密)
      加密后的结果变成 yutredfgh
      
  • 多表替换

    • 表示有多张表,原文和密文进行对比

    • 例如:我对 fly 进行加密(多表方式进行加密)
      表1: hepingfly ->  asdfghjkl   表2:hepingfly -> qwertyuio  表3:hepingfly -> zxcvbnmas
      密钥: 213
      加密后的结果就变成: uks
      
    • 这里只是举例使用三张表进行加密,真正使用的时候表可能会更多。

2、移位法

移位法就是将原文中的所有字母都在字母表上向后或向前按照一个固定的数目进行偏移后得出密文。移位法最常见的一个应用就是「凯撒加密」

凯撒加密:

hepingfly -- 每个字母往后移动 2 位 -- jgrkpihna

移位法密码表

三、现代密码学

1、散列函数

散列函数也叫哈希函数。可以将任意长度的消息经过运算,变成固定长度的数值。

常见的加密方式有:MD5、SHA-1、SHA-256

  • MD5 可以将任意长度的原文生成一个 128 位(16 字节)的哈希值
  • SHA-1 可以将任意长度的原文生成一个 160 位(20 字节)的哈希值

2、对称加密

对称加密,加密和解密使用的是同一把密钥。对称加密分为流加密和块加密两种。流加密是对信息流中的每一个元素(一个字母或者一个比特)作为基本的处理单元进行加密。块加密是先对信息流分块,再对每一块分别加密。

例如:
-- 原文为 12345 ,流加密就是先对 1 进行加密,再对 2 进行加密,再对 3 进行加密.... 最后拼接成密文。
-- 块加密,先分成不同的块,比如 123 一个块, 45 一个块,再分别对不同块进行加密,最后拼接成密文。

3、非对称加密

非对称加密,有两把密钥,使用公钥加密,必须使用私钥解密。使用私钥加密,必须使用公钥解密。公钥可以公开,大家使用公钥对信息进行加密,再发送给私钥的持有者。私钥持有者使用私钥对信息进行解密,获得信息原文。

四、凯撒加密

1、凯撒加密

  • 凯撒密码最早是由古罗马军事统帅凯撒在军队中用来传递加密信息。这是一种位移加密方式,只对 26 个字母进行位移替换加密,规则简单,容易破解。
  • 它是一种替换加密技术,明文中的所有字母都在字母表上向后(或向前)按照一个固定数目进行偏移后被替换成密文。
  • 一共只有 26 个字母,所以最多只能移动 25 位
public class KaisarDemo {
    public static void main(String[] args) {
        String originStr = "hepingfly";
        // 原文向右移动 3 位
        int key = 3;
        String encrypt = KaisarEncryption(originStr,key);
        System.out.println("加密后的密文是:" + encrypt);
        
        String decrypt = KaisarDecryption(encrypt, key);
        System.out.println("解密后的原文是:" + decrypt);
    }

    /**
     * 凯撒加密
     * @author hepingfly
     * @date  2020/8/1 下午11:57
     * @param originStr 原始密文
     * @return 
     */
    private static String KaisarEncryption(String originStr, int key) {
        char[] originArray = originStr.toCharArray();
        StringBuilder sb = new StringBuilder();
        for (char c : originArray) {
            int num = c;
            // 移动 3 位
            num+=key;
            sb.append((char)num);
        }
        return sb.toString();
    }
    
    /** 
     * 凯撒解密
     * @author hepingfly
     * @date  2020/8/2 上午11:30
     * @param decryptStr 密文字符串
     * @param key 密钥
     * @return
     */
    private static String KaisarDecryption(String decryptStr, int key) {
        char[] decryptArray = decryptStr.toCharArray();
        StringBuilder sb = new StringBuilder();
        for (char c : decryptArray) {
            int num = c;
            num-=key;
            sb.append((char)num);
        }
        return sb.toString();
    }
}

2、频率分析法

① 作用:在不知道密钥的情况下,也想对凯撒加密后的密文进行破解。

② 原理:以英文字母为例,在一篇英文文章中,英文字母 e 出现的频率是最高的,英文字母 t 出现的频率是第二高的,英文字母 a 出现的频率是第三高的。所以当我们拿到密文的时候,密文里面也会有一个出现频率最高的字母,假设密文里面出现频率最高的字母是 j ,那么 j 就可能是明文里面的 e ,假设密文里面出现频率第二高的是 p ,那么 p 就可能是明文里面的 t,以此类推。

3、Byte 和 bit

Byte :字节。数据存储的基本单位。比如移动硬盘 1T,单位就是 Byte。

bit :比特。又叫位。一个位要么是 0 要么是 1。数据传输的单位。比如家里的宽带是 100M,下载速度并没有达到 100M,一般都是 12-13M 。因为需要使用 100/8

1Byte = 8bit

public static void main(String[] args) {
        String str = "a";
        byte[] bytes = str.getBytes();
        for (byte aByte : bytes) {
            // 打印下这个字节,其实就是 Ascii 码
            System.out.println(aByte);
            // 把这个字节转换成 bit
            String binaryStr = Integer.toBinaryString(aByte);
            System.out.println(binaryStr);
        }
    // 最后输出的结果是: 97    1100001
} 

//------------------------------------------------------------------------------------------
public static void main(String[] args) {
        String str = "和";       // 如果这里是中文的话
        byte[] bytes = str.getBytes();
        for (byte aByte : bytes) {
            System.out.println(aByte);
            String binaryStr = Integer.toBinaryString(aByte);
            System.out.println(binaryStr);
        }
}
// 我们就会发现字节数组的长度时 3
// 打印的结果是: -27   -110    -116     
// 11111111111111111111111111100101   11111111111111111111111110010010  11111111111111111111111110001100

public static void main(String[] args) throws UnsupportedEncodingException {
        String str = "和";
        byte[] bytes = str.getBytes("GBK");
        for (byte aByte : bytes) {
            // 打印下这个字节,其实就是 Ascii 码
            System.out.println(aByte);
            // 把这个字节转换成 bit
            String binaryStr = Integer.toBinaryString(aByte);
            System.out.println(binaryStr);
        }
    }

注:

  • 根据编码格式不一样,一个中文汉字对应的字节也不一样。
  • 如果是 UTF-8,一个中文对应的是 3 个字节。
  • 如果是 GBK ,一个中文对应的是 2 个字节

五、常见加密方式

1、对称加密之 DES 加密

简单说一下对称加密的特点:

  • 加密速度快
  • 如果在 Ascii 码表上找不到对应的字符,就会出现乱码
  • 一般需要结合 base64 一起使用
public static void main(String[] args) throws Exception {
        // 原文
        String input = "和平";
        // 定义 key,如果使用 DES 去进行加密,密钥必须是 8 个字节,否则会报错
        String key = "12345678";
        // 使用到的加密算法
        String transformation = "DES";
        // 加密类型
        String algorithm = "DES";
        // 创建加密对象
        Cipher cipher = Cipher.getInstance(transformation);
        /*
          创建加密规则
          param1: key 的字节数组
          param2: 加密的类型
         */
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm);
        /*
         * 进行加密初始化
         * param1: 模式,加密模式还是解密模式
         * param2: 加密的规则
         */
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
        // 调用加密方法,返回加密后的字节数组
        byte[] encryptBytes = cipher.doFinal(input.getBytes());
        /*
         * 这里我就可以直接把这个密文字节数组转成字符串打印出来
         * 但是打印的结果会出现乱码。
         * 原因在于这个字节数组的内容是 [44,-72,-123,-125,-105,-27,-107,42] (可以遍历看下)
         * 中间的这几个负数在 Ascii 码表上是找不到的,所以就会出现乱码
         * 因此为了把加密后的密文显示出来,可以借助 Base64 进行转码,这样就可以显示加密结果了
         */
        System.out.println(new String(encryptBytes));    // 得到的结果是一个乱码
        // 对密文字节数组进行 Base64 转码
        String encryptStr = Base64.encode(encryptBytes);
        System.out.println(encryptStr);
    }

2、对称加密之 DES 解密

/**
 * @auther hepingfly
 * @date 2020/8/2 下午8:23
 */
public class DesAesDemo {
    public static void main(String[] args) throws Exception {
        // 原文
        String input = "和平";
        // 定义 key,如果使用 DES 去进行加密,密钥必须是 8 个字节
        String key = "12345678";
        // 使用到的加密算法
        String transformation = "DES";
        // 加密类型
        String algorithm = "DES";
        String encryptStr = encryptDes(input, key, transformation, algorithm);
        String originStr = decryptDes(encryptStr, key, transformation, algorithm);
        System.out.println(originStr);
    }

    /** 
     * DES 解密
     * @author hepingfly
     * @date  2020/8/3 下午8:35
     * @param input 需要解密的密文
     * @param key 密钥
     * @param transformation 使用的解密算法
     * @param algorithm 解密的类型
     * @return 解密后的字符串
     */
    private static String decryptDes(String input, 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[] bytes = cipher.doFinal(Base64.decode(input));
        return new String(bytes);
    }

3、对称加密之 AES 加密

AES 加密和 DES 加密基本一样。

只不过 AES 加密比 DES 加密更高级,所以它需要的密钥 key 必须是 16 个字节。

    public static void main(String[] args) throws Exception {
        // 原文
        String input = "和平";
        // 定义 key,如果使用 AES 去进行加密,密钥必须是 16 个字节
        String key = "1234567812345678";
        // 使用到的加密算法
        String transformation = "DES";
        // 加密类型
        String algorithm = "DES";
    }

4、base64 介绍

1)、简单介绍

  • base64 不是加密算法,它是一种可读性算法。
  • base64 它的目的不是为了保护我们的数据,而是为了提高可读性
  • base64 是由 64 个字符组成的,大写的 A-Z ,小写的 a-z ,数字 0-9+/

2)、编码过程

1、首先将待转换的数据进行切分,每 3 个字节分为一组,1 个字节 8 位,所以总共 24 位。

2、对切分后的数据进行重组, 24 位重组成 4 组,这样每组就 6 位。

3、对重组后的数据进行处理,一个字节应该是 8 位,缺少 2 位,在高位进行补齐,高位补 0。构成每组 8 位。

4、根据 base64 编码表,获取相应的编码值。

base64编码过程

3)、base64 补等号规则

3个字节是一组,当字节不够的时候,需要使用 = 进行补齐

/**
 * @auther hepingfly
 * @date 2020/8/3 下午9:55
 */
public class Base64Test {

    // 3个字节是一组,当字节不够的时候,需要使用 = 进行补齐
    public static void main(String[] args) {
        /*
         * 字符串 1 占 1个字节,不够 3 个字节,所以会补两个等号
         * 结果是: MQ==
         */
        System.out.println(Base64.encode("1".getBytes()));

        /*
         * 字符串 12 占 2个字节,不够 3 个字节,所以会补一个等号
         * 结果是:MTI=
         */
        System.out.println(Base64.encode("12".getBytes()));
        /*
         * 字符串 123 占 3个字节,不用补等号
         * 结果是:MTIz
         */
        System.out.println(Base64.encode("123".getBytes()));
        /*
         * 字符串 1234 占 4个字节,需要补两个等号
         * 结果是:MTIzNA==
         */
        System.out.println(Base64.encode("1234".getBytes()));
        /*
         * 中文汉字,在 UTF-8 编码下,一个汉字占 3 个字节,所以不用补等号
         * 结果是:5ZKM
         */
        System.out.println(Base64.encode("和".getBytes()));
    }
}

六、加密模式

1、ECB

ECB:Electronic codebook ,电子密码本。把一段文本进行分拆加密,使用同一个 key ,分别进行加密,然后组合到一起。

优点:

  • 可以并行处理数据

缺点:

  • 同样的原文生成同样的密文,不能很好的保护数据

同时加密,原文是一样的,加密出来的密文也是一样的。

2、CBC

CBC:Cipher-block chaining 密码块连接。每个明文块先与前一个密文块进行异或后,再进行加密。在这种方法中,每个密文块都依赖于它前面的所有明文块。

优点:

  • 同样的原文生成的密文不一样

缺点:

  • 串行处理数据

七、消息摘要

1、消息摘要介绍

1)、介绍

  • 消息摘要(Message Digest)又称为数字摘要(Digital Digest)
  • 它是一个唯一对应一个消息或文本的固定长度的值,它由一个单向 Hash 加密函数对消息进行作用而产生
  • 使用数字摘要生成的值是不可篡改的,为了保证文件或者值的安全

2)、特点

无论输入的消息有多长,计算出来的消息摘要的长度总是固定的。例如用 MD5 算法摘要的消息有 128 位,用 SHA-1 算法摘要的消息有 160 位输出。

3)、常见的摘要算法

  • MD5
  • SHA1
  • SHA256
  • SHA512

2、摘要算法代码实现

/**
 * 摘要算法,md5, SHA-1   SHA-256
 * @auther hepingfly
 * @date 2020/8/6 下午8:11
 */
public class DigestDemo {
    public static void main(String[] args) throws NoSuchAlgorithmException {
        /*
         * 这里可以换成别的摘要算法 如 SHA-1  SHA-256
        */
        String algorithm = "MD5";
//        String algorithm = "SHA-1";
        String input = "hepingfly";
        String str = encryptMD5(algorithm, input);
        System.out.println(str);
    }

    private static String encryptMD5(String algorithm, String input) throws NoSuchAlgorithmException {
        // 创建消息摘要对象
        MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
        // 执行消息摘要算法
        byte[] bytes = messageDigest.digest(input.getBytes());
        StringBuilder sb= new StringBuilder();
        for (byte aByte : bytes) {
            // 把密文转换成 16 进制
            // 0xff 转换成 8 位二进制是 11111111  aByte 跟它 & 完之后还是它自己
            String hexStr = Integer.toHexString(aByte & 0xff);
            // 如果密文的长度是 1 ,需要在高位进行补 0
            if (hexStr.length() == 1) {
                hexStr = "0" + hexStr;
            }
            sb.append(hexStr);
        }
        return sb.toString();
    }
}

八、非对称加密

1、非对称加密简单介绍

1)、简单介绍

  • 加密和解密使用的是不同的密钥
  • 如果使用私钥加密,就只能使用公钥解密
  • 如果使用公钥加密,就只能使用私钥解密

2)、常见算法

  • RSA
  • ECC

2、公钥和私钥生成

/**
 * @auther hepingfly
 * @date 2020/8/8 10:30 PM
 */
public class RSADemo {
    public static void main(String[] args) throws NoSuchAlgorithmException, Base64DecodingException {
        /*
         * KeyPairGenerator 密钥对生成器
         */
        String algorithm = "RSA";
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
        // 生成密钥对
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        // 生成公钥
        PublicKey publicKey = keyPair.getPublic();
        // 生成私钥
        PrivateKey privateKey = keyPair.getPrivate();
        // 获取私钥字节数组
        byte[] privateKeyEncoded = privateKey.getEncoded();
        // 获取公钥字节数组
        byte[] publicKeyEncoded = publicKey.getEncoded();
        // 对公钥和私钥进行 base64 转码
        String privateEncode = Base64.encode(privateKeyEncoded);
        String publicEncode = Base64.encode(publicKeyEncoded);
        System.out.println(privateEncode);
        System.out.println(publicEncode);
    }
}

3、私钥加密

public class RSADemo {
    public static void main(String[] args) throws NoSuchAlgorithmException, Base64DecodingException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        String input = "hepingfly";
        /*
         * KeyPairGenerator 密钥对生成器
         */
        String algorithm = "RSA";
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
        // 生成密钥对
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        // 生成公钥
        PublicKey publicKey = keyPair.getPublic();
        // 生成私钥
        PrivateKey privateKey = keyPair.getPrivate();
        // 获取私钥字节数组
        byte[] privateKeyEncoded = privateKey.getEncoded();
        // 获取公钥字节数组
        byte[] publicKeyEncoded = publicKey.getEncoded();
        // 对公钥和私钥进行 base64 转码
        String privateEncode = Base64.encode(privateKeyEncoded);
        String publicEncode = Base64.encode(publicKeyEncoded);

        // 创建加密对象
        Cipher cipher = Cipher.getInstance(algorithm);
        /*
         * 对加密进行初始化
         * 第一个参数:加密的模式
         * 第二个参数:想使用公钥加密还是私钥加密
         */
        cipher.init(Cipher.ENCRYPT_MODE,privateKey);
        // 调用 doFinal 方法进行加密
        byte[] bytes = cipher.doFinal(input.getBytes());
        String privateStr = Base64.encode(bytes);
        System.out.println(privateStr);
    }
}

4、公钥解密

public class RSADemo {
    public static void main(String[] args) throws NoSuchAlgorithmException, Base64DecodingException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        String input = "hepingfly";
        /*
         * KeyPairGenerator 密钥对生成器
         */
        String algorithm = "RSA";
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
        // 生成密钥对
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        // 生成公钥
        PublicKey publicKey = keyPair.getPublic();
        // 生成私钥
        PrivateKey privateKey = keyPair.getPrivate();
        // 获取私钥字节数组
        byte[] privateKeyEncoded = privateKey.getEncoded();
        // 获取公钥字节数组
        byte[] publicKeyEncoded = publicKey.getEncoded();
        // 对公钥和私钥进行 base64 转码
        String privateEncode = Base64.encode(privateKeyEncoded);
        String publicEncode = Base64.encode(publicKeyEncoded);

        // 创建加密对象
        Cipher cipher = Cipher.getInstance(algorithm);
        /*
         * 对加密进行初始化
         * 第一个参数:加密的模式
         * 第二个参数:想使用公钥加密还是私钥加密
         */
        cipher.init(Cipher.ENCRYPT_MODE,privateKey);
        // 调用 doFinal 方法进行加密
        byte[] bytes = cipher.doFinal(input.getBytes());
        String privateStr = Base64.encode(bytes);
        System.out.println(privateStr);
		
        // 注意,这里必须要使用公钥去进行解密
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        byte[] decryptByte = cipher.doFinal(bytes);
        System.out.println(new String(decryptByte));

    }
}

九、数字签名

1、介绍

数字签名:只有信息的发送者,才能产生别人无法伪造的一段数字串,类似于写在纸上的普通物理签名。

2、原理

3、生成数字签名