跳到主要内容

混合加密

常见的加密算法有对称加密如 AES,非对称加密如 RSA 和 ECC。非对称加密无需预先共享密钥,但加解密速度较慢,且加密后数据量通常会增大,因此实践中常使用混合加密传输数据:

  1. 接收方使用非对称加密算法生成一对公私钥,其中公钥可以公开,私钥必须保密
  2. 加密方生成一个随机的对称密钥,并使用该对称密钥加密实际数据
  3. 加密方使用接收方的公钥加密该对称密钥,并将加密后的对称密钥和加密后的数据一起发送给接收方
  4. 接收方使用自己的私钥解密得到对称密钥,再使用该对称密钥解密得到实际数据

注意事项:

  • RSA 密钥长度至少应为 2048 位,且密钥长度会限制可加密的原文长度,根据 Go 中 rsa.EncryptOAEP() 的函数说明,使用 2048 位密钥时 RSA-OAEP 最多可加密 256-2*20-2=214 字节的数据
  • 在浏览器中使用 Web Crypto API 加密数据时,对称加密算法 AES-CBC 仅支持 PKCS#7 填充方式且无法自定义,非对称加密算法仅支持 RSA-OAEP,且只能在 安全上下文 中使用

参考来源:

非对称密钥生成

openssl genrsa -out rsa_private.pem 2048 # 生成 RSA 私钥
openssl rsa -in rsa_private.pem -pubout -out rsa_public.pem # 从私钥中提取 RSA 公钥

加解密示例

将以下代码保存为 hybrid_encryption.rb,执行 ruby hybrid_encryption.rb 运行该脚本

require 'openssl'
require 'base64'
require 'json'

RSA_PRIVATE_KEY_PEM = <<~EOF
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCcvaxoGO5vhlzA
l0cWnRNTP8W1rg8zmVrJA3Yv6OSt+tPf0kcXVpGpBrIHofXxiPAY2FnhKLzaHYrA
MBl218JKxV5cqIF9kRgV10CjRQRq419sWq0p8AhjCUELIfGkmJ6go6KaQ/vRqDEr
jzi8mWFi2/7rG8OE2PanNZ516WjFHi7atH1LUCaRNKObQTUfWAnw9UatB/Te5KI9
MrdRv+k7XcayW4osIIfv9L65KxHw44PHsTxQDZhdpAzBQn/DxY+x2OLtLiBo9NG9
C8S8WIQUPQRzXmx7+C/ZUjmgPK0M1PqrOzIln+wKmQBHk6uA7UO24zUzDxjk4jqF
5KAypIppAgMBAAECggEASsDLMPpy/Q1/vvpnFQnk5ZdMm9FfvqQtACF+hGr2ZWNz
GRQYg7Dt10cyMSG1QnSkzbShaRkcuInOWsqV2nAcYbyvvFFBMLm6WvBGC5gm+zNl
ly/H1nB8D3+iXcTakj2XqdVg/2Hc/6jPio7L6GbeY1+4vDiJjxpKyIRyBppDNX7o
kBbjN9k/bmPDQE3z0F1kBv+W791xMPyzZ6678qbh3uJvPrK5L3OUP3EQ7iaIDqfH
TqbFy3vNvwYA4mGuTsZnAl1lxbuoIhzGDrD6p1KameX+hCyF4oxGrrkS3t14EXIx
rx9sKo26IU0x8H1WuRzgpP8n9nrSI0aD2dls7Y9D4QKBgQDL1/EMG5vD+X6WqRwU
6a1027TGXxzN3BVOIgFjOQV+05yTTTRwzAAFQTH6rSGrG9rAqg9UuW/y0dsXaJw+
EEhJvrrhZ3ievWdszxue37gD9D+LDgQ1axm3mP8MZYXL9sACknJhrQKNpOiJFRS+
CzTTtqXAm3fXlWcxPkw48pjTXwKBgQDE2HCX68kpdYwaJfbVIjIuC1QLSLTTJ2Oj
OP0C1hFUA7sOj8rX1ZJLWyzHl3mXq48q2ii3JecF16FiC+aHi5zO4yivZVQn/KFP
3jhowj5UpWQUmmbHWyemOWGk8O6l/jZ90b97quvT0rnPFUc9NutmfBS5isp9R62P
3J4t7O1/NwKBgQCH2eGm/Rthj3yDMi8p7NaSI/6lmivbMorsaJNeKll0PdmC/hgt
+HcnCV0iwJHItKakcnIHOBLY1G1ce3ZtknJq23c207u6p2YvSRQSXO0JjZVvuiap
5zfbeVa5T0vNNCShUjJy0Ff8SGRFP8x0H4nUc0yiQbqr7cE5O1iXN28MpwKBgBRI
fSmaWSsVeq9DFDEYCfWmoy1ae496cpDiEWgWaMiwbbjtZihhUGbFaVLTr+rJ+cGf
oyxkEm8OrgltUn7LCwhibdHM7iQEqxaQvl1FDqkoNazN0CNgqDA/n2kgjma6UCEc
2M9EaHJ2N0E4XxPWseDojjYkMANCvIajZAU1ca93AoGBAILI5+n25djOOj7kh+94
QJ9goKXmDYmGoj9Q7QtpqaiJzFW49Ksq+b5g8sj9mT8VSvCFpB2w9P0xpI6rtNkK
AQQ7dN/EDedy3a4WhFyBYbWi4psoBbaat46ZU60qY4/QTTxvFt3L/2g2XQV1PaJK
GcdSC8Jdq4D6ifVmZ7oN0nfx
-----END PRIVATE KEY-----
EOF

