Search code examples
pythonrsapycrypto

Pycrypto fails inconsistantly to verify signature loaded from file


I am trying to have a program sign and then later verify the contents of a file. However, wile the first verification will always return true, once the data is written to files and then loaded again, the verification usually fails, but is sometimes successful.

Even when the code fails, The outputs of the two print signature and print hash.hexdigest() calls are visually identical.

My test code is:

from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_PSS
def generate():
    key_file = open("TestPrivateKey")
    private_key = RSA.importKey(key_file)
    public_key = private_key.publickey()

    seed_file = open("Seed")

    plaintext = seed_file.read()

    hash = SHA256.new(plaintext)
    signer = PKCS1_PSS.new(private_key)
    signature =  signer.sign(hash)

    plaintext_file = open("plaintext", 'w')
    plaintext_file.write(plaintext)
    signature_file = open("signature", 'w')
    signature_file.write(signature)
    print signature
    print hash.hexdigest()

    verifier = PKCS1_PSS.new(public_key)

    print verifier.verify(hash, signature)

def verification_test():
    plaintext_file = open("plaintext")
    signature_file = open("signature", 'rb')

    plaintext = plaintext_file.read()
    public_key = RSA.importKey(open("TestPublicKey"))
    signature = signature_file.read()
    print signature

    hash = SHA256.new(plaintext)
    print hash.hexdigest()

    verifier = PKCS1_PSS.new(public_key)
    return verifier.verify(hash, signature)


if __name__ == '__main__':
    generate()
    print verification_test()

Does anyone know what mistake I made? There must be something happening when the signature is written to the file and then read back in, but I can't figure out what it is.

Edit: Before I run this script, I run the initialization functions:

from Crypto.PublicKey import RSA

def create_keys():
    private_key = RSA.generate(4096)
    file = open("TestPrivateKey", 'w')
    file.write(private_key.exportKey())
    file = open("TestPublicKey", 'w')
    file.write(private_key.publickey().exportKey())

def create_seed():
    file = open("Seed", 'w')
    file.write("Test")

Solution

  • I notice two problems with your code.

    First, you are writing arbitrary binary data to a file what was opened for text:

    signature_file = open("signature", 'w')  #bad
    signature_file.write(signature)
    

    should be:

    signature_file = open("signature", 'wb')  #good
    signature_file.write(signature)
    

    Second, you never close your files. Try this:

    with open("signature", 'wb') as signature_file:
        signature_file.write(signature)
    

    and similarly for all of the other open files.


    from Crypto.Hash import SHA256
    from Crypto.PublicKey import RSA
    from Crypto.Signature import PKCS1_PSS
    def generate():
        with open("TestPrivateKey") as key_file:
            private_key = RSA.importKey(key_file)
        public_key = private_key.publickey()
    
        with open("Seed") as seed_file:
            plaintext = seed_file.read()
    
        hash = SHA256.new(plaintext)
        signer = PKCS1_PSS.new(private_key)
        signature =  signer.sign(hash)
    
        with open("plaintext", 'w') as plaintext_file:
            plaintext_file.write(plaintext)
        with open("signature", 'wb') as signature_file:
            signature_file.write(signature)
        #print signature
        print hash.hexdigest()
    
        verifier = PKCS1_PSS.new(public_key)
    
        print verifier.verify(hash, signature)
    
    def verification_test():
        with open("plaintext") as plaintext_file:
            plaintext = plaintext_file.read()
        with open("signature", 'rb') as signature_file:
            signature = signature_file.read()
    
        with open("TestPublicKey") as public_key_file:
            public_key = RSA.importKey(public_key_file)
        #print signature
    
        hash = SHA256.new(plaintext)
        print hash.hexdigest()
    
        verifier = PKCS1_PSS.new(public_key)
        return verifier.verify(hash, signature)
    
    
    if __name__ == '__main__':
        generate()
        print verification_test()