Using the native library
Using a local cache and the native library to perform local encryption and decryption operations considerably speeds them up.
Download or build the native library¶
The library is written in Rust and is open-sourced.
Pre-built versions are available here but in case no version matches your system, follow these instructions to build the library.
The cloudproof_java
uses Java Native Access to call the native library.
In your java project, the library needs to be placed inside in
src/main/resources/linux-x86-64
folder for a Linux Intel machinesrc/main/resources/linux-amd64
folder for a Linux AMD machinesrc/main/resources/darwin
folder for a Mac running MacOSsrc/main/resources/win32-x86
folder for a Windows machine
To run tests only, place the library inside the corresponding folder above replacing main
with test
Check the JNA documentation for alternative locations for the native library.
Coming soon!
Initialize the FFI library¶
The native code can be found in the: - on Linux, libcosmian_cover_crypt.so - on MacOS, libcosmian_cover_crypt.dylib - on Windows, cosmian_cover_crypt.dll
Using the native library for attributes encryption¶
Recovering the keys from the KMS¶
In order to perform local encryption or decryption, respectively the public key or the user decryption key must be recovered from the KMS.
Abe abe = new Abe(
new RestClient([KMS_SERVER_URL], [YOUR_API_KEY]),
new Specifications(Implementation.CoverCrypt)
);
// public key (for encryption)
PublicKey publicMasterKey =
abe.retrievePublicMasterKey(publicMasterKeyUID);
// user decryption key
PrivateKey userDecryptionKey =
abe.retrieveUserDecryptionKey(userDecryptionKeyUniqueIdentifier);
The keys can be serialized and deserialized for local storage.
Coming soon!
Encrypting¶
1. create an encryption cache¶
To speed up repeated encryptions, the public key should be loaded in memory in an encryption cache
2. encrypt¶
Attributes encryption is an hybrid encryption scheme:
- the header is a public key encryption of a randomly generated symmetric key
- the encrypted content is the symmetric encryption of the clear text using the generated symmetric key
EncryptedHeader encryptedHeader;
try {
encryptedHeader = ffi.encryptHeaderUsingCache(encryptionCacheHandle, attributes);
} catch (FfiException | CosmianException e) {
throw new AppException("Failed to encrypt the header: " + e.getMessage(), e);
}
byte[] encryptedContent;
// The data we want to encrypt/decrypt
byte[] data = "This s a test message".getBytes(StandardCharsets.UTF_8);
// A unique ID associated with this message. The unique id is used to
// authenticate the message in the AES encryption scheme.
// Typically this will be a hash of the content if it is unique, a unique
// filename or a database unique key
byte[] uid = MessageDigest.getInstance("SHA-256").digest(data);
try {
encryptedContent = ffi.encryptBlock(encryptedHeader.getSymmetricKey(), uid, 0, data);
} catch (FfiException e) {
throw new AppException("Failed to encrypt the content: " + e.getMessage(), e);
}
If you wish to prepend the symmetrically encrypted content with the encrypted header, as is done by the KMS, you can use the following
// Create a full message with header+encrypted data. The length of the header
// is pre-pended.
ByteBuffer headerSize =
ByteBuffer.allocate(4)
.order(ByteOrder.BIG_ENDIAN)
.putInt(encryptedHeader.getEncryptedHeaderBytes().length);
// Write the message
ByteArrayOutputStream bao = new ByteArrayOutputStream();
bao.write(headerSize.array());
bao.write(encryptedHeader.getEncryptedHeaderBytes());
bao.write(encryptedContent);
bao.flush();
byte[] ciphertext = bao.toByteArray();
// Parse a cipher text back into encrypted header and encrypted content
// First recover the header length
int headerSize = ByteBuffer.wrap(ciphertext).order(ByteOrder.BIG_ENDIAN).getInt(0);
// Then recover the encrypted header and encrypted content
byte[] encryptedHeader = Arrays.copyOfRange(ciphertext, 4, 4 + headerSize);
byte[] encryptedContent = Arrays.copyOfRange(ciphertext, 4 + headerSize, ciphertext.length);
Coming soon!
3. destroy the encryption cache¶
When you are done encrypting, destroy the encryption cache to recover memory
Decrypting¶
1. create a decryption cache¶
To speed up repeated decryptions, the user decryption key should be loaded in memory in a decryption cache
2. decrypt¶
Attributes encryption is an hybrid encryption scheme:
- the header is a public key encryption of a randomly generated symmetric key
- the encrypted content is the symmetric encryption of the clear text using the generated symmetric key
byte[] clearText;
try {
// If the cipher text was generated by the KMS, the encrypted header is pre-pended
// to the encrypted content
// Parse the message by first recovering the header length
int headerSize = ByteBuffer.wrap(ciphertext).order(ByteOrder.BIG_ENDIAN).getInt(0);
System.out.println("HEADER SIZE: " + headerSize);
// Then recover the encrypted header and encrypted content
byte[] encryptedHeader = Arrays.copyOfRange(ciphertext, 4, 4 + headerSize);
byte[] encryptedContent = Arrays.copyOfRange(ciphertext, 4 + headerSize, ciphertext.length);
// Decrypt he header to recover the symmetric AES key
DecryptedHeader decryptedHeader = ffi.decryptHeader(userKey, encryptedHeader);
// decrypt the content, passing the unique id and block number
clearText = ffi.decryptBlock(decryptedHeader.getSymmetricKey(), encryptedContent);
} catch (FfiException | CosmianException e) {
throw new AppException("Failed to decrypting the ciphertext: " + e.getMessage(), e);
}
Coming soon!
3. destroy the decryption cache¶
When you are done decrypting, destroy the decryption cache to recover memory