MD5 算法


参考:Java 安全加密算法 - MD5,SHA256,SHA512,PBKDF2,BCrypt,SCrypt

消息摘要算法(Message-Digest Algorithm 5 ,MD5)是一种 密码散列函数
返回一个128 bit(16 bytes)的 散列值

  • 消息(message):要编码的密码 就称为消息。
  • 摘要(digest): 生成的散列值称为消息摘要。

openssl 示例

它非常简单直接的基本思想是:
<mark>将 可变长度数据集 映射到 数据集的 固定长度 的。</mark>

# 特点

  1. 数字指纹:输入任意长度的信息,经过处理,输出为128位的信息
  2. 唯一性:不同的输入得到的不同的结果
  3. 不可逆:无法通过密文得到明文
  4. 不抗冲突:这意味着不同的密码最终会导致相同的哈希

# MD5 用途

  1. 防止被篡改:
    用一个文档生成一个MD5结果,之后如果文档被修改,再次生成的MD5结果必然与之前的不同。
    如:Git、SVN等版本控制器

  2. 密码的存储
    由于<mark>不可逆性</mark>,不法分子拿到数据库中的MD5加密有的用户密码,不法分子也无法解析得到密码明文。
    相反,通过<mark>唯一性</mark>,用户输入明文,服务器将明文进行MD5加密,然后跟数据库中的密文对比,能判断用户输入的明文是否正确。

  3. 防止抵赖(数字签名)
    跟防止篡改一个道理。
    第三方机构,通过 记录 文档的 MD5 加密后数据作者(或一些别的东西),形成映射关系。
    当要追究文档责任时,能溯源找回 作者 (或一些别额东西)

# 安全性

  • 当加密内容过于简单或者<mark>有规律可循时</mark>,可以破解。(如生日,纯数字,身份证、电话号码等)
    如:
  • 当加密内容复杂时,(内容范围包含数字、字母大小写和其他字符,并且超过一定长度,如7时)普遍认为是安全的。

