密码学算法新手入门教程

密码学算法新手入门教程

导出为图片

总览:加密、摘要与编码

在进入具体算法之前,我们必须先分清三个核心概念:

概念 核心目的 类比 是否可逆 本文涉及的算法
编码 (Encoding) 将数据从一种形式转换为另一种形式,以便于传输或处理。不为保密 翻译官。把中文翻译成英文,内容不变,只是换了种表达方式。 可逆 (知道规则就能转回来) Base64
摘要/哈希 (Hashing) 为数据生成一个固定长度的、唯一的"指纹"。用于校验数据完整性 榨汁机。把苹果放进去变成苹果汁,你无法从果汁变回完整的苹果。 不可逆 MD5 (以及更安全的 SHA 系列)
加密 (Encryption) 通过"密钥"将明文数据变成看不懂的密文,目的是保护数据机密性 带锁的日记本。只有拿对钥匙的人才能打开看内容。 可逆 (有密钥就能解密) RSA, SM2, SM4

Base64: 编码而非加密

是什么 (What is it?)

Base64 是一种将任意二进制数据转换为由 64 个可打印字符(A-Z, a-z, 0-9, +, /)组成的文本字符串的编码方法。它就像一位"翻译官",负责把计算机内部的 0101 二进制数据"翻译"成纯文本,以便在那些只支持文本的环境中(如 URL、JSON、邮件正文)安全地传输。

核心要点: Base64 是公开的编码规则,任何人都可以解码。它不提供任何保密性,不是加密!

能干嘛 (What can it do?)

  • 在 URL 中传递二进制数据(如小图片)。
  • 在 JSON 或 XML 文件中嵌入二进制内容。
  • 作为数据 URI (Data URI scheme) 在网页中直接嵌入图片,减少 HTTP 请求。

怎么玩 (How to play)

Java 8 及以上版本内置了强大的 Base64 工具类,无需任何第三方库。

Java Demo: 使用 java.util.Base64 复制

java 复制代码
import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class Base64Example {
    public static void main(String[] args) {
        String originalInput = "Hello, Wireshark, RSA, MD5, and SM!";

        // 编码 (Encode)
        String encodedString = Base64.getEncoder().encodeToString(originalInput.getBytes(StandardCharsets.UTF_8));
        System.out.println("原始字符串: " + originalInput);
        System.out.println("Base64 编码后: " + encodedString);

        // 解码 (Decode)
        byte[] decodedBytes = Base64.getDecoder().decode(encodedString);
        String decodedString = new String(decodedBytes, StandardCharsets.UTF_8);
        System.out.println("Base64 解码后: " + decodedString);
    }
}

原始字符串: Hello, Wireshark, RSA, MD5, and SM!

Base64 编码后: SGVsbG8sIFdpcmVzaGFyaywgUlNBLCBNRDUgYW5kIFNNIQ==

Base64 解码后: Hello, Wireshark, RSA, MD5, and SM!

小总结

Base64 是一个简单实用的编码工具,用于在文本环境中安全地传输二进制数据。它简单、快速、通用,但切记它与"安全"或"保密"无关。


MD5: 消息摘要 (已不安全)

是什么 (What is it?)

MD5 (Message-Digest Algorithm 5) 是一种消息摘要算法,也叫哈希函数。它能将任意长度的数据,通过一个不可逆的"搅拌"过程,生成一个固定为 128 位(通常表示为 32 个十六进制字符)的唯一"指纹"或"摘要"。

核心特性:

  • 定长输出: 无论输入多长,输出总是 32 个十六进制字符。
  • 不可逆性: 无法从 MD5 值反推出原始数据。
  • 雪崩效应: 输入数据的任何微小变动,都会导致输出的 MD5 值发生巨大变化。

能干嘛 (What can it do?)

MD5 主要用于校验数据完整性 (Integrity Check)

  • 文件校验: 网站提供软件下载时,通常会附上一个 MD5 值。你下载文件后,在本地计算其 MD5 值,如果与网站提供的一致,说明文件在下载过程中没有损坏或被篡改。
  • 数字签名: 在对数据签名时,通常不是对整个庞大的原文签名,而是对原文的哈希值进行签名,效率更高。

常见的坑和陷阱

