Skip to content

RxJava wrapper for the Android keystore and cryptography utilities

License

Notifications You must be signed in to change notification settings

neXenio/RxKeyStore

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Travis GitHub release JitPack Codecov license

RxKeyStore

This library provides an RxJava wrapper for the Android Keystore, as well as utilities to use it for cryptographic operations.

Features

  • CRUD for keys and certificates
  • Symmetric cryptography (AES)
    • Generate secret keys
    • Encrypt & Decrypt
  • Asymmetric cryptography (RSA | EC)
    • Generate key pairs
    • Encrypt & Decrypt
    • Sign & Verify
  • Utilities
    • Base64 Encode and Decode
    • Cryptographic hash generation

Usage

Integration

You can get the latest artifacts from JitPack:

allprojects {
    repositories {
        maven { url 'https://jitpack.io' }
    }
}

dependencies {
    implementation 'com.github.neXenio:RxKeyStore:0.5.1'
}

Overview

The entrypoint of this library is RxKeyStore. It provides the functionality of the Android keystore in a reactive fashion.

To make use of it, the library also provides RxCryptoProvider interfaces, further extended in RxSymmetricCipherProvider, RxAsymmetricCipherProvider and other providers. You can implement them suiting your needs or use default implementations, e.g. AES, RSA or EC.

For usage samples, check out the instrumentation tests.

Key store

You can use an RxKeyStore instance to get or delete entries and to initialize an RxCryptoProvider.

Get a key store

To work with the default Android keystore, simply use the default constructor:

RxKeyStore androidKeyStore = new RxKeyStore();

The actual KeyStore from the Android framework will be initialized lazily once its needed. You can also directly access it using getLoadedKeyStore().

You can also use a custom keystore type and crypto provider if you prefer:

RxKeyStore bouncyCastleKeyStore = new RxKeyStore(RxKeyStore.TYPE_BKS, RxKeyStore.PROVIDER_BOUNCY_CASTLE);

You may want to use the load and save methods to restore and persist custom keystores to the filesystem, which expect an InputStream or OutputStream, respectively.

Cipher provider

An RxCipherProvider is in charge of generating keys and using them for cryptographic operations. All cipher providers either implement RxSymmetricCipherProvider or RxAsymmetricCipherProvider.

Get a cipher provider

You can use default implementations for AES, RSA or EC. You can also create your own by implementing RxSymmetricCipherProvider or RxAsymmetricCipherProvider.

RxAsymmetricCipherProvider cipherProvider = new RsaCipherProvider(keyStore);

Generate keys

// symmetric
symmetricCipherProvider.generateKey("my_fancy_key", context)
        .subscribe(secretKey -> {
            // use secret key to encrypt data?
        });

// asymmetric
asymmetricCipherProvider.generateKeyPair("my_fancy_keypair", context)
        .subscribe(keyPair -> {
            PublicKey publicKey = keyPair.getPublic();
            // transfer public key to second party?

            PrivateKey privateKey = keyPair.getPrivate();
            // use private key to sign data?
        });

Encrypt data

byte[] unencryptedBytes = ...;
Key key = ...;

cipherProvider.encrypt(unencryptedBytes, key)
        .subscribe(encryptedBytesAndIV -> {
            byte[] encryptedBytes = encryptedBytesAndIV.first;
            byte[] initializationVector = encryptedBytesAndIV.second;
            // transfer encrypted data and IV to second party?
        });

If you don't want to use a random initialization vector, you can also specify a custom one:

byte[] unencryptedBytes = ...;
byte[] initializationVector = ...;
Key key = ...;

cipherProvider.encrypt(unencryptedBytes, initializationVector, key)
        .subscribe(encryptedBytes -> {
            // transfer encrypted data and IV to second party?
        });

Decrypt data

byte[] encryptedBytes = ...;
byte[] initializationVector = ...;
Key key = ...;

cipherProvider.decrypt(encryptedBytes, initializationVector, key)
        .subscribe(decryptedBytes -> {
            // process decrypted data?
        });

Signature provider

An RxSignatureProvider is in charge of generating and verifying signatures.

Get a signature provider

You can get an RxSignatureProvider instance by using the base class and specifying the desired signature algorithm. Available signature algorithms supported by the default Android crypto provider are listed here.

RxSignatureProvider signatureProvider = new BaseSignatureProvider(keyStore, "SHA256withECDSA");

Create a signature

byte[] data = ...;
PrivateKey privateKey = ...;

signatureProvider.sign(data, privateKey)
        .subscribe(signature -> {
            // transfer signature to second party?
        });

Verify a signature

byte[] data = ...;
byte[] signature = ...;
PublicKey publicKey = ...;

signatureProvider.verify(data, signature, publicKey)
        .subscribe(() -> {
            // signature is valid!
        }, throwable -> {
            // signature is invalid!
        });

If you don't want to treat invalid signatures as an error, you can also use getVerificationResult instead of verify, which will emit a boolean that you can check.

MAC provider

An RxMacProvider is in charge of generating and verifying message authentication codes, which you can use like signatures if you only have symmetric secret keys instead of asymmetric key pairs.

Get a MAC provider

You can get an RxMacProvider instance by using the base class and specifying the desired MAC algorithm. Available MAC algorithms supported by the default Android crypto provider are listed here.

RxMacProvider macProvider = new BaseMacProvider(keyStore, "HmacSHA256");

Create a MAC

byte[] data = ...;
SecretKey secretKey = ...;

macProvider.sign(data, secretKey)
        .subscribe(mac -> {
            // transfer MAC to second party?
        });

Verify a MAC

byte[] data = ...;
byte[] mac = ...;
SecretKey secretKey = ...;

macProvider.verify(data, mac, secretKey)
        .subscribe(() -> {
            // MAC is valid
        }, throwable -> {
            // MAC is invalid
        });

Hash provider

An RxHashProvider is in charge of generating cryptographic hashes from arbitrary-sized data, useful for generating checksums or fingerprints.

Get a Hash provider

You can get an RxHashProvider instance by using the base class and specifying the desired message digest algorithm. Available message digest algorithms supported by the default Android crypto provider are listed here.

RxHashProvider hashProvider = new BaseHashProvider(keyStore, "SHA-256");

Create a hash

byte[] data = ...;

hashProvider.hash(data)
        .subscribe(hash -> {
            // use hash as checksum?
        });