Skip to content

Oulipo Keystore

Shane Isbell edited this page Aug 19, 2017 · 1 revision

The Oulipo Keystore is an easy, portable way to store EC keys.

Motivation

There are lot of existing formats for key stores, everything from jks to p12 to pfx. It is complicated getting all the bits in place for storing a key in the correct format for a given key store implementation. There should be an easy to implement, portable format that works across a range of devices, including mobile and web.

This portability is needed to make one's identity and keys interoperable across different platforms. As a JSON format, it makes it easier to use within web applications.

The concept of a JSON based keystore is not new, as it has been implemented for the JSON Web Key concept used in OpenID Connect.

Specification

The implementation only needs to support EC keys so this simplifies the process. The format is in JSON.

{  
   "keys":[  
      {  
         "alias":"1.0.1.0.3.5",
         "crv":"P-256",
         "iv":"kFUrk3F0yE0NC9kXi6XHpw==",
         "kty":"EC",
         "pk":"TDkehBlIVE_0P4wSvQFFm_sI5X9KGQ9-ZWXmuix2l3ur6mGTQoxvD6b3iRV1B6ec"
      }
   ],
   "scrypt":{  
      "salt":"H5MtloYsaew=",
      "p":1,
      "n":512,
      "r":8
   }
}

To encrypt passwords, the scrypt protocol is required to be supported. This is a password-based KDF.

Param Description
n CPU/memory cost parameter
p Parallelization parameter
r Blocksize in use for underlying hash
salt Salt for the password (base64Url encoded)

The other element is the array of key entries. Each key does not have an individual password. Key entries are encrypted using the global password in the scrypt element.

Param Description
alias The alias for the key
crv Curve
iv Initialization Vector
kty type
pk Encrypted base64Url encoded private key

Reference Code

To import a keystore, a typical case to create a key would look like:

ObjectMapper mapper = new ObjectMapper();
KeyStore keystore = mapper.readValue(new FileInputStream(file), KeyStore.class);

Protos.ScryptParameters.Builder scryptParametersBuilder
    = Protos.ScryptParameters.newBuilder()
     .setSalt(ByteString.copyFrom(base64Url().decode(keystore.getScrypt().getSalt())))
     .setN(keystore.getScrypt().getN())
     .setR(keystore.getScrypt().getR())
     .setP(keystore.getScrypt().getP());

KeyCrypterScrypt crypterScrypt 
    = new KeyCrypterScrypt(scryptParametersBuilder.build());
KeyParameter aesKey = crypterScrypt.deriveKey(password);

for (KeyEntry entry : keystore.getKeys()) {
    byte[] key = base64Url().decode(entry.getPk());
    byte[] iv = base64Url().decode(entry.getIv());

    EncryptedData data = new EncryptedData(iv, key);
    ECKey ecKey = ECKey.fromPrivate(crypterScrypt.decrypt(data, aesKey));
}

Notice that we are using an ObjectMapper to read in the scrypt parameters from a JSON file. These parameters combined with the user's password allow us to reconstruct the AES key. This key is then used to decrypt our private EC keys, which we then use to identity the user and to obtain session tokens.

Classes

The reference code above covered how the KeyStore works. But there are other parts. From the class diagram below, you can see that the ECKeyStore is the core API for managing keys.

For loading previously saved keys, ECKeyStore provides the method ECKeystore.load(File, String[password], Storage). This method uses an instance of the Storage class to load a JSON keystore file from the specified File. The KeyCrypterScrypt uses the password String to derive the AES key used to decrypt the users private keys.

ECKeystore also has an export method for reversing this process and encypting the private keys before writing them to a file.

Keystore

References

http://en.wikipedia.org/wiki/Scrypt

https://github.com/wg/scrypt

http://self-issued.info/docs/draft-ietf-jose-json-web-key.html

Clone this wiki locally