对称加密算法

简介

默认情况下加密模式和填充模式为ECB/PKCS5Padding

使用CBC加密方式,加密、解密都需要创建IV向量。IV向量的长度也必须是特定长度的整数倍。

特点

  • 加密速度快, 可以加密大文件
  • 密文可逆, 一旦密钥文件泄漏, 就会导致数据暴露
  • 加密后编码表找不到对应字符, 出现乱码
  • 一般结合Base64使用,解决乱码的问题

DES

DES : Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。

密钥key必须是8位

原理

DES(Data Encryption Standard)是一种对称加密算法,其原理基于置换和替代。下面是DES加密算法的工作原理:

  1. 密钥生成:DES算法使用固定长度的密钥(56位)进行加密和解密。首先,根据用户提供的密钥,生成16个子密钥,每个子密钥48位长。

  2. 初始置换(Initial Permutation):明文经过一个固定的初始置换IP(Initial Permutation)置换后得到一个64位的数据块。

  3. 轮函数(Round Function):DES算法使用16轮替换-置换网络(Feistel Network)来加密数据。每一轮中,明文的左半部分和右半部分会分别经过一系列的替代和置换操作,并且会使用不同的子密钥。

  4. 替代操作(Substitution):在DES算法中,会使用S盒进行替代操作。S盒是一个4x16的矩阵,将6位输入映射为4位输出。DES算法共有8个不同的S盒。

  5. 置换操作(Permutation):DES算法中还会进行置换操作,将数据重新排列以增加其随机性。

  6. 逆初始置换(Final Permutation):经过16轮的替换和置换操作后,得到的加密结果会经过逆初始置换IP^-1,将64位数据块重新排列并输出。

  7. 密文输出:经过逆初始置换后得到的密文即为加密结果。

总的来说,DES算法通过多轮的替换和置换操作来加密数据,并且通过密钥生成子密钥来增加加密的随机性。虽然DES算法曾经是比较安全的加密算法,但由于密钥长度较短,目前已经不适合用于加密敏感数据,因此已经被更高级的加密算法如AES所取代。

Java调用(DES/ECB/PKCS5Padding)

package code;

import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

public class DESDemo {
    // DES加密算法,key的大小必须是8个字节

    public static void main(String[] args) throws Exception {
        String input = "硅谷";
        // DES加密算法,比较高级,所以key的大小必须是8个字节
        String key = "12345678";

        String transformation = "DES/ECB/PKCS5Padding"; // 加密算法/加密模式/填充方式
        // 指定获取密钥的算法
        String algorithm = "DES";
        // 先测试加密,然后在测试解密
        String encryptDES = encryptDES(input, key, transformation, algorithm);
        System.out.println("加密:" + encryptDES);
        String s = decryptDES(encryptDES, key, transformation, algorithm);
        System.out.println("解密:" + s);

    }

    /**
     * 使用DES加密数据
     *
     * @param input          : 原文
     * @param key            : 密钥(DES, 密钥的长度必须是16个字节)
     * @param transformation : 获取Cipher对象的算法
     * @param algorithm      : 获取密钥的算法
     * @return : 密文
     * @throws Exception
     */
    private static String encryptDES(String input, String key, String transformation, String algorithm) throws Exception {
        // 获取加密对象
        Cipher cipher = Cipher.getInstance(transformation);
        // 创建加密规则
        SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
        // ENCRYPT_MODE:加密模式
        // DECRYPT_MODE: 解密模式
        // 初始化加密模式和算法
        cipher.init(Cipher.ENCRYPT_MODE, sks);
        // 加密
        byte[] bytes = cipher.doFinal(input.getBytes());

        // 输出加密后的数据

        return Base64.getEncoder().encodeToString(bytes);
    }

    /**
     * 使用DES解密
     *
     * @param input          : 密文
     * @param key            : 密钥
     * @param transformation : 获取Cipher对象的算法
     * @param algorithm      : 获取密钥的算法
     * @return: 原文
     * @throws Exception
     */
    private static String decryptDES(String input, String key, String transformation, String algorithm) throws Exception {
        // 获取Cipher对象
        Cipher cipher = Cipher.getInstance(transformation);
        // 指定密钥规则
        SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
        cipher.init(Cipher.DECRYPT_MODE, sks);
        // 解密
        byte[] bytes = cipher.doFinal(Base64.getDecoder().decode(input));

        return new String(bytes);
    }
}

