Generating SSH keypair with paramiko in Python
Question: I am trying to generate a SSH key pair with the python module paramiko. There
doesn't seem to be much info about key generation. I've read through the
paramiko docs but can't figure out whats wrong. I can generate a private and
public key without password encryption. However, when I try to encrypt the
private key I get the following error.

**ValueError: IV must be 8 bytes long**

I believe the above error is from pycrypto. I've looked through the relevant
code in paramiko.pkey and pycrypto without any luck.

Here is a small example.

    
    
    import paramiko
    
    def keygen(filename,passwd=None,bits=1024):
        k = paramiko.RSAKey.generate(bits)
        #This line throws the error.
        k.write_private_key_file(filename,password = 'cleverpassword')
        o = open(fil+'.pub' ,"w").write(k.get_base64())
    

_traceback_

    
    
    Traceback (most recent call last):
      File "/var/mobile/Applications/149E4C21-2F92-4712-BAC6-151A171C6687/Documents/test.py", line 14, in keygen
        k.write_private_key_file(filename,password = 'cleverpassword')
      File "/var/mobile/Applications/149E4C21-2F92-4712-BAC6-151A171C6687/Pythonista.app/pylib/site-packages/paramiko/rsakey.py", line 127, in write_private_key_file
        self._write_private_key_file('RSA', filename, self._encode_key(), password)
      File "/var/mobile/Applications/149E4C21-2F92-4712-BAC6-151A171C6687/Pythonista.app/pylib/site-packages/paramiko/pkey.py", line 323, in _write_private_key_file
        self._write_private_key(tag, f, data, password)
      File "/var/mobile/Applications/149E4C21-2F92-4712-BAC6-151A171C6687/Pythonista.app/pylib/site-packages/paramiko/pkey.py", line 341, in _write_private_key
        data = cipher.new(key, mode, salt).encrypt(data)
      File "/var/mobile/Applications/149E4C21-2F92-4712-BAC6-151A171C6687/Pythonista.app/pylib/site-packages/Crypto/Cipher/DES3.py", line 114, in new
        return DES3Cipher(key, *args, **kwargs)
      File "/var/mobile/Applications/149E4C21-2F92-4712-BAC6-151A171C6687/Pythonista.app/pylib/site-packages/Crypto/Cipher/DES3.py", line 76, in __init__
        blockalgo.BlockAlgo.__init__(self, _DES3, key, *args, **kwargs)
      File "/var/mobile/Applications/149E4C21-2F92-4712-BAC6-151A171C6687/Pythonista.app/pylib/site-packages/Crypto/Cipher/blockalgo.py", line 141, in __init__
        self._cipher = factory.new(key, *args, **kwargs)
    ValueError: IV must be 8 bytes long
    


Answer: ### The Problem

This looks like a bug in `paramiko`.

If you look at the line that threw the error in `pkey.py`, it is the following
line:

    
    
            data = cipher.new(key, mode, salt).encrypt(data)
    

Let us now look at the lines before it, which set the `mode` by first
selecting a `cipher_name`.

    
    
            # since we only support one cipher here, use it
            cipher_name = list(self._CIPHER_TABLE.keys())[0]
            cipher = self._CIPHER_TABLE[cipher_name]['cipher']
            keysize = self._CIPHER_TABLE[cipher_name]['keysize']
            blocksize = self._CIPHER_TABLE[cipher_name]['blocksize']
            mode = self._CIPHER_TABLE[cipher_name]['mode']
    

Here are the contents of `_CIPHER_TABLE`.

    
    
    _CIPHER_TABLE = {
        'AES-128-CBC': {'cipher': AES, 'keysize': 16, 'blocksize': 16, 'mode': AES.MODE_CBC},
        'DES-EDE3-CBC': {'cipher': DES3, 'keysize': 24, 'blocksize': 8, 'mode': DES3.MODE_CBC},
    }
    

Observe how the comment contradicts the code. Two ciphers are available, and
the line above which selects the `cipher_name` assumes there is only one.

Based on the error, it appears that `'DES-EDE3-CBC'` is selected. If we look
at the comment in `DES3.py`, we see the following requirement for an IV.

    
    
      IV : byte string
        The initialization vector to use for encryption or decryption.
    
        It is ignored for `MODE_ECB` and `MODE_CTR`.
    
        For `MODE_OPENPGP`, IV must be `block_size` bytes long for encryption
        and `block_size` +2 bytes for decryption (in the latter case, it is
        actually the *encrypted* IV which was prefixed to the ciphertext).
        It is mandatory.
    

From paramiko's source, we observe that no `IV` is passed, and hence the error
we saw.

### Workaround

Change the following line in `pkey.py` to hardcode the `'AES-128-CBC'` cipher
instead.

    
    
       # cipher_name = list(self._CIPHER_TABLE.keys())[1]
       cipher_name = 'AES-128-CBC'
    