# 简单 JAVA 示例

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class Enctypt {
    public static void main(String[] args) {
        // 密码
        final String password = "123456";
        // openssl算出来的 123456 的 MD5 加密结果
        String passwordGenerated = "e10adc3949ba59abbe56e057f20f883e";
        System.out.println(passwordGenerated.equals(Enctypt.MD5(password)));
    }

    public static String MD5(String message) {
        String result = null;
        try {
            // Create MessageDigest instance for MD5
            MessageDigest encoder = MessageDigest.getInstance("MD5");
            // Add password bytes to digest
            byte[] digest = encoder.digest(message.getBytes());
            // This bytes[] has bytes in decimal format;
            // Convert it to hexadecimal format
            StringBuilder sb = new StringBuilder();
            for (byte b : digest) {
                sb.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
            }
            //Get complete hashed password in hex format
            result = sb.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return result;
    }
}

# 为 MD5 添加 salt 示例

考虑在安全性中添加一些 (salt)

维基百科将 salt 定义为 <mark>随机数据</mark> ,用作哈希密码或密码短语的单向函数的附加输入
更简单的说,salt是一些随机生成的文本,在获取哈希值之前会附加到密码中。

salting 的最初意图主要是打败预先计算的彩虹表攻击,否则可以用来大大提高破解密码数据库的效率。现在更大的好处是减慢并行操作,将一次密码猜测的哈希值与多个密码哈希值进行比较。

生成 salt
只要是随机数即可:

  • UUID
  • SHA1PRNG

Java 示例

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;

public class Enctypt {
    public static void main(String[] args) {
        // 密码
        final String password = "123456";
        // openssl算出来的 123456 的 MD5 加密结果(无salt)
        final String passwordGenerated = "e10adc3949ba59abbe56e057f20f883e";
        System.out.println("no salt:" + Enctypt.MD5(password));
        // 利用 UUID 生成 salt
        String salt = UUID.randomUUID().toString();
        System.out.println("salt:" + salt);
        System.out.println("add salt:" + Enctypt.MD5(password, salt));
    }

    public static String MD5(String message) {
        return MD5(message, null);
    }

    public static String MD5(String message, String salt) {
        String result = null;
        try {
            // Create MessageDigest instance for MD5
            MessageDigest encoder = MessageDigest.getInstance("MD5");
            // Add salt bytes to digest
            if (salt != null && !"".equals(salt)) {
                encoder.update(salt.getBytes());
            }
            // Add password bytes to digest
            byte[] digest = encoder.digest(message.getBytes());
            result = parseToString(digest);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return result;
    }

    private static String parseToString(byte[] digest) {
        // This bytes[] has bytes in decimal format;
        // Convert it to hexadecimal format
        StringBuilder sb = new StringBuilder();
        for (byte b : digest) {
            sb.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
        }
        //Get complete hashed password in hex format
        return sb.toString();
    }
}

<mark>注意:如果加了 salt,必须把 salt 和 密文 存储在一起。否则,明文无法再次生成相同的密文</mark>

SHA 算法


安全散列算法(Secure Hash Algorithm,SHA)是 加密散列函数

相同:

<mark>与MD5一样,两者均由MD4导出</mark>,SHA-1和MD5彼此很相似。
相应的,他们的强度和其他特性也是相似:
<mark>结果唯一、不抗冲突不可逆</mark>

不同:
有以下几点不同:

  • 对强行攻击的安全性:最显著和最重要的区别是SHA-1摘要比MD5摘要长32 位。使用强行技术,产生任何一个报文使其摘要等于给定报摘要的难度对MD5是 2^128 数量级的操作,而对SHA-1则是2^160数量级的操作。这样,SHA-1对强行攻击有更大的强度。
  • 速度:在相同的硬件上,SHA-1的运行速度比MD5慢。

Java有4种SHA算法实现。与MD5(128位散列)相比,它们生成以下长度哈希值:

  • SHA-1(160位哈希 - 最简单的一个 )
  • SHA-256(256位散列 - 强于SHA-1)
  • SHA-384(384位哈希 - 强于SHA-256)
  • SHA-512(512位散列 - 强于SHA-384)

<mark>更长的哈希更难以打破。这是核心理念</mark>。

openssl示例

# JAVA 示例

将上面代码的获取实例部分修改即可:

MessageDigest.getInstance("SHA-1");
MessageDigest.getInstance("SHA-256");
... 384
... 512

概念:对称加密算法


参考:常见对称加密算法

看到这里,你应该要知道 对称加密算法 的概念。

对称加密算法 中:

  • 数据发信方将 明文(原始数据) 和加密 密钥(mi yue) 一起经过特殊加密算法处理后,使其变成复杂的加密密文发送出去。
  • 收信方收到密文后,若想解读原文,则需要使用 加密用过的密钥相同算法的逆算法 对密文进行解密,才能使其恢复成可读明文。

在对称加密算法中,<mark>使用的密钥只有一个,发收信双方都使用这个密钥对数据进行加密和解密</mark>,这就要<mark>求解密方事先必须知道加密密钥</mark>。

特点

  • 有三样东西:明文密钥加密&解密算法
  • 密钥只有一个(发收双方持有相同密钥)
  • 解密方需要事先知道 解密算法

优缺点

  • 优点:算法公开、计算量小、加密速度快、加密效率高。
  • 缺点:
    1、使用同样钥匙,安全性得不到保证。
    2、<mark>密钥管理困难(随通信次数,钥匙数量呈几何倍数增长,分布式情况尤为严重!)</mark>

五种对称加密算法

  • DES:已破解,不再安全,基本没有企业在用了
    <mark>但是对称加密算法的基石,具有学习价值,马上讲</mark>
  • DESede(三重DES)
  • AES:最常用的对称加密算法
    <mark>下面下面第二个讲</mark>
  • IDEA:常用的电子邮件加密算法
  • PBE:综合了消息摘要算法和对称加密算法,最常见的是 PBEWithMD5AndDES

DES 算法


参考:视频【⭐️⭐️⭐️】 - DES加密算法-《信息安全技术》课程微课设计

数据加密算法(Data Encryption Standard,DES),它是IBM公司于1975年研究成功并公开发表的。

DES详解(Java实现)

DES 算法(理论部分)


因为这是对称加密的典型案例,所以必要介绍一些理论知识。

# 密码学基础

从本质上来说,DES的安全性依赖于虚假表象,从密码学的术语来讲就是依赖于“混乱和扩散”的原则。

  • 混乱:目的是为隐藏任何明文同密文、或者密钥之间的关系
  • 扩散:目的是使明文中的有效位和密钥一起组成尽可能多的密文。

两者结合到一起就使得安全性变得相对较高。

## Feistel结构

DES详解

# DES 算法基础

  • 分组:DES是一种分组加密算法,该算法每次处理固定长度的数据段,称之为<mark>分组</mark>。
  • 填充:DES分组的大小是64位,如果加密的数据长度不是64位的倍数,可以按照某种具体的规则来<mark>填充</mark>位。
  • 置换:对每个分组

# 一般结构图

其中,

  • 扩展置换(P盒置换)的作用是 扩散(Diffusion) 。 使明文和密钥产生更多的密文
  • S盒压缩(Substitution-box )的作用是 混乱 。隐藏明文和密文的关系

## 初始置换IP(initial permulation)和终止置换IP(final permulation)

初始置换IP(Initial Permutation)的作用在于将64位数据打乱重排,并分成左右两半,供后面的迭代使用。

  • 置换,即对照置换表,一位一位的对数据分组进行置换

  • 初始置换的结果(下图)

  • 终止置换
    终止置换的置换表和初始置换表示互逆的。

<mark>即,通过初始置换和终止置换,各分组的排序不变</mark>

## 扩展置换

  • 经过初始置换的64位数据被分为左右32位数据(下图)
  • 其中一边的32位数据进行扩展置换得到48位数据
  • 具体填充过程如下
    (<mark>其实,就是等分成8块,各自的首尾位置进行了添位操作</mark>)

## S盒压缩处理

上面进行了扩展,这里进行压缩

<mark>在经过扩展的48位明文和48密钥进行异或运算后,就会使用8个S盒压缩处理,得到32位数据</mark>

和扩展过程一样,等分成八份(每份6位),每份压缩为4位

以一份为例,演示压缩过程

AES 算法


参考:AES 加密算法的原理详解
AES详解(Java实现)

<mark>高级加密标准(AES,Advanced Encryption Standard)为最常见的对称加密算法标准</mark>
Rijndael是该标准下的具体算法
(微信小程序加密传输就是用这个加密算法的)

# 与 DES 的异同

  • 名字上看
    Advanced Encryption StandardData Encryption Standard 就差第一个单词,就知道他们关系不简单。
    可以理解:AES就是DES的升级版。

    <mark>相同点:</mark>

    • 均是分块加密(也均是对称加密)
    • 均需要扩展密钥
    • 均需要特定的表对明文进行转换
  • 历史上看
    DES早被破解了(不能对抗差分和线性密码分析),是不安全的。于是出了个三重DES
    三重DES保证了安全性,但效率低。
    于是,才出现了RSA,<mark>既保证安全性、又保证了效率</mark>

  • 分组长度
    DES分组比较短。
    <mark>AES标准支持可变分组长度,分组长度可设定为32比特的任意倍数,最小值为128比特,最大值为256比特。</mark>

另外,RSA很好的抵抗差分密码分析及线性密码分析的能力。

# 加密流程(简述)

加密解密结构(下图)

加密函数(单次加密)的结构(下图)

# 现实应用

实际中,一般是

  • 通过RSA(下面介绍)加密 AES的密钥 ,传输到接收方
  • 接收方解密得到 AES密钥 ,然后发送方和接收方用 AES密钥 来通信。

RSA 算法


参考:李永乐老师11分钟讲RSA加密算法

RSA( Rivest - Shamir - Adleman ),名字来源算法的三位研发者(美国麻 省理工 学院三 位学者 Ron RivestAdi ShamirLeonard Adleman )(左)。

另外,后来资料表明,右边三位大神,最早提出这种算法,但由于工作保密的原因,无法公布。

<mark>这个算法的核心,是基于大素数的数学难题。</mark>

记住两点:

  • <mark>公钥:是大家都能知道的(包括不法分子),用于加密方借助公钥对数据加密</mark>
    (非对称加密的核心就是:加密用的是公钥,而不再是私钥)
  • <mark>私钥:让不法分子知道了,数据就泄漏了,因为能用于对数据的解密</mark>

1 生成公钥(generate public key )

公钥是在一定约束内,随便取的
<mark>公钥 由两部分组成: ne</mark>

  • 得到 公钥 n
    我们选择两个素数(prime) P 和 Q
    通过 PxQ 我们得到 公钥 n

  • 得到 公钥 e
    e 必须是个整数,不能整除 公钥 n ,同时在一个范围内( 1 < e < φ ( n ) 1<e<\varphi (n) 1<e<φ(n)
    <mark>这个 φ ( n ) \varphi (n) φ(n) “phi of n” 我们下面讲</mark>

  • <mark>“phi of n” 的计算公式 : φ ( n ) = ( p 1 ) ( Q 1 ) \varphi (n)= (p-1)(Q-1) φ(n)=(p1)(Q1)</mark>
    是不是和 n 很像?

至此,我们得到:

  • 公开的 : <mark>e</mark> 和 <mark>n</mark>
  • 用于计算的:<mark>P</mark>、<mark>Q</mark>、 <mark> φ ( n ) = ( p 1 ) ( Q 1 ) \varphi (n)= (p-1)(Q-1) φ(n)=(p1)(Q1)</mark>

2 生成私钥(generate private key)

计算私钥,要用到 公钥 e 和 用于计算的 <mark> φ ( n ) = ( p 1 ) ( Q 1 ) \varphi (n)= (p-1)(Q-1) φ(n)=(p1)(Q1)</mark>

私钥 d 的计算如下:

对于这个计算过程,我们只需要知道:
由于之前 对 公钥 的 一些列限值,
这里在计算 私钥 时,><mark>d 肯定是能算开的</mark>(即不存在无限小数)

3 用公钥加密数据(encrypt data with public key)

一堆乱七八糟,各种各样格式的信息,要加密,首先要转成加密方法认可的格式(如数字)

信息加密步骤1:转换

首先,将信息(data)转成(convert)数字形式
【<mark>注意,这不叫加密,只是一种方便处理的信息形式转换</mark>】
方法移异常多,如: ASCII、base64

这里简单把信息:Hi,转换成:89

信息加密步骤2:加密

计算密文,要用到完整的公钥,即: 公钥 n公钥 e

通过(下图)公式:( c = D a t a e m o d    n c=Data^{e}\mod n c=Dataemodn


得到我们的 密文 c (encrypt data)

4 私钥解密数据(decrypt data with private key)

再复杂的加密,都需要能解密
(像MD5那样不可逆的,个人觉得不算完整加密算法,<mark>就像你把盒子用锁锁了,同时把钥匙丢海里,你不能说那把锁本身是把好锁,一个道理</mark>)
RSA 好在在复杂加密的情况下,有方便的解密过程。

这里要用到的参数:
.
加密数据 c私钥 d公钥 n(不需要 公钥 e

通过和加密公式类似的公式:( c = D a t a d m o d    n c=Data^{d}\mod n c=Datadmodn

<mark>和加密公式的区别是,幂次从 公钥 e 变成了 私钥 d 即可</mark>

即可进行解密

5 流程梳理

生成钥匙:

  1. 选取计算因子 P、Q
    同时得到 φ ( n ) = ( p 1 ) ( Q 1 ) \varphi (n)= (p-1)(Q-1) φ(n)=(p1)(Q1)
  2. 发布
    计算出的 公钥 n
    和选取的 公钥 e
  3. 保存
    计算出的 私钥 d

通信:

  1. 公钥 n公钥 e 对数据进行加密
  2. 公钥 n私钥 d 对数据进行解密

# 性能分析:RSA和AES对比

参考:浅析DES与AES、RSA三种典型加密算法的比较

  • 速度方面: AES优
    从 RSA 加密过程中,需要对数据取模可以看出
    (类似具体时间的测试很多)

  • 安全方面:毋庸置疑,非对称加密方式安全性上优于对称加密。

因此,<mark>目前最常用的加密方式采用AES与RSA相结合的应用</mark>,使它们的优缺点正好互补

  • RSA 加密速度慢,安全性好,应用于 AES 密钥的加密 ,传输到接收方,可解决DES 密钥分配的问题。
  • AES 加密速度快,适合加密较长的报文,可用其 加密明文 ,接收方 通过RSA解密出AES密钥,从而解密出加密数据;

目前这种RSA和AES结合的方法已成为EMAIL保密通信标准。

done~