严重警告: 由于 MD5 算法已被发现存在"哈希碰撞"(即可以找到两个不同的输入,得到相同的 MD5 值)的严重漏洞,绝对不能再用于任何需要安全性的场景,例如:

  • 密码存储:直接存储用户密码的 MD5 值是极其危险的,容易被彩虹表攻击破解。
  • 代码签名或证书颁发:攻击者可以构造一个恶意文件,使其 MD5 与一个合法文件相同。

对于需要安全哈希的场景,请至少使用 SHA-256 或更强的算法。

怎么玩 (How to play)

Java 的 `java.security.MessageDigest` 类提供了 MD5 功能。

Java Demo: 计算字符串的MD5 复制

java 复制代码
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class MD5Example {
    public static String getMd5(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] messageDigest = md.digest(input.getBytes(StandardCharsets.UTF_8));
            BigInteger no = new BigInteger(1, messageDigest);
            String hashtext = no.toString(16);
            while (hashtext.length() < 32) {
                hashtext = "0" + hashtext;
            }
            return hashtext;
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        String text1 = "Hello, MD5!";
        String text2 = "Hello, MD5."; // 仅一个点不同
        
        System.out.println("MD5 of '" + text1 + "': " + getMd5(text1));
        System.out.println("MD5 of '" + text2 + "': " + getMd5(text2));
    }
}

MD5 of 'Hello, MD5!': 85079b70343a41e212341103b415a776

MD5 of 'Hello, MD5.': 2d4292193433554c63742468351516e8

小总结

MD5 是一个快速的哈希算法,仅适用于非安全领域文件完整性校验 。任何与安全、密码、认证相关的场景都应禁用 MD5,并使用如 SHA-256 等更现代、更安全的算法。


RSA: 非对称加密

是什么 (What is it?)

RSA 是一种非对称加密 算法,是目前应用最广泛的公钥加密算法。它的核心特点是拥有一对数学上相关的密钥:公钥 (Public Key)私钥 (Private Key)

  • 公钥:可以公开给任何人,用于加密数据和验证签名。
  • 私钥:必须由自己妥善保管,绝不泄露,用于解密数据和生成签名。

工作原则: 用公钥加密的数据,只有 对应的私钥才能解密。反之,用私钥签名的数据,只有对应的公钥才能验证。

核心思想原理

RSA 的安全性基于一个数学难题:大整数质因数分解。简单来说,将两个大质数相乘很容易,但要把它们的乘积分解回原来的两个质数,在计算上是极其困难的。这个难题保证了从公钥很难推导出私钥。

graph TD subgraph A[发送方 Alice] A1[明文] --> A2{用 Bob 的公钥加密}; A2 --> A3[密文]; end subgraph B[接收方 Bob] B1[密文] --> B2{用 Bob 自己的私钥解密}; B2 --> B3[明文]; end subgraph C[不可信的信道] A3 -- 通过网络传输 --> B1; end subgraph D[窃听者 Eve] C -- 截获 --> D1[密文]; D1 --> D2{只有 Bob 的公钥, 无法解密}; end

RSA 加密解密流程图

能干嘛 (What can it do?)

  1. 数据加密: 发送方向接收方发送敏感数据时,用接收方的公钥加密,确保只有接收方能用自己的私钥解开。这是 HTTPS 建立安全连接的关键步骤之一。
  2. 数字签名: 发送方为了证明某条消息确实是自己发的,可以用自己的私钥对消息的哈希值进行"签名"。接收方用发送方的公钥来"验证签名",如果验证通过,就能确认消息来源可靠且未被篡改。

怎么玩 (How to play)

Java 的 JCE (Java Cryptography Extension) 框架提供了完整的 RSA 支持。

Java Demo: RSA 加密、解密与签名 复制

java 复制代码
import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;

public class RsaExample {
    public static KeyPair generateKeyPair() throws Exception {
        KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
        generator.initialize(2048, new SecureRandom());
        return generator.generateKeyPair();
    }

