Search code examples
pythonprivate-keyopensshnacl-cryptographypynacl

Can I use pyNaCl sealed boxes with an existing openssh key pair?


I'm trying to use PyNacl to do asymetric encryption (public and private ssh key pairs) to safely transmit data.

I'm using an existing key pair generated with ssh-keygen -t ed25519 in the openssh format. (more details on my code below)

The question is basically, has anyone done this with success before, and how?

After extracting what I'm fairly confident are the keys with a library called openssh key parser. (64 bytes, 32 private and then 32 public)

"private_public": "b'2\\xfbO\\xab\\xd1\\\\Ie\\xa3\\x8b\\xc9\\x16\\xe8\\xd5\\xfcc\\xdc\\xa5k+H\\tQ\\xae\"\\x1c5+\\x89Q\\xe1p\\xf5\\x01\\xe4\\xfa\\xa1<[5\\xc4\\x07\\xc8\\xf5\\xd5\\xa7\\xbb\\xa3\\xefZm\\x99\\xd7<y\\x96\\xda\\x89x\\x04\\xcc\\x0e8p'"

I use the public key to create a sealed box that will do the encryption

#load the public key
server_pubk = (OpenPublicKey.from_string(server_pubk_file_content))
#get exactly the key bytes
server_pubk = server_pubk.params.data['public']
#pass the key bytes to nacl
server_pubk = nacl.public.PublicKey(server_pubk)
#now we create the nacl SealedBox 
sealed_box = SealedBox(server_pubk)
#and encrypt a message
encrypted = sealed_box.encrypt(message)

this works as expected as far as I can tell. My issue is when I try to use the private key to create a sealedbox that will decrypt the message.

unseal_box = SealedBox(server_privk)
plaintext = unseal_box.decrypt(encrypted) #does not work with the unhelpful error traceback below :

File "script.py", line 140, in <module>
unseal_box.decrypt(encrypted)
File "/usr/lib/python3.7/site-packages/nacl/public.py", line 361, in decrypt self._private_key,
File "/usr/lib/python3.7/site-packages/nacl/bindings/crypto_box.py", line 318, in crypto_box_seal_open
raising=exc.CryptoError)
File "/usr/lib/python3.7/site-packages/nacl/exceptions.py", line 81, in ensure
raise raising(*args)
nacl.exceptions.CryptoError: An error occurred trying to decrypt the message

I poked around a bit, and noticed that when I do

server_privk = nacl.public.PrivateKey(server_privk)

to create the PrivateKey object that will be used by the SealedBox, nacl generates a public key (server_privk.public_key attribute) which does not match the public key I know is correct and used in the first SealedBox.

I tried re assigning server_privk.public_key to the same key I used to make the first box, but that gave me the same problem.

My current ideas are :

  • I'm somehow missing something on how the openssh format works (maybe not getting the right privatekey bytes, maybe I have to do a transformation on them, maybe the openssh key parser library messes things up).
  • I shouldn't use openssh, and instead transform my key format and maybe use another library to deal with the encryption.

Any answer or ideas would be appreciated :)

references : openssh parser : https://github.com/scottcwang/openssh_key_parser pyNaCl : https://pynacl.readthedocs.io/en/latest/public/


Solution

  • Ok, question solved, it is possible to use pyNaCl with ed25519, you just have to convert the key properly. Found how to do this here : gist.github.com/R-VdP/b7ac0106a4fd395ee1c37bfe6f552a36

    Kind of annoying the documentation is incomplete on that...