@@ -64,50 +64,89 @@ fn load_pem_private_key<'p>(
64
64
backend : Option < pyo3:: Bound < ' _ , pyo3:: PyAny > > ,
65
65
unsafe_skip_rsa_key_validation : bool ,
66
66
) -> CryptographyResult < pyo3:: Bound < ' p , pyo3:: PyAny > > {
67
+ let _ = backend;
68
+
67
69
let p = x509:: find_in_pem (
68
70
data. as_bytes ( ) ,
69
71
|p| [ "PRIVATE KEY" , "ENCRYPTED PRIVATE KEY" , "RSA PRIVATE KEY" , "EC PRIVATE KEY" , "DSA PRIVATE KEY" ] . contains ( & p. tag ( ) ) ,
70
72
"Valid PEM but no BEGIN/END delimiters for a private key found. Are you sure this is a private key?"
71
73
) ?;
72
- // TODO: if proc-type is present, decrypt PEM layer.
73
- if p. headers ( ) . get ( "Proc-Type" ) . is_none ( ) {
74
- let pkey = match p. tag ( ) {
75
- "PRIVATE KEY" => cryptography_key_parsing:: pkcs8:: parse_private_key ( p. contents ( ) ) ?,
76
- "ENCRYPTED PRIVATE KEY" => {
77
- cryptography_key_parsing:: pkcs8:: parse_encrypted_private_key (
78
- p. contents ( ) ,
79
- password. as_ref ( ) . map ( |v| v. as_bytes ( ) ) ,
80
- ) ?
81
- }
82
- "RSA PRIVATE KEY" => {
83
- cryptography_key_parsing:: rsa:: parse_pkcs1_private_key ( p. contents ( ) ) ?
84
- }
85
- "EC PRIVATE KEY" => {
86
- cryptography_key_parsing:: ec:: parse_pkcs1_private_key ( p. contents ( ) , None ) ?
87
- }
88
- "DSA PRIVATE KEY" => {
89
- cryptography_key_parsing:: dsa:: parse_pkcs1_private_key ( p. contents ( ) ) ?
90
- }
91
- _ => unreachable ! ( ) ,
92
- } ;
93
- if password. is_some ( ) && p. tag ( ) != "ENCRYPTED PRIVATE KEY" {
74
+ let password = password. as_ref ( ) . map ( |v| v. as_bytes ( ) ) ;
75
+ let mut password_used = false ;
76
+ // TODO: Surely we can avoid this clone?
77
+ let tag = p. tag ( ) . to_string ( ) ;
78
+ let data = match p. headers ( ) . get ( "Proc-Type" ) {
79
+ Some ( "4,ENCRYPTED" ) => {
80
+ password_used = true ;
81
+ let Some ( dek_info) = p. headers ( ) . get ( "DEK-Info" ) else {
82
+ todo ! ( )
83
+ } ;
84
+ let Some ( ( cipher_algorithm, iv) ) = dek_info. split_once ( ',' ) else {
85
+ todo ! ( )
86
+ } ;
87
+
88
+ let password = match password {
89
+ None | Some ( b"" ) => {
90
+ return Err ( CryptographyError :: from (
91
+ pyo3:: exceptions:: PyTypeError :: new_err (
92
+ "Password was not given but private key is encrypted" ,
93
+ ) ,
94
+ ) )
95
+ }
96
+ Some ( p) => p,
97
+ } ;
98
+
99
+ let cipher = match cipher_algorithm {
100
+ "AES-128-CBC" => openssl:: symm:: Cipher :: aes_128_cbc ( ) ,
101
+ "AES-256-CBC" => openssl:: symm:: Cipher :: aes_256_cbc ( ) ,
102
+ "DES-EDE3-CBC" => openssl:: symm:: Cipher :: des_ede3_cbc ( ) ,
103
+ _ => {
104
+ return Err ( CryptographyError :: from (
105
+ pyo3:: exceptions:: PyValueError :: new_err (
106
+ "Key encrypted with unknown cipher." ,
107
+ ) ,
108
+ ) )
109
+ }
110
+ } ;
111
+ let iv = utils:: hex_decode ( iv) ?;
112
+ let key = cryptography_crypto:: pbkdf1:: openssl_kdf (
113
+ openssl:: hash:: MessageDigest :: md5 ( ) ,
114
+ password,
115
+ & iv,
116
+ cipher. key_len ( ) ,
117
+ ) ?;
118
+ openssl:: symm:: decrypt ( cipher, & key, Some ( & iv) , p. contents ( ) ) . map_err ( |_| {
119
+ pyo3:: exceptions:: PyValueError :: new_err ( "Incorrect password, could not decrypt key" )
120
+ } ) ?
121
+ }
122
+ Some ( _) => {
94
123
return Err ( CryptographyError :: from (
95
- pyo3:: exceptions:: PyTypeError :: new_err (
96
- "Password was given but private key is not encrypted ." ,
124
+ pyo3:: exceptions:: PyValueError :: new_err (
125
+ "Proc-Type PEM header is not valid, key could not be decrypted ." ,
97
126
) ,
98
- ) ) ;
127
+ ) )
99
128
}
100
- return private_key_from_pkey ( py , & pkey , unsafe_skip_rsa_key_validation ) ;
101
- }
129
+ None => p . into_contents ( ) ,
130
+ } ;
102
131
103
- let _ = backend;
104
- let password = password. as_ref ( ) . map ( CffiBuf :: as_bytes) ;
105
- let mut status = utils:: PasswordCallbackStatus :: Unused ;
106
- let pkey = openssl:: pkey:: PKey :: private_key_from_pem_callback (
107
- data. as_bytes ( ) ,
108
- utils:: password_callback ( & mut status, password) ,
109
- ) ;
110
- let pkey = utils:: handle_key_load_result ( py, pkey, status, password) ?;
132
+ let pkey = match tag. as_str ( ) {
133
+ "PRIVATE KEY" => cryptography_key_parsing:: pkcs8:: parse_private_key ( & data) ?,
134
+ "ENCRYPTED PRIVATE KEY" => {
135
+ password_used = true ;
136
+ cryptography_key_parsing:: pkcs8:: parse_encrypted_private_key ( & data, password) ?
137
+ }
138
+ "RSA PRIVATE KEY" => cryptography_key_parsing:: rsa:: parse_pkcs1_private_key ( & data) ?,
139
+ "EC PRIVATE KEY" => cryptography_key_parsing:: ec:: parse_pkcs1_private_key ( & data, None ) ?,
140
+ "DSA PRIVATE KEY" => cryptography_key_parsing:: dsa:: parse_pkcs1_private_key ( & data) ?,
141
+ _ => unreachable ! ( ) ,
142
+ } ;
143
+ if password. is_some ( ) && !password_used {
144
+ return Err ( CryptographyError :: from (
145
+ pyo3:: exceptions:: PyTypeError :: new_err (
146
+ "Password was given but private key is not encrypted." ,
147
+ ) ,
148
+ ) ) ;
149
+ }
111
150
private_key_from_pkey ( py, & pkey, unsafe_skip_rsa_key_validation)
112
151
}
113
152
0 commit comments