    public static String encrypt(String plainText, PublicKey publicKey) throws Exception {
        Cipher encryptCipher = Cipher.getInstance("RSA");
        encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] cipherText = encryptCipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(cipherText);
    }

    public static String decrypt(String cipherText, PrivateKey privateKey) throws Exception {
        byte[] bytes = Base64.getDecoder().decode(cipherText);
        Cipher decriptCipher = Cipher.getInstance("RSA");
        decriptCipher.init(Cipher.DECRYPT_MODE, privateKey);
        return new String(decriptCipher.doFinal(bytes), StandardCharsets.UTF_8);
    }
    
    public static String sign(String plainText, PrivateKey privateKey) throws Exception {
        Signature privateSignature = Signature.getInstance("SHA256withRSA");
        privateSignature.initSign(privateKey);
        privateSignature.update(plainText.getBytes(StandardCharsets.UTF_8));
        byte[] signature = privateSignature.sign();
        return Base64.getEncoder().encodeToString(signature);
    }

    public static boolean verify(String plainText, String signature, PublicKey publicKey) throws Exception {
        Signature publicSignature = Signature.getInstance("SHA256withRSA");
        publicSignature.initVerify(publicKey);
        publicSignature.update(plainText.getBytes(StandardCharsets.UTF_8));
        byte[] signatureBytes = Base64.getDecoder().decode(signature);
        return publicSignature.verify(signatureBytes);
    }
    
    public static void main(String[] args) throws Exception {
        // 1. 生成密钥对
        KeyPair keyPair = generateKeyPair();
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();

        String originalMessage = "This is a secret message for RSA!";
        System.out.println("原始消息: " + originalMessage);

        // 2. 加密与解密
        String encryptedMessage = encrypt(originalMessage, publicKey);
        System.out.println("加密后 (Base64): " + encryptedMessage);
        String decryptedMessage = decrypt(encryptedMessage, privateKey);
        System.out.println("解密后: " + decryptedMessage);
        
        System.out.println("\n--- 数字签名演示 ---");
        // 3. 签名与验签
        String signature = sign(originalMessage, privateKey);
        System.out.println("签名 (Base64): " + signature);
        boolean isVerified = verify(originalMessage, signature, publicKey);
        System.out.println("签名验证结果: " + isVerified);
        
        // 尝试用错误数据验签
        boolean isTamperedVerified = verify("This is a wrong message!", signature, publicKey);
        System.out.println("篡改后数据验签结果: " + isTamperedVerified);
    }
}

原始消息: This is a secret message for RSA!

加密后 (Base64): [一段很长的,每次运行都不同的Base64编码字符串]

解密后: This is a secret message for RSA!

--- 数字签名演示 ---

签名 (Base64): [另一段很长的,每次运行都不同的Base64编码字符串]

签名验证结果: true

篡改后数据验签结果: false

小总结

RSA 是现代密码学的基石。它通过公钥和私钥这对独特的组合,完美地解决了数据机密性 (加密)和身份真实性/数据完整性(签名)两大安全难题。但由于其计算速度较慢,通常不用于加密大文件,而是用于加密对称密钥或进行签名。


国密算法: SM2 & SM4

是什么 (What are they?)

国密算法(SM, 商用密码)是中国国家密码管理局发布的一系列密码标准,旨在实现密码技术的自主可控。SM2 和 SM4 是其中最核心、应用最广的两个。

  • SM4 (对称加密): 对标国际上的 AES 算法,用于高速数据加密。
  • SM2 (非对称加密): 对标国际上的 RSA/ECC 算法,基于椭圆曲线,用于加密和数字签名。

去哪下 (Where to get it?)

Java 原生 JDK 不支持国密算法,必须引入第三方密码学库。目前最主流、最权威的是 Bouncy Castle

Maven Dependency: Bouncy Castle 复制

xml 复制代码
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk18on</artifactId>
    <version>1.78.1</version> <!-- 建议使用最新稳定版 -->
</dependency>

怎么玩 (SM4 对称加密)

SM4 用于高效地加解密大量数据,加密和解密使用同一个密钥。

Java Demo: SM4/ECB/PKCS5Padding 加解密 复制

java 复制代码
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.Security;
import java.util.Base64;

public class SM4Example {
    static {
        // 必须在使用前静态注册BouncyCastle
        Security.addProvider(new BouncyCastleProvider());
    }

