-
Notifications
You must be signed in to change notification settings - Fork 3
Oulipo Keystore
The Oulipo Keystore is an easy, portable way to store EC keys.
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.
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 |
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.
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.
http://en.wikipedia.org/wiki/Scrypt
https://github.com/wg/scrypt
http://self-issued.info/docs/draft-ietf-jose-json-web-key.html
- Overview
- Dependencies
- Document
- Object Model
- TED Services
- Security
- Storage Service