加密与安全_AES & RSA 密钥对生成及PEM格式的代码实现

2024-05-27 08:13:53 浏览数 (2)

RSA(非对称)和AES(对称)加密算法

在现代信息安全中,加密算法扮演着至关重要的角色。今天我们来聊聊两种常见的加密算法——RSA和AES,用通俗易懂的语言带大家理解它们的核心原理和优缺点。

一、RSA(Rivest-Shamir-Adleman)

核心原理: RSA是一种非对称加密算法,这意味着它使用一对密钥:公钥和私钥。通俗来说,公钥可以公开,用来加密信息;而私钥需要保密,用来解密信息。它的安全性主要依赖于一个数学难题:大整数的质因数分解。具体步骤如下:

  1. 选择质数:选择两个大质数p和q。
  2. 计算乘积:计算p和q的乘积n = p * q,这就是公钥的一部分。
  3. 计算欧拉函数:计算欧拉函数φ(n) = (p-1)*(q-1)。
  4. 选择公钥指数:选择一个小于φ(n)的整数e,且e与φ(n)互质,这就是公钥的另一部分。
  5. 计算私钥指数:计算e对于φ(n)的模反元素d,这就是私钥。

优点:

  • 安全性高:由于大整数的质因数分解非常困难,破解RSA几乎不可能。
  • 公钥加密:公钥可以公开分享,方便信息的安全传输。

缺点:

  • 速度慢:加密和解密过程计算复杂,速度较慢。
  • 密钥长度大:需要较长的密钥(通常为2048位或以上)来保证安全性,存储和处理成本高。

二、AES(Advanced Encryption Standard)

核心原理: AES是一种对称加密算法,这意味着它使用同一个密钥进行加密和解密。它基于一种叫做“分组密码”的方法,把数据分成固定大小的块(通常是128位),然后通过多个轮次的处理来加密数据。每轮处理包括以下步骤:

  1. 替代(Substitution):使用预先定义的S盒对数据进行替换。
  2. 换位(Permutation):重新排列数据的顺序。
  3. 混淆(Mixing):对数据进行复杂的数学运算混合。
  4. 轮密钥加(Add Round Key):与轮密钥进行异或运算。

优点:

  • 速度快:由于AES算法的设计简单且高效,处理速度快,适合大数据量的加密。
  • 安全性高:AES算法在多年的使用中,未被成功破解,具有很高的安全性。

缺点:

  • 密钥管理复杂:对称加密要求通信双方事先共享同一个密钥,密钥的分发和管理较为复杂。
  • 密钥泄露风险:一旦密钥泄露,所有加密的数据都会暴露,因此密钥保护非常重要。

RSA和AES是现代密码学中两种重要的加密算法,各有优缺点。RSA依赖于复杂的数学难题,安全性高但速度较慢;AES则以其高效的加密速度和广泛应用而著称,但在密钥管理上存在挑战。

RSA加密三种填充模式

RSA加密算法在实际应用中常常使用填充模式来确保数据的安全性和算法的有效性。

填充模式是为了使加密数据和公钥长度一致,并增加加密的安全性。

接下来我们看下主要的RSA填充模式:ENCRYPTION_OAEPENCRYPTION_PKCS1ENCRYPTION_NONE,以及常见的填充模式组合。

一、RSA填充模式

1. ENCRYPTION_OAEP(Optimal Asymmetric Encryption Padding)

  • 简介:OAEP是目前最安全的RSA填充模式,广泛推荐用于现代加密应用中。
  • 原理:OAEP在加密数据前,先通过一个哈希函数和一个掩码生成函数(MGF)对数据进行填充,确保每次加密的结果都不同,即使相同的数据和密钥也不会产生相同的密文。
  • 优点:高安全性,防止多种已知攻击(如选择密文攻击)。
  • 使用场景:需要高度安全性的数据加密和解密操作。