输出结果如下:

加密:qANksk5lvqM=
解密:硅谷

Python调用

在Python中使用DES加密可以使用pycryptodome库。以下是一个用DES算法加密字符串“打倒老大哥”并进行解密的示例代码:

from Crypto.Cipher import DES
from Crypto.Random import get_random_bytes
import base64

def encrypt_text(text):
    key = b'abcdefgh'  # DES密钥必须是8个字节
    cipher = DES.new(key, DES.MODE_ECB)
    padded_data = text.ljust(8 * (len(text) // 8 + 1), ' ')  # 补齐数据长度为8的倍数
    ciphertext = cipher.encrypt(padded_data.encode('utf-8'))
    encrypted_text = base64.b64encode(ciphertext).decode('utf-8')
    return encrypted_text

def decrypt_text(encrypted_text):
    key = b'abcdefgh'  # DES密钥必须是8个字节
    cipher = DES.new(key, DES.MODE_ECB)
    ciphertext = base64.b64decode(encrypted_text.encode('utf-8'))
    decrypted_data = cipher.decrypt(ciphertext).decode('utf-8').strip()
    return decrypted_data

text = "打倒老大哥"
encrypted_text = encrypt_text(text)
print("加密后的字符串:", encrypted_text)

decrypted_text = decrypt_text(encrypted_text)
print("解密后的字符串:", decrypted_text)

在这个示例代码中,我们使用了DES算法,并指定了ECB模式。首先定义了DES加密的密钥key,并创建一个DES加密对象cipher。然后对要加密的明文补齐至8的倍数长度,使用密钥对明文进行加密,再将加密后的密文进行Base64编码以便于输出。

运行以上代码,将会先输出加密后的密文,然后再输出解密后的原始明文,即可完成先加密后解密的过程。请注意,在实际应用中,需要注意密文传输的安全性和确保密钥只有合法的用户能够访问。

AES

AES : Advanced Encryption Standard, 高级加密标准 .在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。

密钥key必须是16位

原理

AES(Advanced Encryption Standard)是一种对称加密算法,其原理基于替代和置换。下面是AES加密算法的工作原理:

  1. 密钥扩展(Key Expansion):AES算法使用固定长度的密钥(128位、192位或256位)来进行加密和解密。在加密和解密过程中,需要生成轮密钥(Round Key)。轮密钥是从初始密钥生成的一系列密钥,每一轮加密或解密过程都使用不同的轮密钥。密钥扩展算法根据初始密钥生成足够的轮密钥。

  2. 初始轮(Initial Round):AES算法首先使用初始轮密钥对明文进行初始处理。初始轮包括字节替换(SubBytes)、行移位(ShiftRows)、列混淆(MixColumns)和轮密钥加(AddRoundKey)。

  3. 多轮加密(Rounds):AES算法使用多轮加密来保障数据安全。每一轮加密包括字节替换、行移位、列混淆和轮密钥加这四个步骤。加密轮数取决于密钥长度,128位密钥需要10轮,192位密钥需要12轮,256位密钥需要14轮。

  4. 最终轮(Final Round):在最后一轮加密中,不执行列混淆步骤。最后一轮只包括字节替换、行移位和轮密钥加这三个步骤。

  5. 密文输出(Cipher Text):经过多轮加密后,明文被转换成密文。最后输出的密文即为加密结果。

总的来说,AES算法是通过多轮的替代和置换操作来保证数据的安全性。密钥长度的不同会导致加密的轮数、轮密钥数量的不同,从而增加了加密的强度和安全性。AES算法是目前最为常用和安全的加密算法之一。

Java实现(AES/ECB/PKCS5Padding)

package code;

import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

public class AESDemo {

    public static void main(String[] args) throws Exception {
        String input = "硅谷";
        // AES加密算法,比较高级,所以key的大小必须是16个字节
        String key = "1234567812345678";

        String transformation = "AES/ECB/PKCS5Padding"; // 加密算法/加密模式/填充方式
        // 指定获取密钥的算法
        String algorithm = "AES";
        // 先测试加密,然后在测试解密
        String encryptAES = encryptAES(input, key, transformation, algorithm);
        System.out.println("加密:" + encryptAES);
        String s = decryptAES(encryptAES, key, transformation, algorithm);
        System.out.println("解密:" + s);

    }

    /**
     * 使用AES加密数据
     *
     * @param input          : 原文
     * @param key            : 密钥(AES, 密钥的长度必须是16个字节)
     * @param transformation : 获取Cipher对象的算法
     * @param algorithm      : 获取密钥的算法
     * @return : 密文
     * @throws Exception
     */
    private static String encryptAES(String input, String key, String transformation, String algorithm) throws Exception {
        // 获取加密对象
        Cipher cipher = Cipher.getInstance(transformation);
        // 创建加密规则
        SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
        // ENCRYPT_MODE:加密模式
        // DECRYPT_MODE: 解密模式
        // 初始化加密模式和算法
        cipher.init(Cipher.ENCRYPT_MODE, sks);
        // 加密
        byte[] bytes = cipher.doFinal(input.getBytes());

        // 输出加密后的数据  

        return Base64.getEncoder().encodeToString(bytes);
    }

    /**
     * 使用AES解密
     *
     * @param input          : 密文
     * @param key            : 密钥
     * @param transformation : 获取Cipher对象的算法
     * @param algorithm      : 获取密钥的算法
     * @return: 原文
     * @throws Exception
     */
    private static String decryptAES(String input, String key, String transformation, String algorithm) throws Exception {
        // 获取Cipher对象
        Cipher cipher = Cipher.getInstance(transformation);
        // 指定密钥规则
        SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
        cipher.init(Cipher.DECRYPT_MODE, sks);
        // 解密
        byte[] bytes = cipher.doFinal(Base64.getDecoder().decode(input));

        return new String(bytes);
    }
}

输出结果如下:

加密:LIdHYmgk7CfTDY/pvc/SlQ==
解密:硅谷

Python实现

在Python中使用AES加密可以通过pycryptodome库来实现。

要先对字符串进行加密,然后再对加密后的密文进行解密,可以修改上面的代码稍作调整。下面是一个完整的示例代码:

from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
import base64

def encrypt_text(text):
    key = b'Sixteen byte key'  # AES密钥必须是16个字节(128位)
    cipher = AES.new(key, AES.MODE_ECB)
    padded_data = text.ljust(16 * (len(text) // 16 + 1), ' ')  # 补齐数据长度为16的倍数
    ciphertext = cipher.encrypt(padded_data.encode('utf-8'))
    encrypted_text = base64.b64encode(ciphertext).decode('utf-8')
    return encrypted_text

def decrypt_text(encrypted_text):
    key = b'Sixteen byte key'  # AES密钥必须是16个字节(128位)
    cipher = AES.new(key, AES.MODE_ECB)
    ciphertext = base64.b64decode(encrypted_text.encode('utf-8'))
    decrypted_data = cipher.decrypt(ciphertext).decode('utf-8').strip()
    return decrypted_data

text = "打倒老大哥"
encrypted_text = encrypt_text(text)
print("加密后的字符串:", encrypted_text)

decrypted_text = decrypt_text(encrypted_text)
print("解密后的字符串:", decrypted_text)

在这个示例代码中,我们添加了一个decrypt_text函数来实现对加密后的密文进行解密。在解密函数中,我们同样需要提供AES加密时使用的密钥,并使用相同的AES对象对密文进行解密操作,最后得到原始的明文。

运行以上代码,将会先输出加密后的密文,然后再输出解密后的原始明文,即可完成先加密后解密的过程。请注意,在实际应用中,需要注意密文传输的安全性和确保密钥只有合法的用户能够访问。

加密方式

前面DES和AES都有key,而加密模式是说怎么用key。

ECB是同key并行,CBC是变key串行

对称加密中的加密模式有很多种,其中比较常见的有 ECB(Electronic Codebook)、CBC(Cipher Block Chaining)、CFB(Cipher Feedback)、OFB(Output Feedback)、CTR(Counter)等。

  1. ECB(Electronic Codebook)模式:

ECB 是最简单的加密模式,将明文分成固定大小的块,然后每个块独立加密。虽然简单易实现,但由于每个块独立加密,导致相同明文块会得到相同的密文块,存在明显的安全问题。

  1. CBC(Cipher Block Chaining)模式:

CBC 是一种常见的加密模式,是将前一个加密块的密文和当前明文块进行异或操作,再进行加密。每个块的加密依赖于前一个块的密文,避免了 ECB 模式的问题,并提高了安全性。

  1. CFB(Cipher Feedback)模式:

CFB 模式将密文作为输入反馈到密码算法中生成密文块,再与明文块异或生成最终的密文。CFB 模式可以将块密码转换为流密码使用,适合于需要实时传输的数据加密。

  1. OFB(Output Feedback)模式:

OFB 模式将前一个加密块输出作为输入反馈到密码算法中生成密钥流,再与明文异或得到密文。OFB 模式不依赖于前一个块的密文,因此可以提高并行加密的速度。

  1. CTR(Counter)模式:

CTR 模式通过一个计数器和一个初始化向量生成密钥流,再与明文异或得到密文。CTR 模式避免了与前一个块的依赖关系,并可以实现高效的并行加密。

这些是常见的对称加密模式,每种模式都有自己的特点和适用场景,选择合适的加密模式可以提高数据的安全性和性能。

ECB(Electronic codebook)

电子密码本按照密码块的大小把需要加密的消息分为数个块,然后使用同一个key对每个块分别加密。

优点:可以并行处理数据

缺点:同样的原文生成同样的密文,不能很好地保护数据

同时加密,原文一样则密文也一样。

CBC(Cipher-block chaining)

密码块链接,每个明文块都与前一个密文块进行异或运算后,再使用同一个key进行加密。在这种方法中,每个密文块都依赖于它前面的所有明文块。

优点:同样的原文生成的密文不一样

缺点:串行处理数据,速度较慢

填充模式

对称加密中的填充模式是为了解决明文长度不满足加密算法块大小要求时的处理方式。常见的填充模式有 PKCS#5/PKCS#7 填充、ISO7816-4 填充、Zero 填充和ANSI X.923 填充等。

  1. PKCS#5/PKCS#7 填充:

PKCS#5 和 PKCS#7 填充是较为常见的填充模式,它们在加密前将明文填充到加密块的大小,填充内容是填充的字节数。PKCS#5 不允许填充长度为块大小,而 PKCS#7 需要填充块大小的长度。在解密时,根据最后一个字节的填充长度可以准确去除填充。

  1. ISO7816-4 填充:

ISO7816-4 填充是一种简单的填充模式,将剩余需要填充的字节数用 0x80 填充,剩余的位置用 0x00 填充。在解密时,找到第一个 0x80 并移除,后面的 0x00 即为填充部分。

  1. Zero 填充:

Zero 填充是一种填充模式,将剩余需要填充的字节数用 0x00 填充。在解密时就需要遍历找到非 0 字节,判断是否是填充部分并去除。

  1. ANSI X.923 填充:

ANSI X.923 填充是一种填充模式,将剩余需要填充的字节数用 0x00 填充,最后一个字节记录填充的长度。在解密时,根据最后一个字节的填充长度可以准确去除填充。

以上是常见的对称加密中的填充模式,选择合适的填充模式可以保证数据在加密后正确性和完整性,并避免数据截断的问题。不同的填充模式适用于不同的场景和系统要求,需要根据具体需求选择合适的填充模式。

NoPadding

不填充。

使用DES加密算法时,要求原文长度必须是8byte的整数倍

使用AES加密算法时,要求原文长度必须是16byte的整数倍

PKCS5Padding

数据块大小为8位,不够就补足。