Search code examples
pythonpython-cryptographypynacl

How to load SigningKey from its value in pyNaCl?


I need to generate signing and private keys in pyNaCl and store them somewhere. After that, I need to make it possible to load them from a string.

This is a code that generates new SigningKey and public key.

def gen_keys():
    global sk
    sk = nacl.signing.SigningKey.generate()
    global pk
    pk = sk.verify_key
    pk = pk.encode(encoder=nacl.encoding.HexEncoder)
    sk = sk.encode(encoder=nacl.encoding.HexEncoder)
    pk_list.insert(INSERT, "=PRIVATE=\n")
    pk_list.insert(INSERT, sk)
    pk_list.insert(INSERT, "\n=PUBLIC=\n")
    pk_list.insert(INSERT, pk)
    pk_list.insert(INSERT, "\n\n")

I expect to load SigningKey from its value, but the only available option is to generate a new one using a seed.


Solution

  • This code will do what you want:

    import nacl.encoding
    import nacl.signing
    import os
    
    # Generate a new signing key
    signing_key_original = nacl.signing.SigningKey.generate()
    # Obtain its verify key (which we will use to test the reloaded signing key)
    verify_key = signing_key_original.verify_key
    # Export the signing key to hex (binary)
    private_key_bytes = signing_key_original.encode(encoder=nacl.encoding.HexEncoder)
    # Save the signing key to the environment variables
    if os.supports_bytes_environ:
        # The operating system supports hex-encoded environment variables
        os.environb['private_key_bytes'] = private_key_bytes
    else:
        # The operating system does not support hex-encoded environment variables - decode to utf-8
        private_key_utf8 = private_key_bytes.decode('utf-8')
        os.environ['private_key_utf8'] = private_key_utf8
    
    # Now reverse the process (by loading a signing key from the appropriate environment variable)
    if os.supports_bytes_environ:
        # The operating system supports hex-encoded environment variable
        private_key_bytes_loaded = os.environb['private_key_bytes']
    else:
        # The operating system does not support hex-encoded environment variables - look for the utf-8 encoded private key
        private_key_utf8_loaded = os.environ['private_key_utf8']
        private_key_bytes_loaded = str.encode(private_key_utf8_loaded)
    
    # Create a PyNaCL signing key from the loaded private key
    signing_key_loaded = nacl.signing.SigningKey(private_key_bytes_loaded, encoder=nacl.encoding.HexEncoder)
    # Sign a new message
    signed_with_loaded = signing_key_loaded.sign(b"PyNaCl signing keys can be exported, saved and loaded")
    # Verify the new signed message with the original verify key (the one we created with signing_key_original)
    verify_key.verify(signed_with_loaded)