def importKey(self, extern_key, passphrase=None): """Import an RSA key (public or private half), encoded in standard form. :Parameter extern_key: The RSA key to import, encoded as a string. An RSA public key can be in any of the following formats: - X.509 ``subjectPublicKeyInfo`` DER SEQUENCE (binary or PEM encoding) - `PKCS#1`_ ``RSAPublicKey`` DER SEQUENCE (binary or PEM encoding) - OpenSSH (textual public key only) An RSA private key can be in any of the following formats: - PKCS#1 ``RSAPrivateKey`` DER SEQUENCE (binary or PEM encoding) - `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo`` DER SEQUENCE (binary or PEM encoding) - OpenSSH (textual public key only) For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. The private key may be encrypted by means of a certain pass phrase either at the PEM level or at the PKCS#8 level. :Type extern_key: string :Parameter passphrase: In case of an encrypted private key, this is the pass phrase from which the decryption key is derived. :Type passphrase: string :Return: An RSA key object (`_RSAobj`). :Raise ValueError/IndexError/TypeError: When the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _`PKCS#1`: http://www.ietf.org/rfc/rfc3447.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt """ extern_key = tobytes(extern_key) if passphrase is not None: passphrase = tobytes(passphrase) if extern_key.startswith(b('-----')): # This is probably a PEM encoded key. (der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase) if enc_flag: passphrase = None return self._importKeyDER(der, passphrase) if extern_key.startswith(b('ssh-rsa ')): # This is probably an OpenSSH key keystring = binascii.a2b_base64(extern_key.split(b(' '))[1]) keyparts = [] while len(keystring) > 4: l = struct.unpack(">I", keystring[:4])[0] keyparts.append(keystring[4:4 + l]) keystring = keystring[4 + l:] e = bytes_to_long(keyparts[1]) n = bytes_to_long(keyparts[2]) return self.construct([n, e]) if bord(extern_key[0]) == 0x30: # This is probably a DER encoded key return self._importKeyDER(extern_key, passphrase) raise ValueError("RSA key format is not supported")
def exportKey(self, format='PEM', passphrase=None, pkcs=1, protection=None): """Export this RSA key. :Parameters: format : string The format to use for wrapping the key: - *'DER'*. Binary encoding. - *'PEM'*. Textual encoding, done according to `RFC1421`_/`RFC1423`_. - *'OpenSSH'*. Textual encoding, done according to OpenSSH specification. Only suitable for public keys (not private keys). passphrase : string For private keys only. The pass phrase used for deriving the encryption key. pkcs : integer For *DER* and *PEM* format only. The PKCS standard to follow for assembling the components of the key. You have two choices: - **1** (default): the public key is embedded into an X.509 ``SubjectPublicKeyInfo`` DER SEQUENCE. The private key is embedded into a `PKCS#1`_ ``RSAPrivateKey`` DER SEQUENCE. - **8**: the private key is embedded into a `PKCS#8`_ ``PrivateKeyInfo`` DER SEQUENCE. This value cannot be used for public keys. protection : string The encryption scheme to use for protecting the private key. If ``None`` (default), the behavior depends on ``format``: - For *DER*, the *PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC* scheme is used. The following operations are performed: 1. A 16 byte Triple DES key is derived from the passphrase using `Crypto.Protocol.KDF.PBKDF2` with 8 bytes salt, and 1 000 iterations of `Crypto.Hash.HMAC`. 2. The private key is encrypted using CBC. 3. The encrypted key is encoded according to PKCS#8. - For *PEM*, the obsolete PEM encryption scheme is used. It is based on MD5 for key derivation, and Triple DES for encryption. Specifying a value for ``protection`` is only meaningful for PKCS#8 (that is, ``pkcs=8``) and only if a pass phrase is present too. The supported schemes for PKCS#8 are listed in the `Crypto.IO.PKCS8` module (see ``wrap_algo`` parameter). :Return: A byte string with the encoded public or private half of the key. :Raise ValueError: When the format is unknown or when you try to encrypt a private key with *DER* format and PKCS#1. :attention: If you don't provide a pass phrase, the private key will be exported in the clear! .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _`PKCS#1`: http://www.ietf.org/rfc/rfc3447.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt """ if passphrase is not None: passphrase = tobytes(passphrase) if format=='OpenSSH': eb = long_to_bytes(self.e) nb = long_to_bytes(self.n) if bord(eb[0]) & 0x80: eb=bchr(0x00)+eb if bord(nb[0]) & 0x80: nb=bchr(0x00)+nb keyparts = [ b('ssh-rsa'), eb, nb ] keystring = b('').join([ struct.pack(">I",len(kp))+kp for kp in keyparts]) return b('ssh-rsa ')+binascii.b2a_base64(keystring)[:-1] # DER format is always used, even in case of PEM, which simply # encodes it into BASE64. if self.has_private(): binary_key = newDerSequence( 0, self.n, self.e, self.d, self.p, self.q, self.d % (self.p-1), self.d % (self.q-1), inverse(self.q, self.p) ).encode() if pkcs==1: keyType = 'RSA PRIVATE' if format=='DER' and passphrase: raise ValueError("PKCS#1 private key cannot be encrypted") else: # PKCS#8 if format=='PEM' and protection is None: keyType = 'PRIVATE' binary_key = PKCS8.wrap(binary_key, oid, None) else: keyType = 'ENCRYPTED PRIVATE' if not protection: protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC' binary_key = PKCS8.wrap(binary_key, oid, passphrase, protection) passphrase = None else: keyType = "RSA PUBLIC" binary_key = newDerSequence( algorithmIdentifier, newDerBitString( newDerSequence( self.n, self.e ) ) ).encode() if format=='DER': return binary_key if format=='PEM': pem_str = PEM.encode(binary_key, keyType+" KEY", passphrase, self._randfunc) return tobytes(pem_str) raise ValueError("Unknown key format '%s'. Cannot export the RSA key." % format)