    public static void main(String[] args) throws Exception {
        String plainText = "你好,国密SM4对称加密!";

        // 1. 生成 SM4 密钥
        KeyGenerator keyGen = KeyGenerator.getInstance("SM4", "BC");
        keyGen.init(128); // SM4 密钥长度固定为 128 位
        SecretKey secretKey = keyGen.generateKey();
        byte[] keyBytes = secretKey.getEncoded();

        // 2. 加密
        Cipher cipher = Cipher.getInstance("SM4/ECB/PKCS5Padding", "BC");
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, "SM4"));
        byte[] encryptedBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
        String encodedEncrypted = Base64.getEncoder().encodeToString(encryptedBytes);
        System.out.println("SM4 Encrypted: " + encodedEncrypted);

        // 3. 解密
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBytes, "SM4"));
        byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encodedEncrypted));
        System.out.println("SM4 Decrypted: " + new String(decryptedBytes, "UTF-8"));
    }
}

SM4 Encrypted: [一段每次运行都不同的Base64编码字符串]

SM4 Decrypted: 你好,国密SM4对称加密!

怎么玩 (SM2 非对称加密)

SM2 性能优于 RSA,在同等安全强度下密钥更短。同样用于加密和签名。

Java Demo: SM2 加解密 复制

java 复制代码
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import java.security.*;
import java.util.Base64;

public class SM2Example {
    static {
        Security.addProvider(new BouncyCastleProvider());
    }
    public static void main(String[] args) throws Exception {
        String plainText = "你好,国密SM2非对称加密!";

        // 1. 生成SM2密钥对
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("EC", "BC");
        // SM2推荐使用256位椭圆曲线
        keyPairGen.initialize(256);
        KeyPair keyPair = keyPairGen.generateKeyPair();
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();

        // 2. 加密
        Cipher cipher = Cipher.getInstance("SM2", "BC");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] encryptedBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
        String encodedEncrypted = Base64.getEncoder().encodeToString(encryptedBytes);
        System.out.println("SM2 Encrypted: " + encodedEncrypted);

        // 3. 解密
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encodedEncrypted));
        System.out.println("SM2 Decrypted: " + new String(decryptedBytes, "UTF-8"));
    }
}

SM2 Encrypted: [一段每次运行都不同的Base64编码字符串]

SM2 Decrypted: 你好,国密SM2非对称加密!

小总结

国密 SM2/SM4 是符合中国国家标准的高强度现代密码算法。在需要满足国内合规性要求(如金融、政务等)的项目中,应优先使用国密算法。SM2 相比 RSA 在性能和密钥长度上有优势,而 SM4 则提供了与 AES 同等级别的对称加密能力。


整体学习建议

学习路径建议

  1. 第一站:编码 (Base64) - 理解数据如何在不同系统中"变形"传输,彻底分清编码和加密的区别。
  2. 第二站:摘要 (MD5, SHA-256) - 理解哈希函数的单向性和抗碰撞性,学会用它来校验数据完整性,并明白为何 MD5 已被淘汰。
  3. 第三站:对称加密 (SM4, AES) - 学习使用单一密钥进行高效数据加解密,理解其在加密海量数据时的核心作用。
  4. 第四站:非对称加密 (RSA, SM2) - 掌握公私钥体系,理解它如何解决密钥分发难题,并学会用它进行加密和数字签名。
  5. 第五站:融会贯通 - 学习 HTTPS、数字证书等实际应用是如何将以上技术组合起来,构建出强大的安全体系的。

持续学习资源

密码学算法新手入门教程

导出为图片

总览:加密、摘要与编码

在进入具体算法之前,我们必须先分清三个核心概念:

概念 核心目的 类比 是否可逆 本文涉及的算法
编码 (Encoding) 将数据从一种形式转换为另一种形式,以便于传输或处理。不为保密 翻译官。把中文翻译成英文,内容不变,只是换了种表达方式。 可逆 (知道规则就能转回来) Base64
摘要/哈希 (Hashing) 为数据生成一个固定长度的、唯一的"指纹"。用于校验数据完整性 榨汁机。把苹果放进去变成苹果汁,你无法从果汁变回完整的苹果。 不可逆 MD5 (以及更安全的 SHA 系列)
加密 (Encryption) 通过"密钥"将明文数据变成看不懂的密文,目的是保护数据机密性 带锁的日记本。只有拿对钥匙的人才能打开看内容。 可逆 (有密钥就能解密) RSA, SM2, SM4

