首页/我的作品/密码学/13.4 代码示例:Python/Go/Java的加密实战
密码学:守护数字世界的秘密

13.4 代码示例:Python/Go/Java的加密实战

第 73 / 78 篇 · Crazy Boy

理论讲再多,不如亲手写一遍。这一节,我们分别用 Python、Go、Java 三种语言,展示一个完整的对称加密流程。我们选择 AES-256-GCM 作为加密算法,因为它既提供机密性(加密)又提供完整性(认证),是目前最推荐的对称加密方案之一。示例将涵盖:生成密钥、随机 IV、加密、解密,并演示如何处理异常。

所有代码都遵循了我们之前讨论的安全原则:

  • 使用密码学安全的随机数生成器
  • 使用认证加密模式(GCM)
  • 密钥和 IV 的正确管理
  • 解密时对错误的正确处理

Python 示例(使用 cryptography 库)

import os
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend

def encrypt(key: bytes, plaintext: bytes) -> bytes:
"""使用 AES-256-GCM 加密,返回 IV + 密文 + 标签"""
iv = os.urandom(12) # GCM 推荐 12 字节随机数
cipher = Cipher(algorithms.AES(key), modes.GCM(iv), backend=default_backend())
encryptor = cipher.encryptor()
ciphertext = encryptor.update(plaintext) + encryptor.finalize()
# 返回 iv + ciphertext + tag
return iv + ciphertext + encryptor.tag

def decrypt(key: bytes, ciphertext_with_iv_tag: bytes) -> bytes:
"""解密,输入为 iv + ciphertext + tag"""
iv = ciphertext_with_iv_tag[:12]
tag = ciphertext_with_iv_tag[-16:]
ciphertext = ciphertext_with_iv_tag[12:-16]
cipher = Cipher(algorithms.AES(key), modes.GCM(iv, tag), backend=default_backend())
decryptor = cipher.decryptor()
plaintext = decryptor.update(ciphertext) + decryptor.finalize()
return plaintext

# 使用示例
if __name__ == "__main__":
# 生成一个 256 位的密钥(实际应用中应从安全存储读取)
key = os.urandom(32)
message = b"Hello, this is a secret message!"
print(f"Original: {message}")

encrypted = encrypt(key, message)
print(f"Encrypted (hex): {encrypted.hex()}")

decrypted = decrypt(key, encrypted)
print(f"Decrypted: {decrypted.decode('utf-8')}")

# 演示篡改检测
tampered = bytearray(encrypted)
tampered[0] ^= 1 # 翻转一个字节
try:
decrypt(key, bytes(tampered))
except Exception as e:
print(f"Tampering detected: {e}")

关键点:

  • os.urandom 提供安全随机数。
  • modes.GCM 自动处理认证标签,密文被篡改会抛出异常。
  • 返回时把 IV、密文、标签打包在一起,便于传输。

Go 示例(使用标准库 crypto/aes + crypto/cipher)

package main

import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
)

// encrypt 返回格式: nonce + ciphertext(已包含认证标签)
func encrypt(key, plaintext []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
nonce := make([]byte, 12)
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return nil, err
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
ciphertext := aesgcm.Seal(nil, nonce, plaintext, nil)
// 将 nonce 和 ciphertext 拼接返回
return append(nonce, ciphertext...), nil
}

// decrypt 输入格式: nonce + ciphertext
func decrypt(key, ciphertext []byte) ([]byte, error) {
if len(ciphertext) < 12 {
return nil, fmt.Errorf("ciphertext too short")
}
nonce, ciphertext := ciphertext[:12], ciphertext[12:]
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, err
}
return plaintext, nil
}

func main() {
// 生成 256 位密钥
key := make([]byte, 32)
if _, err := io.ReadFull(rand.Reader, key); err != nil {
panic(err)
}
message := []byte("Hello, this is a secret message!")
fmt.Printf("Original: %s\n", message)

encrypted, err := encrypt(key, message)
if err != nil {
panic(err)
}
fmt.Printf("Encrypted (hex): %x\n", encrypted)

decrypted, err := decrypt(key, encrypted)
if err != nil {
panic(err)
}
fmt.Printf("Decrypted: %s\n", decrypted)

// 篡改测试
tampered := make([]byte, len(encrypted))
copy(tampered, encrypted)
tampered[0] ^= 1
_, err = decrypt(key, tampered)
fmt.Printf("Tampering test: %v\n", err)
}

