This library provides an RxJava wrapper for the Android Keystore, as well as utilities to use it for cryptographic operations.
- 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
You can get the latest artifacts from JitPack:
allprojects {
repositories {
maven { url 'https://jitpack.io' }
}
}
dependencies {
implementation 'com.github.neXenio:RxKeyStore:0.5.1'
}
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.
You can use an RxKeyStore
instance to get or delete entries and to initialize an RxCryptoProvider
.
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.
An RxCipherProvider
is in charge of generating keys and using them for cryptographic operations. All cipher providers either implement RxSymmetricCipherProvider or RxAsymmetricCipherProvider.
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);
// 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?
});
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?
});
byte[] encryptedBytes = ...;
byte[] initializationVector = ...;
Key key = ...;
cipherProvider.decrypt(encryptedBytes, initializationVector, key)
.subscribe(decryptedBytes -> {
// process decrypted data?
});
An RxSignatureProvider
is in charge of generating and verifying signatures.
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");
byte[] data = ...;
PrivateKey privateKey = ...;
signatureProvider.sign(data, privateKey)
.subscribe(signature -> {
// transfer signature to second party?
});
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.
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.
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");
byte[] data = ...;
SecretKey secretKey = ...;
macProvider.sign(data, secretKey)
.subscribe(mac -> {
// transfer MAC to second party?
});
byte[] data = ...;
byte[] mac = ...;
SecretKey secretKey = ...;
macProvider.verify(data, mac, secretKey)
.subscribe(() -> {
// MAC is valid
}, throwable -> {
// MAC is invalid
});
An RxHashProvider
is in charge of generating cryptographic hashes from arbitrary-sized data, useful for generating checksums or fingerprints.
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");
byte[] data = ...;
hashProvider.hash(data)
.subscribe(hash -> {
// use hash as checksum?
});