Base64: 编码而非加密

是什么 (What is it?)

Base64 是一种将任意二进制数据转换为由 64 个可打印字符(A-Z, a-z, 0-9, +, /)组成的文本字符串的编码方法。它就像一位"翻译官",负责把计算机内部的 0101 二进制数据"翻译"成纯文本,以便在那些只支持文本的环境中(如 URL、JSON、邮件正文)安全地传输。

核心要点: Base64 是公开的编码规则,任何人都可以解码。它不提供任何保密性,不是加密!

能干嘛 (What can it do?)

  • 在 URL 中传递二进制数据(如小图片)。
  • 在 JSON 或 XML 文件中嵌入二进制内容。
  • 作为数据 URI (Data URI scheme) 在网页中直接嵌入图片,减少 HTTP 请求。

怎么玩 (How to play)

Java 8 及以上版本内置了强大的 Base64 工具类,无需任何第三方库。

Java Demo: 使用 java.util.Base64 复制

java 复制代码
import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class Base64Example {
    public static void main(String[] args) {
        String originalInput = "Hello, Wireshark, RSA, MD5, and SM!";

        // 编码 (Encode)
        String encodedString = Base64.getEncoder().encodeToString(originalInput.getBytes(StandardCharsets.UTF_8));
        System.out.println("原始字符串: " + originalInput);
        System.out.println("Base64 编码后: " + encodedString);

        // 解码 (Decode)
        byte[] decodedBytes = Base64.getDecoder().decode(encodedString);
        String decodedString = new String(decodedBytes, StandardCharsets.UTF_8);
        System.out.println("Base64 解码后: " + decodedString);
    }
}

原始字符串: Hello, Wireshark, RSA, MD5, and SM!

Base64 编码后: SGVsbG8sIFdpcmVzaGFyaywgUlNBLCBNRDUgYW5kIFNNIQ==

Base64 解码后: Hello, Wireshark, RSA, MD5, and SM!

小总结

Base64 是一个简单实用的编码工具,用于在文本环境中安全地传输二进制数据。它简单、快速、通用,但切记它与"安全"或"保密"无关。


MD5: 消息摘要 (已不安全)

是什么 (What is it?)

MD5 (Message-Digest Algorithm 5) 是一种消息摘要算法,也叫哈希函数。它能将任意长度的数据,通过一个不可逆的"搅拌"过程,生成一个固定为 128 位(通常表示为 32 个十六进制字符)的唯一"指纹"或"摘要"。

核心特性:

  • 定长输出: 无论输入多长,输出总是 32 个十六进制字符。
  • 不可逆性: 无法从 MD5 值反推出原始数据。
  • 雪崩效应: 输入数据的任何微小变动,都会导致输出的 MD5 值发生巨大变化。

能干嘛 (What can it do?)

MD5 主要用于校验数据完整性 (Integrity Check)

  • 文件校验: 网站提供软件下载时,通常会附上一个 MD5 值。你下载文件后,在本地计算其 MD5 值,如果与网站提供的一致,说明文件在下载过程中没有损坏或被篡改。
  • 数字签名: 在对数据签名时,通常不是对整个庞大的原文签名,而是对原文的哈希值进行签名,效率更高。

常见的坑和陷阱

严重警告: 由于 MD5 算法已被发现存在"哈希碰撞"(即可以找到两个不同的输入,得到相同的 MD5 值)的严重漏洞,绝对不能再用于任何需要安全性的场景,例如:

  • 密码存储:直接存储用户密码的 MD5 值是极其危险的,容易被彩虹表攻击破解。
  • 代码签名或证书颁发:攻击者可以构造一个恶意文件,使其 MD5 与一个合法文件相同。

对于需要安全哈希的场景,请至少使用 SHA-256 或更强的算法。

怎么玩 (How to play)

Java 的 `java.security.MessageDigest` 类提供了 MD5 功能。

Java Demo: 计算字符串的MD5 复制