RSA_PUBLIC_KEY_PEM = <<~EOF
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnL2saBjub4ZcwJdHFp0T
Uz/Fta4PM5layQN2L+jkrfrT39JHF1aRqQayB6H18YjwGNhZ4Si82h2KwDAZdtfC
SsVeXKiBfZEYFddAo0UEauNfbFqtKfAIYwlBCyHxpJieoKOimkP70agxK484vJlh
Ytv+6xvDhNj2pzWedeloxR4u2rR9S1AmkTSjm0E1H1gJ8PVGrQf03uSiPTK3Ub/p
O13GsluKLCCH7/S+uSsR8OODx7E8UA2YXaQMwUJ/w8WPsdji7S4gaPTRvQvEvFiE
FD0Ec15se/gv2VI5oDytDNT6qzsyJZ/sCpkAR5OrgO1DtuM1Mw8Y5OI6heSgMqSK
aQIDAQAB
-----END PUBLIC KEY-----
EOF

INPUT = "The quick brown fox jumps over the lazy dog"

RSA_PUBLIC_KEY = OpenSSL::PKey::RSA.new(RSA_PUBLIC_KEY_PEM)
RSA_PRIVATE_KEY = OpenSSL::PKey::RSA.new(RSA_PRIVATE_KEY_PEM)

# public encryption
aes_cipher = OpenSSL::Cipher::AES256.new(:CBC)
aes_cipher.encrypt
aes_key = aes_cipher.random_key
aes_iv = aes_cipher.random_iv
encrypted_data = aes_cipher.update(INPUT) + aes_cipher.final
encrypted_key = RSA_PUBLIC_KEY.public_encrypt(aes_key, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
encrypted_result = JSON.dump({
encrypted_data: Base64.strict_encode64(encrypted_data),
encrypted_key: Base64.strict_encode64(encrypted_key),
iv: Base64.strict_encode64(aes_iv)
})

puts "Original Data: #{INPUT}"
puts "Encrypted Data: #{encrypted_result}"

# private decryption
params = JSON.parse(encrypted_result)
encrypted_data = Base64.decode64(params["encrypted_data"])
encrypted_key = Base64.decode64(params["encrypted_key"])
aes_iv = Base64.decode64(params["iv"])

aes_decipher = OpenSSL::Cipher::AES256.new(:CBC)
aes_decipher.decrypt
aes_decipher.key = RSA_PRIVATE_KEY.private_decrypt(encrypted_key, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
aes_decipher.iv = aes_iv
decrypted_data = aes_decipher.update(encrypted_data) + aes_decipher.final

puts "Decrypted Data: #{decrypted_data}"

输出结果示例:

Original  Data: The quick brown fox jumps over the lazy dog
Encrypted Data: {"encrypted_data":"dfh0hI37NtmMSYhr2o0QEkaDOfLVk6qIBDZpdBOvqF2b3Lw9TE3xEsLqvLcvsugO","encrypted_key":"gUwBwput0Q1LMmoUosDDC5l4T8a2VQaEOPOY9ZiHPQi8UEPX7cKihLpl4U2FYdTOGUI5+Qs0DKa9CQYBEn5TwLe38nuqlmd4cxCF0UKOa0MoS4X6XQtRouYw0ngSBaZMQotP6WJGOtBrTsHyLTGGSX67Z0JZRyKzw70SaMfPELpk3Pe/D/Dx12nMFogYExvt1myjB51WeMpDfDYLnDj7y4ZOk0NDv28DIjLlz4yZeSHd4iyEZO78nXH630U2BatwIM2vWU+cxCTjeEfLAh4lcxkq6ZJi8QuBS779pYR7nHtDUorZNCUnfqrC1Ke419RDmxbIJbADrViJR3JZZKo/kA==","iv":"egLjHHOtXR9QheL74vunOQ=="}
Decrypted Data: The quick brown fox jumps over the lazy dog