英文:
RSA_private_decrypt padding
问题
我正在使用cryptography
库在Python中使用公钥加密一个密钥。
key_path = "key.bin"
key = secrets.token_bytes(32)
with open(key_path, "w") as key_file:
key_file.write(key.hex())
with open(public_key, "rb") as public_key_file:
public_key = serialization.load_pem_public_key(public_key_file.read())
padding_config = padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
enc_path = key_path + ".enc"
with open(enc_path, "wb") as enc_file:
bytes_array = public_key.encrypt(content, padding_config)
enc_file.write(bytes_array)
这段代码在我看来运行良好,但是用Rust编写的代码来读取这个密钥,它只是一个FFI到openssl C调用。在openssl中没有太多选项。你不能选择一个“算法”,一个“mgf”和一个“label”。填充只是一个枚举,所以我选择了明显的PKCS1_OAEP
。
use openssl::{
cipher::Cipher,
cipher_ctx::CipherCtx,
pkey::Private,
rsa::{Padding, Rsa},
};
pub fn decrypt(key_file: File, pk: &str, pass: &str) -> String {
let rsa = Rsa::private_key_from_pem_passphrase(pk.as_bytes(), pass.as_bytes())
.expect("无法从PEM构建RSA对象");
let mut encrypted = vec![];
key_file.read_to_end(&mut encrypted).expect("无法读取加密密钥文件");
let mut decrypted: Vec<u8> = vec![0; rsa.size() as usize];
rsa.private_decrypt(&encrypted, &mut decrypted, Padding::PKCS1_OAEP).unwrap();
String::from_utf8(decrypted)
}
但是我得到了以下错误:
ErrorStack([
Error { code: 67571871, library: "rsa routines", function: "RSA_padding_check_PKCS1_type_2", reason: "pkcs decoding error", file: "../crypto/rsa/rsa_pk1.c", line: 251 },
Error { code: 67522674, library: "rsa routines", function: "rsa_ossl_private_decrypt", reason: "padding check failed", file: "../crypto/rsa/rsa_ossl.c", line: 500 }
])
我知道Rust代码是“正确”的,因为在之前的版本中它可以正常工作(使用Padding::PKCS1
),当我使用子进程调用openssl而不是cryptography
库时。而且无论如何,在这里只有Padding
枚举可以更改。
openssl文档告诉我:
RSA_PKCS1_OAEP_PADDING
在PKCS#1 v2.0中定义的带有SHA-1,MGF1和空编码参数的EME-OAEP。建议所有新应用程序使用此模式。
但是使用hashes.SHA1()
并没有改变任何事情。我应该如何设置填充以使openssl接受解密它?
英文:
I'm encrypting a key with a public key with the cryptography
library, in Python.
key_path = "key.bin"
key = secrets.token_bytes(32)
with open(key_path, "w") as key_file:
key_file.write(key.hex())
with open(public_key, "rb") as public_key_file:
public_key = serialization.load_pem_public_key(public_key_file.read())
padding_config = padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
enc_path = key_path + ".enc"
with open(enc_path, "wb") as enc_file:
bytes_array = public_key.encrypt(content, padding_config)
enc_file.write(bytes_array)
This works well, afaik, but the code reading this key is in Rust, which is simply a FFI to openssl C calls. There's not many option with openssl. You can't choose an "algorithm", a "mgf" and a "label. Padding is simply an enum, so I picked the obvious one PKCS1_OAEP
.
use openssl::{
cipher::Cipher,
cipher_ctx::CipherCtx,
pkey::Private,
rsa::{Padding, Rsa},
};
pub fn decrypt(key_file: File, pk: &str, pass: &str) -> String {
let rsa = Rsa::private_key_from_pem_passphrase(pk.as_bytes(), pass.as_bytes())
.expect("Can't build RSA object from PEM");
let mut encrypted = vec![];
key_file.read_to_end(&mut encrypted).expect("Can't read encrypted key file");
let mut decrypted: Vec<u8> = vec![0; rsa.size() as usize];
rsa.private_decrypt(&encrypted, &mut decrypted, Padding::PKCS1_OAEP).unwrap();
String::from_utf8(decrypted)
}
But I get this error:
ErrorStack([
Error { code: 67571871, library: "rsa routines", function: "RSA_padding_check_PKCS1_type_2", reason: "pkcs decoding error", file: "../crypto/rsa/rsa_pk1.c", line: 251 },
Error { code: 67522674, library: "rsa routines", function: "rsa_ossl_private_decrypt", reason: "padding check failed", file: "../crypto/rsa/rsa_ossl.c", line: 500 }
])
I know that the Rust code is "right" because it was working well (with Padding::PKCS1
) in a previous version when I was using subprocess calls to openssl instead of the cryptography
library. And anyway there's only the Padding
enum to change here.
openssl documentation tells me that
> RSA_PKCS1_OAEP_PADDING
>
> EME-OAEP as defined in PKCS #1 v2.0 with SHA-1, MGF1 and an empty encoding parameter. This mode is recommended for all new applications.
but using hashes.SHA1()
didn't change anything. How should I setup my padding so that openssl accepts decrypting it?
答案1
得分: 1
以下是代码部分的翻译:
use openssl::encrypt::{Decrypter};
use openssl::rsa::{Rsa, Padding};
use openssl::pkey::PKey;
use openssl::hash::MessageDigest;
use base64::{engine::general_purpose, Engine as _};
fn main() {
// 导入私钥
let pem_private = "-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAunF5aDa6HCfLMMI/MZLT5hDk304CU+ypFMFiBjowQdUMQKYH
Z+fklB7GpLxCatxYJ/hZ7rjfHH3Klq20/Y1EbYDRopyTSfkrTzPzwsX4Ur/l25Ct
dQldhHCTMgwf/Ev/buBNobfzdZE+Dhdv5lQwKtjI43lDKvAi5kEet2TFwfJcJrBi
RJeEcLfVgWTXGRQn7gngWKykUu5rS83eAU1xH9FLojQfyia89/EykiOO7/3UWwd+
MATZ9HLjSx2/Lf3g2jr81eifEmYDlri/OZp4OhZu+0Bo1LXloCTe+vmIQ2YCX7Ea
tUOuyQMt2Vwx4uV+d/A3DP6PtMGBKpF8St4iGwIDAQABAoIBAEpSY9BMSKJippgY
RvKvEjNbNrFhuoHUDI7OjBHpbkU/9XNr+/XKtg7pucv/lAZfMfE1Cjyki6Qi7Phl
5IlSoB16xZaqDfjmEAKxthFFs7jg8HM3WD4YbqQV8Ed6F+IONZPwbGH0H2QVcwRR
CXVqyAy8gFBVjZX3RiI9gU4gKMkn8qpmpr5lvG7L3PHKIhpWdvFAvOf16sfcDs43
hqEZAh+z3EzJXPRe0sUrYcF/5NVQV5jTh2vh8xjoDv1t5NSw6iW6tsCo2U1t4lRI
NlaHY8YASzJtDLnkhqCOiLmmUjzMhRDN4BrwtvnAwSNki/2poYk1Gv1Q5U1cPyCm
0rmq/bkCgYEA3e+jND6OS6ofGYUN6G4RapHzuRAV8ux1C9eXMOdZFbcBehn/ydhz
R48LIPTW9HiRE00um27lXfW5/POCaEUvfOp1UxTWeHZ4xICo40PBo383ZKW1MbES
1oiMbjkEqSFGRnTItnLU07bKbzLA7I0UWHWCEAnv0g7HRxk973FAsm8CgYEA1w8+
olZ2POBYeYgw1a0DkeJWKMQi/4pAgyYwustZo0dHlRXQT0OI9XQ0j1PZWoQS28tF
cmoEAg6f5MUDpdM9swS0SOCPI1Lc/f/Slus3u1O3UCezk37pneSPezskDhvV2cCl
JEYH8m/zwDAUlEi4KLIt/H/jgtyDd6pbxxc78RUCgYEAiE6VAxJknM4oeakBiL6J
TdXEReY+RMu7e4F2518/lJmoe5CaTCL3cnzFTgFyQAYIvD0MIgSzNMkl6Ni6QEY1
y1fIpTVIIAZLWAzZLXPA6yTIJbWsmo9xzXdiIJQ+a433NnClkYDne/xpSnB2kxJ2
63mIX0drFq1i8STsqDH7lVsCgYBWpQmzFeqlNC3xKPGj5QTfLbVQz1TaZ7T+IdDY
MT14DyvD4PoANVFksuDKLhoP4c5HR2o/Yn8i5Ql+ffGuSaE+EtMf2XlR3fyzSPJr
Y3Ecw+nDHXu4HRW6S2+TUoyAMq7CAF+Icb6Z6ojmEXj5FSM9Iixs4omjG3zMJZK8
b5vy0QKBgQCz0W7UmxNqT4Lzhyh0xj6+4Cm5rFlWznEAPKxvNNU9/Fv1D3tV7cpz
BI880NvTmlXkML1Dffg8RVyemL8I+ooeFnatdPYuzqNXw5zHMzkDfQynIdnjQB6z
UjjMiwwgZvvsG8hwLEtk9FTvPoMywAb4mZuQBQkFn5HuC2aOZjktdA==
-----END RSA PRIVATE KEY";
let private_key = Rsa::private_key_from_pem(pem_private.as_bytes()).unwrap();
let private_key = PKey::from_rsa(private_key).unwrap();
// 导入Base64编码的密文
let data = "oU1PwQRE0ZEr71OOfoNHLpmjXyTAfdZroeQhX4WLAhXpfTMkEMJ0YptHgFDirJ5fdZ9yRl+y1y6jZSpG7oj5wtJkDa4BeLba++Q1UZcKlne4rfYMEPDrkTCjyHyNskuJuLh3FW+HCp70tRzvgSoBpoIwyxWl3VREYRcJEAdzGwRj0d6JNCO4M3BHX7g59to5urOkTXB7MfcAEz1Ba4AdeNYrZK4XD8GjAqnI95X3Z4F8PoLGMP1eMif0fpNizxZo7hzTfEsfjdlkyfTWLZfxuZ9qZMIpkiQNNEWWMt66
<details>
<summary>英文:</summary>
Presumably, the posted Rust code uses the RFC8017 default values for the OAEP parameters, namely SHA-1 for the content digest and the MGF1 digest, and an empty label.
The posted Python code, on the other hand, applies SHA-256 for both digests, which is why the two codes are incompatible. You have to set both digests to SHA-256 on the Rust side (alternatively you could use SHA-1 on the Python side).
Here you can find a Rust [sample implementation][1] for OAEP. The setting of the OAEP padding is done with [`set_rsa_padding(Padding::PKCS1_OAEP)`][2]. To avoid using the SHA-1 default value for the content digest, the content digest must be set explicitly with [`set_rsa_oaep_md(MessageDigest::sha256())`][3]. The MGF1 digest is implicitly set with the content digest (since contents and MGF1 digests are identical in the posted example, the MGF1 digest does not need to be explicitly set here).
If a different MGF1 digest is to be used, [`set_rsa_mgf1_md()`][4] must be used. If a label is to be set, [`set_rsa_oaep_label()`][5]. Note, however, that the label is actually always left empty (which should also not be changed for compatibility reasons).
Test:
Using the posted Python code, the base64 encoded 32 bytes key
```none
AAECAwQFBgcICQoLDA0OD/Dx8vP09fb3+Pn6+/z9/v8=
is encrypted as follows (Base64 encoded):
oU1PwQRE0ZEr71OOfoNHLpmjXyTAfdZroeQhX4WLAhXpfTMkEMJ0YptHgFDirJ5fdZ9yRl+y1y6jZSpG7oj5wtJkDa4BeLba++Q1UZcKlne4rfYMEPDrkTCjyHyNskuJuLh3FW+HCp70tRzvgSoBpoIwyxWl3VREYRcJEAdzGwRj0d6JNCO4M3BHX7g59to5urOkTXB7MfcAEz1Ba4AdeNYrZK4XD8GjAqnI95X3Z4F8PoLGMP1eMif0fpNizxZo7hzTfEsfjdlkyfTWLZfxuZ9qZMIpkiQNNEWWMt66FmVgFyi8zngz/Tj5Tk7cNzrWOT5BDaFCxrJF1kHoHobojA==
Note that OAEP is not deterministic, i.e. repeated encryption with the same key and plaintext produces different ciphertexts.
This ciphertext can be decrypted with the following Rust code:
use openssl::encrypt::{Decrypter};
use openssl::rsa::{Rsa, Padding};
use openssl::pkey::PKey;
use openssl::hash::MessageDigest;
use base64::{engine::general_purpose, Engine as _};
fn main() {
// Import private key
let pem_private = "-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAunF5aDa6HCfLMMI/MZLT5hDk304CU+ypFMFiBjowQdUMQKYH
Z+fklB7GpLxCatxYJ/hZ7rjfHH3Klq20/Y1EbYDRopyTSfkrTzPzwsX4Ur/l25Ct
dQldhHCTMgwf/Ev/buBNobfzdZE+Dhdv5lQwKtjI43lDKvAi5kEet2TFwfJcJrBi
RJeEcLfVgWTXGRQn7gngWKykUu5rS83eAU1xH9FLojQfyia89/EykiOO7/3UWwd+
MATZ9HLjSx2/Lf3g2jr81eifEmYDlri/OZp4OhZu+0Bo1LXloCTe+vmIQ2YCX7Ea
tUOuyQMt2Vwx4uV+d/A3DP6PtMGBKpF8St4iGwIDAQABAoIBAEpSY9BMSKJippgY
RvKvEjNbNrFhuoHUDI7OjBHpbkU/9XNr+/XKtg7pucv/lAZfMfE1Cjyki6Qi7Phl
5IlSoB16xZaqDfjmEAKxthFFs7jg8HM3WD4YbqQV8Ed6F+IONZPwbGH0H2QVcwRR
CXVqyAy8gFBVjZX3RiI9gU4gKMkn8qpmpr5lvG7L3PHKIhpWdvFAvOf16sfcDs43
hqEZAh+z3EzJXPRe0sUrYcF/5NVQV5jTh2vh8xjoDv1t5NSw6iW6tsCo2U1t4lRI
NlaHY8YASzJtDLnkhqCOiLmmUjzMhRDN4BrwtvnAwSNki/2poYk1Gv1Q5U1cPyCm
0rmq/bkCgYEA3e+jND6OS6ofGYUN6G4RapHzuRAV8ux1C9eXMOdZFbcBehn/ydhz
R48LIPTW9HiRE00um27lXfW5/POCaEUvfOp1UxTWeHZ4xICo40PBo383ZKW1MbES
1oiMbjkEqSFGRnTItnLU07bKbzLA7I0UWHWCEAnv0g7HRxk973FAsm8CgYEA1w8+
olZ2POBYeYgw1a0DkeJWKMQi/4pAgyYwustZo0dHlRXQT0OI9XQ0j1PZWoQS28tF
cmoEAg6f5MUDpdM9swS0SOCPI1Lc/f/Slus3u1O3UCezk37pneSPezskDhvV2cCl
JEYH8m/zwDAUlEi4KLIt/H/jgtyDd6pbxxc78RUCgYEAiE6VAxJknM4oeakBiL6J
TdXEReY+RMu7e4F2518/lJmoe5CaTCL3cnzFTgFyQAYIvD0MIgSzNMkl6Ni6QEY1
y1fIpTVIIAZLWAzZLXPA6yTIJbWsmo9xzXdiIJQ+a433NnClkYDne/xpSnB2kxJ2
63mIX0drFq1i8STsqDH7lVsCgYBWpQmzFeqlNC3xKPGj5QTfLbVQz1TaZ7T+IdDY
MT14DyvD4PoANVFksuDKLhoP4c5HR2o/Yn8i5Ql+ffGuSaE+EtMf2XlR3fyzSPJr
Y3Ecw+nDHXu4HRW6S2+TUoyAMq7CAF+Icb6Z6ojmEXj5FSM9Iixs4omjG3zMJZK8
b5vy0QKBgQCz0W7UmxNqT4Lzhyh0xj6+4Cm5rFlWznEAPKxvNNU9/Fv1D3tV7cpz
BI880NvTmlXkML1Dffg8RVyemL8I+ooeFnatdPYuzqNXw5zHMzkDfQynIdnjQB6z
UjjMiwwgZvvsG8hwLEtk9FTvPoMywAb4mZuQBQkFn5HuC2aOZjktdA==
-----END RSA PRIVATE KEY-----";
let private_key = Rsa::private_key_from_pem(pem_private.as_bytes()).unwrap();
let private_key = PKey::from_rsa(private_key).unwrap();
// Import Base64 encoded ciphertext
let data = "oU1PwQRE0ZEr71OOfoNHLpmjXyTAfdZroeQhX4WLAhXpfTMkEMJ0YptHgFDirJ5fdZ9yRl+y1y6jZSpG7oj5wtJkDa4BeLba++Q1UZcKlne4rfYMEPDrkTCjyHyNskuJuLh3FW+HCp70tRzvgSoBpoIwyxWl3VREYRcJEAdzGwRj0d6JNCO4M3BHX7g59to5urOkTXB7MfcAEz1Ba4AdeNYrZK4XD8GjAqnI95X3Z4F8PoLGMP1eMif0fpNizxZo7hzTfEsfjdlkyfTWLZfxuZ9qZMIpkiQNNEWWMt66FmVgFyi8zngz/Tj5Tk7cNzrWOT5BDaFCxrJF1kHoHobojA==";
let encrypted = general_purpose::STANDARD.decode(&data).unwrap();
// Decrypt ciphertext
let mut decrypter = Decrypter::new(&private_key).unwrap();
decrypter.set_rsa_padding(Padding::PKCS1_OAEP).unwrap();
decrypter.set_rsa_oaep_md(MessageDigest::sha256()).unwrap();
//decrypter.set_rsa_mgf1_md(MessageDigest::sha256()).unwrap(); // implicitly set
// Create an output buffer
let buffer_len = decrypter.decrypt_len(&encrypted).unwrap();
let mut decrypted = vec![0; buffer_len];
// Encrypt and truncate buffer
let decrypted_len = decrypter.decrypt(&encrypted, &mut decrypted).unwrap();
decrypted.truncate(decrypted_len);
println!("Decrypted: {}", general_purpose::STANDARD.encode(&decrypted)); // Decrypted: AAECAwQFBgcICQoLDA0OD/Dx8vP09fb3+Pn6+/z9/v8=
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论