2

I see myself in the need to use PKI in a program on Linux.

The following code summarizes the approach I would take.

Generate a key pair, store it on disk. The private key is encrypted with AES for encryption at rest (the unencrypted private key is also stored for testing purposes)

Does this reflect the current standards of the security community?

import os
import json
import base64
import getpass
from Cryptodome.Cipher import AES
from Cryptodome.PublicKey import RSA

print '################################################'

if True:
  PASSWORD = '01234567'[:32].ljust(32)
else:
  print 'Enter password (max 32 chars)'
  PASSWORD = getpass.getpass()[:32].ljust(32)

print '################################################'

GENERATE = False
if not os.path.isfile('private-key-2048.pem.encrypted'):
  GENERATE = True
  print 'GENERATING KEY PAIR'

print '################################################'
print '   CREATE / LOAD THE PRIVATE KEY'
print '################################################'

if GENERATE:

  # store the private key
  private_key = RSA.generate(2048)

  with open('private-key-2048.pem','wb') as f:
    f.write(private_key.exportKey('PEM'))

  with open('private-key-2048.pem.encrypted','wb') as f:
    cipher = AES.new(PASSWORD, AES.MODE_EAX)
    ciphertext, tag = cipher.encrypt_and_digest(private_key.exportKey('PEM'))
    print repr(tag)
    f.write(json.dumps({
        'ciphertext': base64.b64encode(ciphertext),
        'nonce': base64.b64encode(cipher.nonce),
        'tag': base64.b64encode(tag)
      }, indent=1))

else:

  # already generated, read the private key
  private_key = RSA.importKey(open('private-key-2048.pem','rb').read())
  with open('private-key-2048.pem.encrypted','rb') as f:
    data = json.loads(f.read())
    cipher = AES.new(PASSWORD, AES.MODE_EAX, base64.b64decode(data['nonce']))
    ciphertext = cipher.decrypt(base64.b64decode(data['ciphertext']))
    try:
      cipher.verify(base64.b64decode(data['tag']))
    except ValueError:
      print 'password incorrect or message corrupted'
  private_key = RSA.importKey(ciphertext)

print private_key.exportKey('PEM')

print '################################################'
print '   SAVE / LOAD THE PUBLIC KEY'
print '################################################'

if GENERATE:

  # store the public key      
  public_key = private_key.publickey()
  with open('public-key-2048.pem','wb') as f:
    f.write(public_key.exportKey('PEM'))

else:

  # already generated, read the public key      
  public_key = RSA.importKey(open('public-key-2048.pem','rb').read())

print public_key.exportKey('PEM')


print '################################################'
print '   ENCRYPT WITH PUBLIC KEY'
print '################################################'


from Cryptodome.Cipher import PKCS1_OAEP
encryptor = PKCS1_OAEP.new(public_key)
encrypted = encryptor.encrypt('this is a test')
print repr(encrypted)

print '################################################'
print '   DECRYPT WITH PRIVATE KEY'
print '################################################'

decryptor = PKCS1_OAEP.new(private_key)
decrypted = decryptor.decrypt(encrypted)
print decrypted

print '################################################'
1

Whats the target OS?

Just been reading on Windows DPAPI which is used for this process and the way its done is based off the master key and the current users passphrase to generate key material. It then uses a key stretching function to produce the key which yours is missing instead you add the passphrase direct into the AES and limit it to 32 characters.

Or if your target is windows just use the DPAPI direct to protect your keys.

2
  • Linux. Initially I used ARC4 instead of AES, would that be just as good?
    – Daniel F
    Mar 1 '18 at 7:29
  • No - haven't heard of ARC4 and looking up I see its RC4 and so this wiki link should be of value: en.wikipedia.org/wiki/RC4#Security. Short of it no - its not going to be that great.
    – McMatty
    Mar 1 '18 at 9:22
1

Generally the method of generating keys and protecting the private key with a password is commonly used. So far so good.

Using your own scheme for the encryption is not recommended however. There have been many vulnerabilities caused by that.

Most systems can handle some of the standard private key encryption methods, which ensures for example that the key derived from the password uses a good key derivation function. The two most common methods I know of are PKCS#12, as used by windows, browsers, etc, and PEM, as used by OpenSSL.

Using either of these methods will make it easier for you. It will probably be more secure, you will be interoperable with standard systems, and you will easily be able to port your code between different programming languages just be searching for how to use this specific standard in the new language.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.