1

I have an application that stores ssh keys. The user writes his private and public key into 2 text boxes and before storing them, my application is supposed to check if private key matches with the public key (using pycrypto). Validating a RSA pair was easy enough :

message = 'Encrypted message'

if 'ssh-rsa' in public_key:

    public_key_container = RSA.importKey(public_key)
    private_key_container = RSA.importKey(private_key)

    encrypted_message = public_key_container.encrypt(message, 0)
    decrypted_message = private_key_container.decrypt(encrypted_message)

    if message == decrypted_message:
        return True

I have found the code that seems to validate the DSA key pair, but I can't find how to extract the PQG values out of users public and private key :

elif 'ssh-dss' in public_key:

    q = "?"
    p = "?"
    g = "?"

    pub_k = ""
    for b in bytearray(public_key, 'utf-8'):
        pub_k += str(b)

    priv_k = ""
    for b in bytearray(private_key, 'utf-8'):
        priv_k += str(b)

    params = ( long(pub_k), long(g), long(p), long(q), long(priv_k))

    key = DSA.construct(params)

    if key.verify(message, key.sign(message,3)):
        return True

Please, do not prompt me to generate a public key out of the private key using a function like ssh-keygen. I know this method, I want to do it using pycrypto.

BenMorel
  • 34,448
  • 50
  • 182
  • 322
Giwrgos Tsopanoglou
  • 1,165
  • 2
  • 8
  • 15

1 Answers1

2

The current code base of PyCrypto contains some code you may find interesting:

  • One open pull request (link) validates RSA and DSA as they are constructed. The tests are more robust than what you show above, even though a malicious user may still craft a weak key and have it pass them. For DSA keys, it goes like this:

    # Modulus must be prime
    fmt_error = not isPrime(key.p)
    # Verify Lagrange's theorem for sub-group 
    fmt_error |= ((key.p-1) % key.q)!=0 
    fmt_error |= key.g<=1 or key.g>=key.p
    fmt_error |= pow(key.g, key.q, key.p)!=1 
    # Public key
    fmt_error |= key.y<=0 or key.y>=key.p 
    if hasattr(key, 'x'):
        fmt_error |= key.x<=0 or key.x>=key.q 
        fmt_error |= pow(key.g, key.x, key.p)!=key.y
    
  • The main branch (see lib/Crypto/PublicKey/DSA.py) has code to import DSA keys in SSH format:

    if extern_key.startswith(b('ssh-dss ')):
        # This is probably a public OpenSSH key
        keystring = binascii.a2b_base64(extern_key.split(b(' '))[1])
        keyparts = []
        while len(keystring) > 4:
            length = struct.unpack(">I", keystring[:4])[0]
            keyparts.append(keystring[4:4 + length])
            keystring = keystring[4 + length:]
        if keyparts[0] == b("ssh-dss"):
            tup = [bytes_to_long(keyparts[x]) for x in (4, 3, 1, 2)]
            return self.construct(tup)
    
  • It work's like charm for the public key, but I just can't understand how to make it work for the private. Do these two have to be on the same string/variable ? – Giwrgos Tsopanoglou Aug 12 '13 at 21:43
  • The 'ssh-dss' line only contains the public key. The DSA private key is typically in a separate PEM file. Is that what your users will copy and paste? That format is totally different (and it may be encrypted by a password too). The current main branch contains code for that as well. – SquareRootOfTwentyThree Aug 13 '13 at 05:29
  • The case is that I'm not an ssh key expert, so everything about ssh keys are totally new to me. Although I understand that rsa and dsa keys are used for different purposes, I know that both have public and private parts. Is that wrong? Is the private part optional for dsa? If yes, what is it's purpose? Maybe you have a link that explains all these, because I can't find one.. – Giwrgos Tsopanoglou Aug 13 '13 at 10:02
  • 1
    Both have public and private parts, and the private part is always present. The topic is not straightforward: IBM has [a few pages](http://www.ibm.com/developerworks/library/l-keyc/index.html) in the context of SSH. – SquareRootOfTwentyThree Aug 13 '13 at 13:35