关键点:

  • 使用 crypto/rand 生成随机数。
  • cipher.NewGCM 返回认证加密对象。
  • Seal 和 Open 自动处理认证标签,验证失败返回错误。

Java 示例(使用 javax.crypto)

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;

public class AESGCMExample {
private static final int GCM_TAG_LENGTH = 16; // 字节
private static final int GCM_IV_LENGTH = 12; // 字节

public static byte[] encrypt(SecretKey key, byte[] plaintext) throws Exception {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] iv = new byte[GCM_IV_LENGTH];
SecureRandom random = new SecureRandom();
random.nextBytes(iv);
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv); // 注意长度单位是位
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
byte[] ciphertext = cipher.doFinal(plaintext);
// 返回 iv + ciphertext(ciphertext 已经包含认证标签)
byte[] result = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, result, 0, iv.length);
System.arraycopy(ciphertext, 0, result, iv.length, ciphertext.length);
return result;
}

public static byte[] decrypt(SecretKey key, byte[] encrypted) throws Exception {
if (encrypted.length < GCM_IV_LENGTH) {
throw new IllegalArgumentException("Ciphertext too short");
}
byte[] iv = Arrays.copyOfRange(encrypted, 0, GCM_IV_LENGTH);
byte[] ciphertext = Arrays.copyOfRange(encrypted, GCM_IV_LENGTH, encrypted.length);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
cipher.init(Cipher.DECRYPT_MODE, key, spec);
return cipher.doFinal(ciphertext);
}

public static void main(String[] args) throws Exception {
// 生成 AES-256 密钥
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
SecretKey key = keyGen.generateKey();

String plaintext = "Hello, this is a secret message!";
System.out.println("Original: " + plaintext);

byte[] encrypted = encrypt(key, plaintext.getBytes());
System.out.println("Encrypted (base64): " + Base64.getEncoder().encodeToString(encrypted));

byte[] decrypted = decrypt(key, encrypted);
System.out.println("Decrypted: " + new String(decrypted));

// 篡改测试
byte[] tampered = encrypted.clone();
tampered[0] ^= 1;
try {
decrypt(key, tampered);
System.out.println("Tampering not detected (should not happen)");
} catch (Exception e) {
System.out.println("Tampering detected: " + e.getMessage());
}
}
}

关键点:

  • SecureRandom 用于生成安全随机 IV。
  • AES/GCM/NoPadding 模式,注意 GCM 标签长度需显式设置(单位是位,所以 16 字节 = 128 位)。
  • 解密时若认证失败会抛出 AEADBadTagException。

关于密钥存储的提醒

上述示例为演示方便,密钥是临时生成的。在生产环境中,密钥绝不能硬编码在代码中,也不应写在配置文件里。正确的做法是使用密钥管理系统(KMS),例如:

  • 云服务商提供的 KMS(AWS KMS、Google Cloud KMS、Azure Key Vault)。
  • 本地硬件安全模块(HSM)。
  • 环境变量 + 安全存储(如 HashiCorp Vault)。

即使开发测试,也应使用环境变量或单独的配置文件,并确保这些文件不提交到版本控制系统。

小结

通过这三个语言的实战,你应该能掌握安全对称加密的标准写法:

  • 使用认证加密模式(GCM、ChaCha20-Poly1305)。
  • 每次加密都生成新的随机 IV。
  • 将 IV 与密文一起传输,并在解密时使用。
  • 正确处理异常,防止信息泄露。

当你需要在项目中集成加密时,直接参考这些示例,并根据具体场景选择合适的库和参数。安全开发不是一蹴而就,但遵循最佳实践,就能避开绝大多数常见陷阱。