2. ENCRYPTION_PKCS1(PKCS #1 v1.5 Padding)

  • 简介:PKCS1是RSA加密最常用的填充模式之一,因其随机填充的特性,确保相同数据每次加密结果不同。
  • 原理:PKCS1填充在数据前面添加一个随机填充字符串,并确保数据长度和密钥长度一致。
  • 优点:较为简单,已经广泛使用和支持。
  • 缺点:相对于OAEP,安全性略低,可能受到一定类型的攻击(如选择密文攻击)。
  • 使用场景:一般数据加密,广泛应用于SSL/TLS协议中。

3. ENCRYPTION_NONE(No Padding)

  • 简介:无填充模式,直接对数据进行加密。
  • 原理:数据长度必须与密钥长度相同,不进行任何额外的填充处理。
  • 优点:实现简单。
  • 缺点:安全性较低,易受各种攻击,不推荐使用。
  • 使用场景:通常仅在特定条件下或内部使用。

二、常见的RSA填充模式组合

1. RSA/None/PKCS1Padding

  • 简介:该模式表示没有指定具体的块加密模式(None),使用PKCS1填充。
  • 特点:确保相同数据每次加密结果不同,适用于许多常见的加密场景。

2. RSA/ECB/PKCS1Padding

  • 简介:该模式表示使用电子密码本(ECB)模式进行加密,并使用PKCS1填充。
  • 特点:在每个块中独立加密,但ECB模式本身不推荐用于大数据量的加密,因为相同的明文块会被加密成相同的密文块,这可能导致模式泄露问题。
  • 适用场景:一般数据加密,广泛应用于加密协议中。

三、选择合适的填充模式

选择合适的填充模式需要根据具体应用场景来考虑:

  1. 高安全性需求:推荐使用ENCRYPTION_OAEP填充模式,尤其是在敏感数据的加密和解密中。
  2. 广泛兼容性:ENCRYPTION_PKCS1是一个较好的选择,已经被广泛支持和使用,适用于大多数应用场景。
  3. 实验性或内部使用:ENCRYPTION_NONE模式仅在特定情况下使用,需确保其他安全措施到位。

四、总结

RSA加密算法的填充模式是确保数据安全性和算法有效性的关键。ENCRYPTION_OAEP、ENCRYPTION_PKCS1和ENCRYPTION_NONE各有优缺点和适用场景。在实际应用中,根据具体需求选择合适的填充模式,能够有效提升加密的安全性和性能。


AES的基本原理、工作模式、填充机制以及密钥和初始化向量(IV)的使用

AES(Advanced Encryption Standard)是一种广泛使用的对称加密算法,旨在替代原先的DES和3DES。AES凭借其高效的加密速度和强大的安全性,成为现代数据加密的首选。

接下来我们来看下AES的基本原理、工作模式、填充机制以及密钥和初始化向量(IV)的使用。

一、AES简介

主要特点:

  • 对称加密:同一个密钥用于加密和解密。
  • 块加密:以固定大小的块(128位)对数据进行加密。
  • 高效性:比公钥加密算法快很多,适用于需要高性能的数据加密场景。

主要缺点:

  • 密钥管理:需要加密端和解密端都使用相同的密钥,密钥分发和管理较为复杂。

二、AES加密解密过程

AES加密需要:

  • 明文:需要加密的数据。
  • 密钥(Key):用于加密和解密的密钥,长度可以是128位、192位或256位。
  • 偏移量(IV):初始化向量,用于将加密随机化,提高安全性。
  • 密码模式:算法/模式/填充,如AES/CBC/PKCS5Padding。

AES解密需要:

  • 密文:已加密的数据。
  • 密钥(Key):与加密时使用的密钥相同。
  • 偏移量(IV):与加密时使用的初始化向量相同。
  • 密码模式:算法/模式/填充,如AES/CBC/PKCS5Padding。

三、AES常见的工作模式

1. 电码本模式(ECB)

  • 特点:每个数据块独立加密,相同的明文块会被加密成相同的密文块。
  • 优点:实现简单,不需要初始化向量。
  • 缺点:不安全,容易受到模式泄露攻击。
  • 使用场景:不推荐在安全需求高的场景下使用。

2. 密码分组链接模式(CBC)

  • 特点:每个明文块与前一个密文块进行异或运算后再加密,第一个块需要初始化向量(IV)。
  • 优点:安全性高,每个块的加密结果都不同。
  • 使用场景:广泛用于文件加密和数据传输加密。

3. 计算器模式(CTR)

  • 特点:将一个计数器的输出与明文块进行异或运算,实现加密。
  • 优点:可并行处理,提高加密速度。
  • 使用场景:适用于流式数据加密和高性能需求的场景。

4. 密码反馈模式(CFB)

  • 特点:前一个密文块作为下一块的输入,结合初始化向量(IV)提高安全性。
  • 优点:适用于需要逐字节或逐位加密的场景。
  • 使用场景:实时数据加密,如网络数据流。

5. 输出反馈模式(OFB)

  • 特点:将初始化向量与密钥结合,通过反馈机制生成加密序列。
  • 优点:加密过程独立于明文块的内容。
  • 使用场景:适用于逐位加密和需要抗噪声的场景。

四、AES填充模式

填充模式的必要性: 由于AES是一种块加密算法,处理的数据块必须是固定长度(128位),因此需要填充模式来确保数据长度符合要求。

常见填充模式:

  • PKCS7:在数据末尾添加一系列字节,每个字节的值表示填充的字节数,广泛使用,兼容性好。
  • None:不进行填充,要求输入数据的长度必须是块长度的整数倍。

五、AES密钥和初始化向量管理

密钥(Key):

  • AES标准规定的密钥长度为128位、192位和256位,分别对应16字节、24字节和32字节。
  • 密钥不能公开传输,需要安全地管理和保护。

初始化向量(IV):

  • IV用于将加密随机化,确保相同的明文被多次加密产生不同的密文。
  • IV可以公开,但不能重复使用。推荐每次加密时生成一个新的16字节随机值。
  • 在加密端将IV和密文一起发送给解密端,确保解密端能够正确还原数据。

RSA 生成 Code

RSA加密算法在Java中有多种实现方式,其中默认的实现方式是RSA/None/PKCS1Padding。在实际应用中,为了确保安全性和兼容性,需要注意密钥长度、密钥格式以及密钥管理。

一、Java中的RSA加密实现

默认实现:RSA/None/PKCS1Padding

  • RSA:表示使用RSA算法进行加密。
  • None:没有指定具体的块加密模式。
  • PKCS1Padding:使用PKCS#1 v1.5填充模式,这是常见的RSA填充方式。

为什么选择PKCS1Padding?

PKCS1Padding是一种较为常用且广泛支持的填充方式,它通过添加随机填充数据,确保相同的数据每次加密结果不同,从而提高安全性。

二、选择合适的密钥长度

推荐使用2048位或更长的密钥

  • 1024位密钥:在现代计算能力下已经不再安全,容易受到攻击。
  • 2048位或更长的密钥:提供更高的安全性,推荐使用2048位或4096位的密钥,以应对未来更强的攻击手段。

创建RSA密钥对

代码语言:javascript复制
 package com.artisan.jasypt.rsa;

import java.io.UnsupportedEncodingException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Base64;

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class RsaTest {

    /**
     * 主程序入口,用于演示如何生成和打印RSA密钥对的公钥和私钥。
     *
     * @param args 命令行参数(未使用)
     * @throws UnsupportedEncodingException 如果编码不被支持
     */
    public static void main(String[] args) throws UnsupportedEncodingException {

        // 生成2048位的RSA密钥对
        KeyPair keyPair = getKeyPair(2048);
        // 获取公钥
        byte[] publicKey = getPublicKey(keyPair);
        // 获取私钥
        byte[] privateKey = getPrivateKey(keyPair);

        // 打印编码后的公钥和私钥
        System.out.println("Private Key: "   Base64.getEncoder().encodeToString(publicKey));
        System.out.println("Public Key: "   Base64.getEncoder().encodeToString(privateKey));
    }


    /**
     * 生成RSA算法的密钥对
     * @param keyLength 密钥长度,用于初始化密钥生成器
     * @return 生成的密钥对,包含公钥和私钥
     */
    public static KeyPair getKeyPair(int keyLength) {
        try {
            // 实例化密钥对生成器,并指定算法为RSA
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            // 初始化密钥对生成器,设置密钥长度
            keyPairGenerator.initialize(keyLength);
            // 生成密钥对
            return keyPairGenerator.generateKeyPair();
        } catch (NoSuchAlgorithmException e) {
            // 当指定的加密算法不可用时,抛出运行时异常
            throw new RuntimeException("生成密钥对时遇到异常"    e.getMessage());
        }
    }


    /**
     * 获取公钥
     *
     * @param keyPair 包含公钥和私钥的密钥对
     * @return 公钥的字节数组形式
     */
    public static byte[] getPublicKey(KeyPair keyPair) {
        // 将密钥对中的公钥转换为RSAPublicKey类型
        RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
        // 获取并返回公钥的编码形式
        return rsaPublicKey.getEncoded();
    }


    /**
     * 获取私钥
     *
     * @param keyPair 包含公钥和私钥的密钥对
     * @return 返回私钥的字节数组形式
     */
    public static byte[] getPrivateKey(KeyPair keyPair) {
        // 将keyPair中的私钥转换为RSAPrivateKey类型
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
        // 返回私钥的编码形式
        return rsaPrivateKey.getEncoded();
    }

}
代码语言:javascript复制
 package com.artisan.jasypt.rsa;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;

/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class RSAKeyPairGenerator {
    /**
     * 主函数用于生成RSA算法的密钥对,并打印出其Base64编码的字符串形式。
     *
     * @param args 命令行参数(未使用)
     * @throws NoSuchAlgorithmException 如果指定的加密算法不可用,则抛出此异常。
     */
    public static void main(String[] args) throws NoSuchAlgorithmException {
        // 创建RSA算法的密钥对生成器
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");

        // 初始化密钥对生成器,设置密钥长度为2048位
        keyPairGen.initialize(2048);

        // 生成密钥对
        KeyPair pair = keyPairGen.generateKeyPair();

        // 从密钥对中提取私钥和公钥
        PrivateKey privateKey = pair.getPrivate();
        PublicKey publicKey = pair.getPublic();

        // 打印出私钥和公钥的Base64编码
        System.out.println("Private Key: "   Base64.getEncoder().encodeToString(privateKey.getEncoded()));
        System.out.println("Public Key: "   Base64.getEncoder().encodeToString(publicKey.getEncoded()));
    }

}

三、密钥的管理和存储

密钥生成与存储的流程

  • 服务器创建密钥对:服务器负责生成RSA密钥对,确保私钥的安全存储。
  • 公钥下发至客户端:将公钥分发给需要加密数据的客户端。
  • 私钥保存在服务器:私钥必须保密,通常存储在服务器上,确保其不被泄露。

密钥格式:DER和PEM

  • DER(Distinguished Encoding Rules):二进制格式,主要用于机器处理,不便于阅读。
  • PEM(Privacy-Enhanced Mail):将DER格式通过Base64编码转换为字符格式,更易于阅读和传输。

PEM格式的密钥示例

代码语言:javascript复制
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr8p1oxcLljRG/Qffkh6N
...
-----END PUBLIC KEY-----

-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCr8p1oxcLljRG/
...
-----END PRIVATE KEY-----

Code: 将密钥转换为PEM格式
代码语言:javascript复制
 package com.artisan.jasypt.rsa;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
/**
 * @author 小工匠
 * @version 1.0
 * @mark: show me the code , change the world
 */
public class KeyToPEM {

    /**
     * 主函数:生成RSA密钥对,并将其转换为PEM格式输出。
     *
     * @param args 命令行参数(未使用)
     * @throws Exception 如果密钥生成或转换过程中发生错误
     */
    public static void main(String[] args) throws Exception {
        // 生成RSA密钥对,使用2048位长度
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
        keyGen.initialize(2048);
        KeyPair keyPair = keyGen.generateKeyPair();

        // 提取公钥和私钥
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();

        // 将公钥和私钥转换为PEM格式
        String publicKeyPEM = convertToPEMFormat(publicKey.getEncoded(), "PUBLIC KEY");
        String privateKeyPEM = convertToPEMFormat(privateKey.getEncoded(), "PRIVATE KEY");

        // 输出转换后的PEM格式公钥和私钥
        System.out.println(publicKeyPEM);
        System.out.println(privateKeyPEM);
    }


    /**
     * 将密钥字节数组转换为PEM格式的字符串。
     * PEM(Privacy Enhanced Mail)格式是一种常见的密钥存储格式,以 base64 编码的密钥数据为主要内容,并以“-----BEGIN”和“-----END”为标记。
     *
     * @param key 密钥的字节数组。
     * @param keyType 密钥的类型(如RSA PRIVATE KEY等)。
     * @return 转换后的PEM格式密钥字符串。
     */
    public static String convertToPEMFormat(byte[] key, String keyType) {
        // 将密钥字节数组转换为Base64编码的字符串
        String base64EncodedKey = Base64.getEncoder().encodeToString(key);
        StringBuilder pemKey = new StringBuilder();

        // 添加PEM格式的起始标记
        pemKey.append("-----BEGIN ").append(keyType).append("-----n");

        // 将Base64编码的密钥拆分为64字符一组,并添加换行符
        for (int i = 0; i < base64EncodedKey.length(); i  = 64) {
            pemKey.append(base64EncodedKey, i, Math.min(i   64, base64EncodedKey.length())).append("n");
        }

        // 添加PEM格式的结束标记
        pemKey.append("-----END ").append(keyType).append("-----");

        return pemKey.toString();
    }
}

四、总结

RSA加密在Java中的默认实现是RSA/None/PKCS1Padding,推荐使用2048位或更长的密钥以确保安全。密钥管理是保证加密系统安全的重要环节,私钥应保存在服务器上,公钥分发给客户端。PEM格式的密钥更易于阅读和传输,通常用于存储和交换密钥。

0 人点赞