2 * Copyright (C) 2012 Free Software Foundation, Inc.
4 * Author: David Woodhouse
6 * This file is part of GnuTLS.
8 * The GnuTLS is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 3 of
11 * the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>
23 #include <gnutls_int.h>
25 #include <gnutls_datum.h>
26 #include <gnutls_global.h>
27 #include <gnutls_errors.h>
28 #include <gnutls_rsa_export.h>
30 #include <gnutls_x509.h>
33 #include <algorithms.h>
34 #include <gnutls_num.h>
36 #include <pbkdf2-sha1.h>
39 openssl_hash_password (const char *pass
, gnutls_datum_t
* key
, gnutls_datum_t
* salt
)
41 unsigned char md5
[16];
42 gnutls_hash_hd_t hash
;
43 unsigned int count
= 0;
46 while (count
< key
->size
)
48 err
= gnutls_hash_init (&hash
, GNUTLS_DIG_MD5
);
56 err
= gnutls_hash (hash
, md5
, sizeof (md5
));
60 gnutls_hash_deinit (hash
, NULL
);
67 err
= gnutls_hash (hash
, pass
, strlen (pass
));
74 err
= gnutls_hash (hash
, salt
->data
, 8);
81 gnutls_hash_deinit (hash
, md5
);
83 if (key
->size
- count
<= sizeof (md5
))
85 memcpy (&key
->data
[count
], md5
, key
->size
- count
);
89 memcpy (&key
->data
[count
], md5
, sizeof (md5
));
90 count
+= sizeof (md5
);
96 static const struct pem_cipher
{
98 gnutls_cipher_algorithm_t cipher
;
100 { "DES-CBC", GNUTLS_CIPHER_DES_CBC
},
101 { "DES-EDE3-CBC", GNUTLS_CIPHER_3DES_CBC
},
102 { "AES-128-CBC", GNUTLS_CIPHER_AES_128_CBC
},
103 { "AES-192-CBC", GNUTLS_CIPHER_AES_192_CBC
},
104 { "AES-256-CBC", GNUTLS_CIPHER_AES_256_CBC
},
105 { "CAMELLIA-128-CBC", GNUTLS_CIPHER_CAMELLIA_128_CBC
},
106 { "CAMELLIA-192-CBC", GNUTLS_CIPHER_CAMELLIA_192_CBC
},
107 { "CAMELLIA-256-CBC", GNUTLS_CIPHER_CAMELLIA_256_CBC
},
111 * gnutls_x509_privkey_import_openssl:
112 * @key: The structure to store the parsed key
113 * @data: The DER or PEM encoded key.
114 * @password: the password to decrypt the key (if it is encrypted).
116 * This function will convert the given PEM encrypted to
117 * the native gnutls_x509_privkey_t format. The
118 * output will be stored in @key.
120 * The @password should be in ASCII. If the password is not provided
121 * or wrong then %GNUTLS_E_DECRYPTION_FAILED will be returned.
123 * If the Certificate is PEM encoded it should have a header of
124 * "PRIVATE KEY" and the "DEK-Info" header.
126 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
127 * negative error value.
130 gnutls_x509_privkey_import_openssl (gnutls_x509_privkey_t key
,
131 const gnutls_datum_t
*data
, const char* password
)
133 gnutls_cipher_hd_t handle
;
134 gnutls_cipher_algorithm_t cipher
= GNUTLS_CIPHER_UNKNOWN
;
135 gnutls_datum_t b64_data
;
136 gnutls_datum_t salt
, enc_key
;
137 unsigned char *key_data
;
138 const char *pem_header
= (void*)data
->data
;
139 const char *pem_header_start
= (void*)data
->data
;
140 ssize_t pem_header_size
;
142 unsigned int i
, iv_size
, l
;
144 pem_header_size
= data
->size
;
146 pem_header
= memmem(pem_header
, pem_header_size
, "PRIVATE KEY---", 14);
147 if (pem_header
== NULL
)
150 return GNUTLS_E_PARSING_ERROR
;
153 pem_header_size
-= (ptrdiff_t)(pem_header
-pem_header_start
);
155 pem_header
= memmem(pem_header
, pem_header_size
, "DEK-Info: ", 10);
156 if (pem_header
== NULL
)
159 return GNUTLS_E_PARSING_ERROR
;
162 pem_header_size
= data
->size
- (ptrdiff_t)(pem_header
-pem_header_start
) - 10;
165 for (i
= 0; i
< sizeof(pem_ciphers
)/sizeof(pem_ciphers
[0]); i
++)
167 l
= strlen(pem_ciphers
[i
].name
);
168 if (!strncmp(pem_header
, pem_ciphers
[i
].name
, l
) &&
169 pem_header
[l
] == ',')
172 cipher
= pem_ciphers
[i
].cipher
;
177 if (cipher
== GNUTLS_CIPHER_UNKNOWN
)
179 _gnutls_debug_log ("Unsupported PEM encryption type: %.10s\n", pem_header
);
181 return GNUTLS_E_INVALID_REQUEST
;
184 iv_size
= _gnutls_cipher_get_iv_size(cipher
);
186 salt
.data
= gnutls_malloc (salt
.size
);
188 return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR
);
190 for (i
= 0; i
< salt
.size
* 2; i
++)
193 const char *c
= &pem_header
[i
];
195 if (*c
>= '0' && *c
<= '9')
197 else if (*c
>= 'A' && *c
<= 'F')
202 /* Invalid salt in encrypted PEM file */
203 ret
= GNUTLS_E_INVALID_REQUEST
;
207 salt
.data
[i
/ 2] |= x
;
209 salt
.data
[i
/ 2] = x
<< 4;
212 pem_header
+= salt
.size
* 2;
213 if (*pem_header
!= '\r' && *pem_header
!= '\n')
216 ret
= GNUTLS_E_INVALID_REQUEST
;
219 while (*pem_header
== '\n' || *pem_header
== '\r')
222 ret
= _gnutls_base64_decode((const void*)pem_header
, pem_header_size
, &b64_data
);
229 if (b64_data
.size
< 16)
231 /* Just to be sure our parsing is OK */
233 ret
= GNUTLS_E_PARSING_ERROR
;
237 ret
= GNUTLS_E_MEMORY_ERROR
;
238 enc_key
.size
= gnutls_cipher_get_key_size (cipher
);
239 enc_key
.data
= gnutls_malloc (enc_key
.size
);
242 ret
= gnutls_assert_val(GNUTLS_E_MEMORY_ERROR
);
246 key_data
= gnutls_malloc (b64_data
.size
);
249 ret
= gnutls_assert_val(GNUTLS_E_MEMORY_ERROR
);
255 memcpy (key_data
, b64_data
.data
, b64_data
.size
);
257 ret
= openssl_hash_password (password
, &enc_key
, &salt
);
264 ret
= gnutls_cipher_init (&handle
, cipher
, &enc_key
, &salt
);
268 gnutls_cipher_deinit (handle
);
272 ret
= gnutls_cipher_decrypt (handle
, key_data
, b64_data
.size
);
273 gnutls_cipher_deinit (handle
);
281 /* We have to strip any padding to accept it.
282 So a bit more ASN.1 parsing for us.*/
283 if (key_data
[0] == 0x30)
285 gnutls_datum_t key_datum
;
286 unsigned int blocksize
= gnutls_cipher_get_block_size (cipher
);
287 unsigned int keylen
= key_data
[1];
288 unsigned int ofs
= 2;
292 int lenlen
= keylen
& 0x7f;
304 keylen
|= key_data
[ofs
++];
310 /* If there appears to be more padding than required, fail */
311 if (b64_data
.size
- keylen
> blocksize
)
317 /* If the padding bytes aren't all equal to the amount of padding, fail */
319 while (ofs
< b64_data
.size
)
321 if (key_data
[ofs
] != b64_data
.size
- keylen
)
329 key_datum
.data
= key_data
;
330 key_datum
.size
= keylen
;
332 gnutls_x509_privkey_import (key
, &key_datum
,
333 GNUTLS_X509_FMT_DER
);
338 ret
= GNUTLS_E_DECRYPTION_FAILED
;
342 gnutls_free (key_data
);
344 gnutls_free (enc_key
.data
);
346 gnutls_free (b64_data
.data
);
348 gnutls_free (salt
.data
);