java 复制代码
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class MD5Example {
    public static String getMd5(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] messageDigest = md.digest(input.getBytes(StandardCharsets.UTF_8));
            BigInteger no = new BigInteger(1, messageDigest);
            String hashtext = no.toString(16);
            while (hashtext.length() < 32) {
                hashtext = "0" + hashtext;
            }
            return hashtext;
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        String text1 = "Hello, MD5!";
        String text2 = "Hello, MD5."; // 仅一个点不同
        
        System.out.println("MD5 of '" + text1 + "': " + getMd5(text1));
        System.out.println("MD5 of '" + text2 + "': " + getMd5(text2));
    }
}

MD5 of 'Hello, MD5!': 85079b70343a41e212341103b415a776

MD5 of 'Hello, MD5.': 2d4292193433554c63742468351516e8

小总结

MD5 是一个快速的哈希算法,仅适用于非安全领域文件完整性校验 。任何与安全、密码、认证相关的场景都应禁用 MD5,并使用如 SHA-256 等更现代、更安全的算法。


RSA: 非对称加密

是什么 (What is it?)

RSA 是一种非对称加密 算法,是目前应用最广泛的公钥加密算法。它的核心特点是拥有一对数学上相关的密钥:公钥 (Public Key)私钥 (Private Key)

  • 公钥:可以公开给任何人,用于加密数据和验证签名。
  • 私钥:必须由自己妥善保管,绝不泄露,用于解密数据和生成签名。

工作原则: 用公钥加密的数据,只有 对应的私钥才能解密。反之,用私钥签名的数据,只有对应的公钥才能验证。

核心思想原理

RSA 的安全性基于一个数学难题:大整数质因数分解。简单来说,将两个大质数相乘很容易,但要把它们的乘积分解回原来的两个质数,在计算上是极其困难的。这个难题保证了从公钥很难推导出私钥。

graph TD subgraph A[发送方 Alice] A1[明文] --> A2{用 Bob 的公钥加密}; A2 --> A3[密文]; end subgraph B[接收方 Bob] B1[密文] --> B2{用 Bob 自己的私钥解密}; B2 --> B3[明文]; end subgraph C[不可信的信道] A3 -- 通过网络传输 --> B1; end subgraph D[窃听者 Eve] C -- 截获 --> D1[密文]; D1 --> D2{只有 Bob 的公钥, 无法解密}; end

RSA 加密解密流程图

能干嘛 (What can it do?)

  1. 数据加密: 发送方向接收方发送敏感数据时,用接收方的公钥加密,确保只有接收方能用自己的私钥解开。这是 HTTPS 建立安全连接的关键步骤之一。
  2. 数字签名: 发送方为了证明某条消息确实是自己发的,可以用自己的私钥对消息的哈希值进行"签名"。接收方用发送方的公钥来"验证签名",如果验证通过,就能确认消息来源可靠且未被篡改。

怎么玩 (How to play)

Java 的 JCE (Java Cryptography Extension) 框架提供了完整的 RSA 支持。

Java Demo: RSA 加密、解密与签名 复制

java 复制代码
import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;

public class RsaExample {
    public static KeyPair generateKeyPair() throws Exception {
        KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
        generator.initialize(2048, new SecureRandom());
        return generator.generateKeyPair();
    }

    public static String encrypt(String plainText, PublicKey publicKey) throws Exception {
        Cipher encryptCipher = Cipher.getInstance("RSA");
        encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] cipherText = encryptCipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(cipherText);
    }

    public static String decrypt(String cipherText, PrivateKey privateKey) throws Exception {
        byte[] bytes = Base64.getDecoder().decode(cipherText);
        Cipher decriptCipher = Cipher.getInstance("RSA");
        decriptCipher.init(Cipher.DECRYPT_MODE, privateKey);
        return new String(decriptCipher.doFinal(bytes), StandardCharsets.UTF_8);
    }
    
    public static String sign(String plainText, PrivateKey privateKey) throws Exception {
        Signature privateSignature = Signature.getInstance("SHA256withRSA");
        privateSignature.initSign(privateKey);
        privateSignature.update(plainText.getBytes(StandardCharsets.UTF_8));
        byte[] signature = privateSignature.sign();
        return Base64.getEncoder().encodeToString(signature);
    }

    public static boolean verify(String plainText, String signature, PublicKey publicKey) throws Exception {
        Signature publicSignature = Signature.getInstance("SHA256withRSA");
        publicSignature.initVerify(publicKey);
        publicSignature.update(plainText.getBytes(StandardCharsets.UTF_8));
        byte[] signatureBytes = Base64.getDecoder().decode(signature);
        return publicSignature.verify(signatureBytes);
    }
    
    public static void main(String[] args) throws Exception {
        // 1. 生成密钥对
        KeyPair keyPair = generateKeyPair();
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();

        String originalMessage = "This is a secret message for RSA!";
        System.out.println("原始消息: " + originalMessage);

        // 2. 加密与解密
        String encryptedMessage = encrypt(originalMessage, publicKey);
        System.out.println("加密后 (Base64): " + encryptedMessage);
        String decryptedMessage = decrypt(encryptedMessage, privateKey);
        System.out.println("解密后: " + decryptedMessage);
        
        System.out.println("\n--- 数字签名演示 ---");
        // 3. 签名与验签
        String signature = sign(originalMessage, privateKey);
        System.out.println("签名 (Base64): " + signature);
        boolean isVerified = verify(originalMessage, signature, publicKey);
        System.out.println("签名验证结果: " + isVerified);
        
        // 尝试用错误数据验签
        boolean isTamperedVerified = verify("This is a wrong message!", signature, publicKey);
        System.out.println("篡改后数据验签结果: " + isTamperedVerified);
    }
}

