Jean-Paul Calderone | abfbab6 | 2013-02-09 21:25:02 -0800 | [diff] [blame] | 1 | from time import time |
| 2 | |
Jean-Paul Calderone | 084b752 | 2013-02-09 09:53:45 -0800 | [diff] [blame] | 3 | from OpenSSL.xcrypto import * |
Jean-Paul Calderone | abfbab6 | 2013-02-09 21:25:02 -0800 | [diff] [blame] | 4 | |
| 5 | from tls.c import api as _api |
| 6 | |
| 7 | FILETYPE_PEM = _api.SSL_FILETYPE_PEM |
| 8 | FILETYPE_ASN1 = _api.SSL_FILETYPE_ASN1 |
| 9 | |
| 10 | # TODO This was an API mistake. OpenSSL has no such constant. |
| 11 | FILETYPE_TEXT = 2 ** 16 - 1 |
| 12 | |
Jean-Paul Calderone | 3e29ccf | 2013-02-19 11:32:46 -0800 | [diff] [blame] | 13 | TYPE_RSA = _api.EVP_PKEY_RSA |
| 14 | TYPE_DSA = _api.EVP_PKEY_DSA |
| 15 | |
Jean-Paul Calderone | abfbab6 | 2013-02-09 21:25:02 -0800 | [diff] [blame] | 16 | |
| 17 | def _bio_to_string(bio): |
| 18 | """ |
| 19 | Copy the contents of an OpenSSL BIO object into a Python byte string. |
| 20 | """ |
| 21 | result_buffer = _api.new('char**') |
| 22 | buffer_length = _api.BIO_get_mem_data(bio, result_buffer) |
| 23 | return _api.buffer(result_buffer[0], buffer_length)[:] |
| 24 | |
| 25 | |
| 26 | |
| 27 | def _raise_current_error(): |
| 28 | errors = [] |
| 29 | while True: |
| 30 | error = _api.ERR_get_error() |
| 31 | if error == 0: |
| 32 | break |
| 33 | errors.append(( |
| 34 | _api.string(_api.ERR_lib_error_string(error)), |
| 35 | _api.string(_api.ERR_func_error_string(error)), |
| 36 | _api.string(_api.ERR_reason_error_string(error)))) |
| 37 | |
| 38 | raise Error(errors) |
| 39 | |
| 40 | _exception_from_error_queue = _raise_current_error |
| 41 | |
| 42 | |
| 43 | class Error(Exception): |
| 44 | pass |
| 45 | |
| 46 | |
| 47 | |
| 48 | class PKey(object): |
Jean-Paul Calderone | edafced | 2013-02-19 11:48:38 -0800 | [diff] [blame] | 49 | _only_public = False |
Jean-Paul Calderone | 09e3bdc | 2013-02-19 12:15:28 -0800 | [diff] [blame] | 50 | _initialized = True |
Jean-Paul Calderone | edafced | 2013-02-19 11:48:38 -0800 | [diff] [blame] | 51 | |
Jean-Paul Calderone | abfbab6 | 2013-02-09 21:25:02 -0800 | [diff] [blame] | 52 | def __init__(self): |
Jean-Paul Calderone | 3e29ccf | 2013-02-19 11:32:46 -0800 | [diff] [blame] | 53 | self._pkey = _api.EVP_PKEY_new() |
Jean-Paul Calderone | 09e3bdc | 2013-02-19 12:15:28 -0800 | [diff] [blame] | 54 | self._initialized = False |
Jean-Paul Calderone | 3e29ccf | 2013-02-19 11:32:46 -0800 | [diff] [blame] | 55 | |
| 56 | |
| 57 | def generate_key(self, type, bits): |
| 58 | """ |
| 59 | Generate a key of a given type, with a given number of a bits |
| 60 | |
| 61 | :param type: The key type (TYPE_RSA or TYPE_DSA) |
| 62 | :param bits: The number of bits |
| 63 | |
| 64 | :return: None |
| 65 | """ |
| 66 | if not isinstance(type, int): |
| 67 | raise TypeError("type must be an integer") |
| 68 | |
| 69 | if not isinstance(bits, int): |
| 70 | raise TypeError("bits must be an integer") |
| 71 | |
| 72 | exponent = _api.new("BIGNUM**") |
| 73 | # TODO Check error return |
| 74 | # TODO Free the exponent[0] |
| 75 | _api.BN_hex2bn(exponent, "10001") |
| 76 | |
| 77 | if type == TYPE_RSA: |
| 78 | if bits <= 0: |
| 79 | raise ValueError("Invalid number of bits") |
| 80 | |
| 81 | rsa = _api.RSA_new(); |
| 82 | |
| 83 | # TODO Release GIL? |
| 84 | result = _api.RSA_generate_key_ex(rsa, bits, exponent[0], _api.NULL) |
| 85 | if result == -1: |
| 86 | 1/0 |
| 87 | |
| 88 | result = _api.EVP_PKEY_assign_RSA(self._pkey, rsa) |
| 89 | if not result: |
| 90 | 1/0 |
| 91 | |
| 92 | elif type == TYPE_DSA: |
| 93 | pass |
| 94 | else: |
| 95 | raise Error("No such key type") |
| 96 | |
Jean-Paul Calderone | 09e3bdc | 2013-02-19 12:15:28 -0800 | [diff] [blame] | 97 | self._initialized = True |
Jean-Paul Calderone | abfbab6 | 2013-02-09 21:25:02 -0800 | [diff] [blame] | 98 | |
| 99 | |
| 100 | def check(self): |
| 101 | """ |
| 102 | Check the consistency of an RSA private key. |
| 103 | |
| 104 | :return: True if key is consistent. |
| 105 | :raise Error: if the key is inconsistent. |
| 106 | :raise TypeError: if the key is of a type which cannot be checked. |
| 107 | Only RSA keys can currently be checked. |
| 108 | """ |
| 109 | if _api.EVP_PKEY_type(self._pkey.type) != _api.EVP_PKEY_RSA: |
| 110 | raise TypeError("key type unsupported") |
| 111 | |
| 112 | rsa = _api.EVP_PKEY_get1_RSA(self._pkey) |
| 113 | result = _api.RSA_check_key(rsa) |
| 114 | if result: |
| 115 | return True |
| 116 | _raise_current_error() |
| 117 | |
| 118 | |
| 119 | |
| 120 | class X509(object): |
| 121 | def __init__(self): |
| 122 | # TODO Allocation failure? And why not __new__ instead of __init__? |
| 123 | self._x509 = _api.X509_new() |
| 124 | |
| 125 | |
| 126 | def set_version(self, version): |
| 127 | """ |
| 128 | Set version number of the certificate |
| 129 | |
| 130 | :param version: The version number |
| 131 | :type version: :py:class:`int` |
| 132 | |
| 133 | :return: None |
| 134 | """ |
| 135 | if not isinstance(version, int): |
| 136 | raise TypeError("version must be an integer") |
| 137 | |
| 138 | _api.X509_set_version(self._x509, version) |
| 139 | |
| 140 | |
| 141 | def get_version(self): |
| 142 | """ |
| 143 | Return version number of the certificate |
| 144 | |
| 145 | :return: Version number as a Python integer |
| 146 | """ |
| 147 | return _api.X509_get_version(self._x509) |
| 148 | |
| 149 | |
Jean-Paul Calderone | edafced | 2013-02-19 11:48:38 -0800 | [diff] [blame] | 150 | def get_pubkey(self): |
| 151 | """ |
| 152 | Get the public key of the certificate |
| 153 | |
| 154 | :return: The public key |
| 155 | """ |
| 156 | pkey = PKey.__new__(PKey) |
| 157 | pkey._pkey = _api.X509_get_pubkey(self._x509) |
| 158 | if pkey._pkey == _api.NULL: |
| 159 | _raise_current_error() |
| 160 | pkey._only_public = True |
| 161 | return pkey |
| 162 | |
| 163 | |
Jean-Paul Calderone | 3e29ccf | 2013-02-19 11:32:46 -0800 | [diff] [blame] | 164 | def set_pubkey(self, pkey): |
| 165 | """ |
| 166 | Set the public key of the certificate |
| 167 | |
| 168 | :param pkey: The public key |
| 169 | |
| 170 | :return: None |
| 171 | """ |
| 172 | if not isinstance(pkey, PKey): |
| 173 | raise TypeError("pkey must be a PKey instance") |
| 174 | |
| 175 | set_result = _api.X509_set_pubkey(self._x509, pkey._pkey) |
| 176 | if not set_result: |
| 177 | _raise_current_error() |
| 178 | |
| 179 | |
| 180 | def sign(self, pkey, digest): |
| 181 | """ |
| 182 | Sign the certificate using the supplied key and digest |
| 183 | |
| 184 | :param pkey: The key to sign with |
| 185 | :param digest: The message digest to use |
| 186 | :return: None |
| 187 | """ |
| 188 | if not isinstance(pkey, PKey): |
| 189 | raise TypeError("pkey must be a PKey instance") |
| 190 | |
Jean-Paul Calderone | edafced | 2013-02-19 11:48:38 -0800 | [diff] [blame] | 191 | if pkey._only_public: |
| 192 | raise ValueError("Key only has public part") |
| 193 | |
Jean-Paul Calderone | 09e3bdc | 2013-02-19 12:15:28 -0800 | [diff] [blame] | 194 | if not pkey._initialized: |
| 195 | raise ValueError("Key is uninitialized") |
| 196 | |
Jean-Paul Calderone | 3e29ccf | 2013-02-19 11:32:46 -0800 | [diff] [blame] | 197 | evp_md = _api.EVP_get_digestbyname(digest) |
| 198 | if evp_md == _api.NULL: |
| 199 | raise ValueError("No such digest method") |
| 200 | |
| 201 | sign_result = _api.X509_sign(self._x509, pkey._pkey, evp_md) |
| 202 | if not sign_result: |
| 203 | _raise_current_error() |
| 204 | |
| 205 | |
Jean-Paul Calderone | e4aa3fa | 2013-02-19 12:12:53 -0800 | [diff] [blame] | 206 | def get_signature_algorithm(self): |
| 207 | """ |
| 208 | Retrieve the signature algorithm used in the certificate |
| 209 | |
| 210 | :return: A byte string giving the name of the signature algorithm used in |
| 211 | the certificate. |
| 212 | :raise ValueError: If the signature algorithm is undefined. |
| 213 | """ |
| 214 | alg = self._x509.cert_info.signature.algorithm |
| 215 | nid = _api.OBJ_obj2nid(alg) |
| 216 | if nid == _api.NID_undef: |
| 217 | raise ValueError("Undefined signature algorithm") |
| 218 | return _api.string(_api.OBJ_nid2ln(nid)) |
| 219 | |
| 220 | |
Jean-Paul Calderone | b407872 | 2013-02-19 12:01:55 -0800 | [diff] [blame] | 221 | def digest(self, digest_name): |
| 222 | """ |
| 223 | Return the digest of the X509 object. |
| 224 | |
| 225 | :param digest_name: The name of the digest algorithm to use. |
| 226 | :type digest_name: :py:class:`bytes` |
| 227 | |
| 228 | :return: The digest of the object |
| 229 | """ |
| 230 | digest = _api.EVP_get_digestbyname(digest_name) |
| 231 | if digest == _api.NULL: |
| 232 | raise ValueError("No such digest method") |
| 233 | |
| 234 | result_buffer = _api.new("char[]", _api.EVP_MAX_MD_SIZE) |
| 235 | result_length = _api.new("unsigned int[]", 1) |
| 236 | result_length[0] = len(result_buffer) |
| 237 | |
| 238 | digest_result = _api.X509_digest( |
| 239 | self._x509, digest, result_buffer, result_length) |
| 240 | |
| 241 | if not digest_result: |
| 242 | 1/0 |
| 243 | |
| 244 | return ':'.join([ |
| 245 | ch.encode('hex').upper() for ch |
| 246 | in _api.buffer(result_buffer, result_length[0])]) |
| 247 | |
| 248 | |
Jean-Paul Calderone | 7813385 | 2013-02-19 10:41:46 -0800 | [diff] [blame] | 249 | def subject_name_hash(self): |
| 250 | """ |
| 251 | Return the hash of the X509 subject. |
| 252 | |
| 253 | :return: The hash of the subject. |
| 254 | """ |
| 255 | return _api.X509_subject_name_hash(self._x509) |
| 256 | |
| 257 | |
Jean-Paul Calderone | abfbab6 | 2013-02-09 21:25:02 -0800 | [diff] [blame] | 258 | def set_serial_number(self, serial): |
| 259 | """ |
| 260 | Set serial number of the certificate |
| 261 | |
| 262 | :param serial: The serial number |
| 263 | :type serial: :py:class:`int` |
| 264 | |
| 265 | :return: None |
| 266 | """ |
Jean-Paul Calderone | 7813385 | 2013-02-19 10:41:46 -0800 | [diff] [blame] | 267 | if not isinstance(serial, (int, long)): |
Jean-Paul Calderone | abfbab6 | 2013-02-09 21:25:02 -0800 | [diff] [blame] | 268 | raise TypeError("serial must be an integer") |
| 269 | |
Jean-Paul Calderone | 7813385 | 2013-02-19 10:41:46 -0800 | [diff] [blame] | 270 | hex_serial = hex(serial)[2:] |
| 271 | if not isinstance(hex_serial, bytes): |
| 272 | hex_serial = hex_serial.encode('ascii') |
| 273 | |
| 274 | bignum_serial = _api.new("BIGNUM**") |
| 275 | |
| 276 | # BN_hex2bn stores the result in &bignum. Unless it doesn't feel like |
| 277 | # it. If bignum is still NULL after this call, then the return value is |
| 278 | # actually the result. I hope. -exarkun |
| 279 | small_serial = _api.BN_hex2bn(bignum_serial, hex_serial) |
| 280 | |
| 281 | if bignum_serial[0] == _api.NULL: |
| 282 | set_result = ASN1_INTEGER_set( |
| 283 | _api.X509_get_serialNumber(self._x509), small_serial) |
| 284 | if set_result: |
| 285 | # TODO Not tested |
| 286 | _raise_current_error() |
| 287 | else: |
| 288 | asn1_serial = _api.BN_to_ASN1_INTEGER(bignum_serial[0], _api.NULL) |
| 289 | _api.BN_free(bignum_serial[0]) |
| 290 | if asn1_serial == _api.NULL: |
| 291 | # TODO Not tested |
| 292 | _raise_current_error() |
| 293 | set_result = _api.X509_set_serialNumber(self._x509, asn1_serial) |
| 294 | if not set_result: |
| 295 | # TODO Not tested |
| 296 | _raise_current_error() |
| 297 | |
Jean-Paul Calderone | abfbab6 | 2013-02-09 21:25:02 -0800 | [diff] [blame] | 298 | |
| 299 | def get_serial_number(self): |
| 300 | """ |
| 301 | Return serial number of the certificate |
| 302 | |
| 303 | :return: Serial number as a Python integer |
| 304 | """ |
Jean-Paul Calderone | 7813385 | 2013-02-19 10:41:46 -0800 | [diff] [blame] | 305 | asn1_serial = _api.X509_get_serialNumber(self._x509) |
| 306 | bignum_serial = _api.ASN1_INTEGER_to_BN(asn1_serial, _api.NULL) |
| 307 | try: |
| 308 | hex_serial = _api.BN_bn2hex(bignum_serial) |
| 309 | try: |
| 310 | hexstring_serial = _api.string(hex_serial) |
| 311 | serial = int(hexstring_serial, 16) |
| 312 | return serial |
| 313 | finally: |
| 314 | _api.OPENSSL_free(hex_serial) |
| 315 | finally: |
| 316 | _api.BN_free(bignum_serial) |
Jean-Paul Calderone | abfbab6 | 2013-02-09 21:25:02 -0800 | [diff] [blame] | 317 | |
| 318 | |
| 319 | def gmtime_adj_notAfter(self, amount): |
| 320 | """ |
| 321 | Adjust the time stamp for when the certificate stops being valid |
| 322 | |
| 323 | :param amount: The number of seconds by which to adjust the ending |
| 324 | validity time. |
| 325 | :type amount: :py:class:`int` |
| 326 | |
| 327 | :return: None |
| 328 | """ |
| 329 | if not isinstance(amount, int): |
| 330 | raise TypeError("amount must be an integer") |
| 331 | |
| 332 | notAfter = _api.X509_get_notAfter(self._x509) |
| 333 | _api.X509_gmtime_adj(notAfter, amount) |
| 334 | |
| 335 | |
| 336 | def has_expired(self): |
| 337 | """ |
| 338 | Check whether the certificate has expired. |
| 339 | |
| 340 | :return: True if the certificate has expired, false otherwise |
| 341 | """ |
| 342 | now = int(time()) |
| 343 | notAfter = _api.X509_get_notAfter(self._x509) |
Jean-Paul Calderone | d7d8127 | 2013-02-19 13:16:03 -0800 | [diff] [blame^] | 344 | return _api.ASN1_UTCTIME_cmp_time_t( |
| 345 | _api.cast('ASN1_UTCTIME*', notAfter), now) < 0 |
Jean-Paul Calderone | abfbab6 | 2013-02-09 21:25:02 -0800 | [diff] [blame] | 346 | |
| 347 | |
| 348 | def get_notBefore(self): |
| 349 | """ |
| 350 | Retrieve the time stamp for when the certificate starts being valid |
| 351 | |
| 352 | :return: A string giving the timestamp, in the format:: |
| 353 | |
Jean-Paul Calderone | d7d8127 | 2013-02-19 13:16:03 -0800 | [diff] [blame^] | 354 | YYYYMMDDhhmmssZ |
| 355 | YYYYMMDDhhmmss+hhmm |
| 356 | YYYYMMDDhhmmss-hhmm |
Jean-Paul Calderone | abfbab6 | 2013-02-09 21:25:02 -0800 | [diff] [blame] | 357 | |
| 358 | or None if there is no value set. |
| 359 | """ |
Jean-Paul Calderone | d7d8127 | 2013-02-19 13:16:03 -0800 | [diff] [blame^] | 360 | timestamp = _api.X509_get_notBefore(self._x509) |
| 361 | string_timestamp = _api.cast('ASN1_STRING*', timestamp) |
| 362 | if _api.ASN1_STRING_length(string_timestamp) == 0: |
| 363 | return None |
| 364 | elif _api.ASN1_STRING_type(string_timestamp) == _api.V_ASN1_GENERALIZEDTIME: |
| 365 | return _api.string(_api.ASN1_STRING_data(string_timestamp)) |
| 366 | else: |
| 367 | generalized_timestamp = _api.new("ASN1_GENERALIZEDTIME**") |
| 368 | _api.ASN1_TIME_to_generalizedtime(timestamp, generalized_timestamp) |
| 369 | if generalized_timestamp[0] == _api.NULL: |
| 370 | 1/0 |
| 371 | else: |
| 372 | string_timestamp = _api.string( |
| 373 | _api.cast("char*", generalized_timestamp[0].data)) |
| 374 | _api.ASN1_GENERALIZEDTIME_free(generalized_timestamp[0]) |
| 375 | return string_timestamp |
Jean-Paul Calderone | abfbab6 | 2013-02-09 21:25:02 -0800 | [diff] [blame] | 376 | |
| 377 | |
| 378 | def set_notBefore(self, when): |
| 379 | """ |
| 380 | Set the time stamp for when the certificate starts being valid |
| 381 | |
| 382 | :param when: A string giving the timestamp, in the format: |
| 383 | |
| 384 | YYYYMMDDhhmmssZ |
| 385 | YYYYMMDDhhmmss+hhmm |
| 386 | YYYYMMDDhhmmss-hhmm |
| 387 | :type when: :py:class:`bytes` |
| 388 | |
| 389 | :return: None |
| 390 | """ |
Jean-Paul Calderone | d7d8127 | 2013-02-19 13:16:03 -0800 | [diff] [blame^] | 391 | if not isinstance(when, bytes): |
| 392 | raise TypeError("when must be a byte string") |
| 393 | |
Jean-Paul Calderone | abfbab6 | 2013-02-09 21:25:02 -0800 | [diff] [blame] | 394 | notBefore = _api.X509_get_notBefore(self._x509) |
Jean-Paul Calderone | d7d8127 | 2013-02-19 13:16:03 -0800 | [diff] [blame^] | 395 | set_result = _api.ASN1_GENERALIZEDTIME_set_string( |
| 396 | _api.cast('ASN1_GENERALIZEDTIME*', notBefore), when) |
| 397 | if set_result == 0: |
| 398 | dummy = _api.ASN1_STRING_new() |
| 399 | _api.ASN1_STRING_set(dummy, when, len(when)) |
| 400 | check_result = _api.ASN1_GENERALIZEDTIME_check( |
| 401 | _api.cast('ASN1_GENERALIZEDTIME*', dummy)) |
| 402 | if not check_result: |
| 403 | raise ValueError("Invalid string") |
| 404 | else: |
| 405 | # TODO No tests for this case |
| 406 | raise RuntimeError("Unknown ASN1_GENERALIZEDTIME_set_string failure") |
Jean-Paul Calderone | abfbab6 | 2013-02-09 21:25:02 -0800 | [diff] [blame] | 407 | |
| 408 | |
| 409 | def set_notAfter(self, when): |
| 410 | """ |
| 411 | Set the time stamp for when the certificate stops being valid |
| 412 | |
| 413 | :param when: A string giving the timestamp, in the format: |
| 414 | |
| 415 | YYYYMMDDhhmmssZ |
| 416 | YYYYMMDDhhmmss+hhmm |
| 417 | YYYYMMDDhhmmss-hhmm |
| 418 | :type when: :py:class:`bytes` |
| 419 | |
| 420 | :return: None |
| 421 | """ |
| 422 | notAfter = _api.X509_get_notAfter(self._x509) |
| 423 | _api.ASN1_GENERALIZEDTIME_set_string(notAfter, when) |
| 424 | X509Type = X509 |
| 425 | |
| 426 | |
| 427 | |
| 428 | def load_certificate(type, buffer): |
| 429 | """ |
| 430 | Load a certificate from a buffer |
| 431 | |
| 432 | :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1) |
| 433 | |
| 434 | :param buffer: The buffer the certificate is stored in |
| 435 | :type buffer: :py:class:`bytes` |
| 436 | |
| 437 | :return: The X509 object |
| 438 | """ |
| 439 | bio = _api.BIO_new_mem_buf(buffer, len(buffer)) |
| 440 | |
| 441 | try: |
| 442 | if type == FILETYPE_PEM: |
| 443 | x509 = _api.PEM_read_bio_X509(bio, _api.NULL, _api.NULL, _api.NULL) |
| 444 | elif type == FILETYPE_ASN1: |
| 445 | x509 = _api.d2i_X509_bio(bio, _api.NULL); |
| 446 | else: |
| 447 | raise ValueError( |
| 448 | "type argument must be FILETYPE_PEM or FILETYPE_ASN1") |
| 449 | finally: |
| 450 | _api.BIO_free(bio) |
| 451 | |
| 452 | if x509 == _api.NULL: |
| 453 | _raise_current_error() |
| 454 | |
| 455 | cert = X509.__new__(X509) |
| 456 | cert._x509 = x509 |
| 457 | return cert |
| 458 | |
| 459 | |
| 460 | def dump_certificate(type, cert): |
| 461 | """ |
| 462 | Dump a certificate to a buffer |
| 463 | |
| 464 | :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1) |
| 465 | :param cert: The certificate to dump |
| 466 | :return: The buffer with the dumped certificate in |
| 467 | """ |
| 468 | bio = _api.BIO_new(_api.BIO_s_mem()) |
| 469 | if type == FILETYPE_PEM: |
| 470 | result_code = _api.PEM_write_bio_X509(bio, cert._x509) |
| 471 | elif type == FILETYPE_ASN1: |
| 472 | result_code = _api.i2d_X509_bio(bio, cert._x509) |
| 473 | elif type == FILETYPE_TEXT: |
| 474 | result_code = _api.X509_print_ex(bio, cert._x509, 0, 0) |
| 475 | else: |
| 476 | raise ValueError( |
| 477 | "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or " |
| 478 | "FILETYPE_TEXT") |
| 479 | |
| 480 | return _bio_to_string(bio) |
| 481 | |
| 482 | |
| 483 | |
| 484 | def dump_privatekey(type, pkey, cipher=None, passphrase=None): |
| 485 | """ |
| 486 | Dump a private key to a buffer |
| 487 | |
| 488 | :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1) |
| 489 | :param pkey: The PKey to dump |
| 490 | :param cipher: (optional) if encrypted PEM format, the cipher to |
| 491 | use |
| 492 | :param passphrase: (optional) if encrypted PEM format, this can be either |
| 493 | the passphrase to use, or a callback for providing the |
| 494 | passphrase. |
| 495 | :return: The buffer with the dumped key in |
| 496 | :rtype: :py:data:`str` |
| 497 | """ |
| 498 | # TODO incomplete |
| 499 | bio = _api.BIO_new(_api.BIO_s_mem()) |
| 500 | |
| 501 | if type == FILETYPE_PEM: |
| 502 | result_code = _api.PEM_write_bio_PrivateKey( |
| 503 | bio, pkey._pkey, _api.NULL, _api.NULL, 0, _api.NULL, _api.NULL) |
| 504 | elif type == FILETYPE_ASN1: |
| 505 | result_code = _api.i2d_PrivateKey_bio(bio, pkey._pkey) |
| 506 | elif type == FILETYPE_TEXT: |
| 507 | rsa = _api.EVP_PKEY_get1_RSA(pkey._pkey) |
| 508 | result_code = _api.RSA_print(bio, rsa, 0) |
| 509 | # TODO RSA_free(rsa)? |
| 510 | else: |
| 511 | raise ValueError( |
| 512 | "type argument must be FILETYPE_PEM, FILETYPE_ASN1, or " |
| 513 | "FILETYPE_TEXT") |
| 514 | |
| 515 | if result_code == 0: |
| 516 | _raise_current_error() |
| 517 | |
| 518 | return _bio_to_string(bio) |
| 519 | |
| 520 | |
| 521 | |
| 522 | def load_privatekey(type, buffer, passphrase=None): |
| 523 | """ |
| 524 | Load a private key from a buffer |
| 525 | |
| 526 | :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1) |
| 527 | :param buffer: The buffer the key is stored in |
| 528 | :param passphrase: (optional) if encrypted PEM format, this can be |
| 529 | either the passphrase to use, or a callback for |
| 530 | providing the passphrase. |
| 531 | |
| 532 | :return: The PKey object |
| 533 | """ |
| 534 | # TODO incomplete |
| 535 | bio = _api.BIO_new_mem_buf(buffer, len(buffer)) |
| 536 | |
| 537 | if type == FILETYPE_PEM: |
| 538 | evp_pkey = _api.PEM_read_bio_PrivateKey(bio, _api.NULL, _api.NULL, _api.NULL) |
| 539 | elif type == FILETYPE_ASN1: |
| 540 | evp_pkey = _api.d2i_PrivateKey_bio(bio, _api.NULL) |
| 541 | else: |
| 542 | raise ValueError("type argument must be FILETYPE_PEM or FILETYPE_ASN1") |
| 543 | |
| 544 | pkey = PKey.__new__(PKey) |
| 545 | pkey._pkey = evp_pkey |
| 546 | return pkey |
| 547 | |
| 548 | |
| 549 | |
| 550 | def dump_certificate_request(type, req): |
| 551 | """ |
| 552 | Dump a certificate request to a buffer |
| 553 | |
| 554 | :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1) |
| 555 | :param req: The certificate request to dump |
| 556 | :return: The buffer with the dumped certificate request in |
| 557 | """ |
| 558 | |
| 559 | |
| 560 | |
| 561 | def load_certificate_request(type, buffer): |
| 562 | """ |
| 563 | Load a certificate request from a buffer |
| 564 | |
| 565 | :param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1) |
| 566 | :param buffer: The buffer the certificate request is stored in |
| 567 | :return: The X509Req object |
| 568 | """ |