原始消息: This is a secret message for RSA!

加密后 (Base64): [一段很长的,每次运行都不同的Base64编码字符串]

解密后: This is a secret message for RSA!

--- 数字签名演示 ---

签名 (Base64): [另一段很长的,每次运行都不同的Base64编码字符串]

签名验证结果: true

篡改后数据验签结果: false

小总结

RSA 是现代密码学的基石。它通过公钥和私钥这对独特的组合,完美地解决了数据机密性 (加密)和身份真实性/数据完整性(签名)两大安全难题。但由于其计算速度较慢,通常不用于加密大文件,而是用于加密对称密钥或进行签名。


国密算法: SM2 & SM4

是什么 (What are they?)

国密算法(SM, 商用密码)是中国国家密码管理局发布的一系列密码标准,旨在实现密码技术的自主可控。SM2 和 SM4 是其中最核心、应用最广的两个。

  • SM4 (对称加密): 对标国际上的 AES 算法,用于高速数据加密。
  • SM2 (非对称加密): 对标国际上的 RSA/ECC 算法,基于椭圆曲线,用于加密和数字签名。

去哪下 (Where to get it?)

Java 原生 JDK 不支持国密算法,必须引入第三方密码学库。目前最主流、最权威的是 Bouncy Castle

Maven Dependency: Bouncy Castle 复制

xml 复制代码
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk18on</artifactId>
    <version>1.78.1</version> <!-- 建议使用最新稳定版 -->
</dependency>

怎么玩 (SM4 对称加密)

SM4 用于高效地加解密大量数据,加密和解密使用同一个密钥。

Java Demo: SM4/ECB/PKCS5Padding 加解密 复制

java 复制代码
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.Security;
import java.util.Base64;

public class SM4Example {
    static {
        // 必须在使用前静态注册BouncyCastle
        Security.addProvider(new BouncyCastleProvider());
    }

    public static void main(String[] args) throws Exception {
        String plainText = "你好,国密SM4对称加密!";

        // 1. 生成 SM4 密钥
        KeyGenerator keyGen = KeyGenerator.getInstance("SM4", "BC");
        keyGen.init(128); // SM4 密钥长度固定为 128 位
        SecretKey secretKey = keyGen.generateKey();
        byte[] keyBytes = secretKey.getEncoded();

        // 2. 加密
        Cipher cipher = Cipher.getInstance("SM4/ECB/PKCS5Padding", "BC");
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, "SM4"));
        byte[] encryptedBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
        String encodedEncrypted = Base64.getEncoder().encodeToString(encryptedBytes);
        System.out.println("SM4 Encrypted: " + encodedEncrypted);

        // 3. 解密
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBytes, "SM4"));
        byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encodedEncrypted));
        System.out.println("SM4 Decrypted: " + new String(decryptedBytes, "UTF-8"));
    }
}

SM4 Encrypted: [一段每次运行都不同的Base64编码字符串]

SM4 Decrypted: 你好,国密SM4对称加密!

怎么玩 (SM2 非对称加密)

SM2 性能优于 RSA,在同等安全强度下密钥更短。同样用于加密和签名。

Java Demo: SM2 加解密 复制

java 复制代码
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import java.security.*;
import java.util.Base64;

public class SM2Example {
    static {
        Security.addProvider(new BouncyCastleProvider());
    }
    public static void main(String[] args) throws Exception {
        String plainText = "你好,国密SM2非对称加密!";

        // 1. 生成SM2密钥对
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("EC", "BC");
        // SM2推荐使用256位椭圆曲线
        keyPairGen.initialize(256);
        KeyPair keyPair = keyPairGen.generateKeyPair();
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();

        // 2. 加密
        Cipher cipher = Cipher.getInstance("SM2", "BC");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] encryptedBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
        String encodedEncrypted = Base64.getEncoder().encodeToString(encryptedBytes);
        System.out.println("SM2 Encrypted: " + encodedEncrypted);

        // 3. 解密
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encodedEncrypted));
        System.out.println("SM2 Decrypted: " + new String(decryptedBytes, "UTF-8"));
    }
}

SM2 Encrypted: [一段每次运行都不同的Base64编码字符串]

SM2 Decrypted: 你好,国密SM2非对称加密!

小总结

国密 SM2/SM4 是符合中国国家标准的高强度现代密码算法。在需要满足国内合规性要求(如金融、政务等)的项目中,应优先使用国密算法。SM2 相比 RSA 在性能和密钥长度上有优势,而 SM4 则提供了与 AES 同等级别的对称加密能力。


整体学习建议

学习路径建议

  1. 第一站:编码 (Base64) - 理解数据如何在不同系统中"变形"传输,彻底分清编码和加密的区别。
  2. 第二站:摘要 (MD5, SHA-256) - 理解哈希函数的单向性和抗碰撞性,学会用它来校验数据完整性,并明白为何 MD5 已被淘汰。
  3. 第三站:对称加密 (SM4, AES) - 学习使用单一密钥进行高效数据加解密,理解其在加密海量数据时的核心作用。
  4. 第四站:非对称加密 (RSA, SM2) - 掌握公私钥体系,理解它如何解决密钥分发难题,并学会用它进行加密和数字签名。
  5. 第五站:融会贯通 - 学习 HTTPS、数字证书等实际应用是如何将以上技术组合起来,构建出强大的安全体系的。

持续学习资源

  • Bouncy Castle 官网 : https://www.bouncycastle.org/ - 国密算法和其他密码学算法最权威的 Java 实现之一。
  • 《图解密码技术》: 一本非常适合新手的图文并茂的密码学入门书籍。
  • RFC 文档: 阅读相关算法的官方标准文档 (RFCs) 是最硬核但最准确的学习方式。
  • OWASP (开放式Web应用程序安全项目): 学习密码学在 Web 安全中的应用和最佳实践。
相关推荐
BAGAE3 天前
量子计算机的发展趋势
去中心化·密码学·网络攻击模型·gpu算力·量子计算·可信计算技术·空间计算
openHiTLS密码开源社区4 天前
密码学与人工智能的深度融合:玄知大模型开启安全智能新纪元
人工智能·密码学·gpt-4o·openhitls·玄知大模型·sm4-hctr·密码学大模型
帅次5 天前
系统分析师-信息安全-信息系统安全体系&数据安全与保密
安全·web安全·网络安全·系统安全·密码学·安全威胁分析·安全架构
搬砖魁首7 天前
密码学系列 - 零知识证明(ZKP) - NTT与MSM的总结
密码学·零知识证明·zkp·ntt·msm·pippenger·kzg
A Runner for leave12 天前
网络与通信安全课程复习汇总3——身份认证
网络·密码学
courniche13 天前
ECDH、ECDHE、ECDLP、ECDSA傻傻分不清?
算法·密码学
Joy T14 天前
Solidity智能合约存储与数据结构精要
数据结构·区块链·密码学·智能合约·solidity·合约function
courniche15 天前
分组密码常见结构简介
算法·密码学
openHiTLS密码开源社区17 天前
【密码学实战】openHiTLS s_server命令行:搭建国密标准安全通信服务器
服务器·物联网·密码学·openhitls·tlcp·商用密码算法·dtlcp