Giter VIP home page Giter VIP logo

adorsys / keystore-management Goto Github PK

View Code? Open in Web Editor NEW
19.0 6.0 11.0 906 KB

Working with Java keystore made easier. Use fluent API to generate secret/private keys and certificates, store them in keystore and read them back. Assign arbitary key metadata, write it to keystore with keys and read it back from keystore using sql-like syntax

Home Page: https://adorsys.github.io/keystore-management/

License: Apache License 2.0

Java 97.61% Shell 2.39%
java keystore fluent-api key-management jca key-generation java-keystore

keystore-management's Introduction

codecov Maintainability

Project overview

This library allows to generate keys and keystores using fluent-like API instead of dealing with JCA intricacies. Additionally to key persistence it provides capability of persisting key metadata directly within Java-KeyStore. Querying keys and their metadata is done using CQEngine under the hood - this allows writing complex queries. For example one can query for key instanceof SecretKey.

Problems solved with this library

  • AES,RSA,etc. fluent encrypting key generation.
  • RSA,DSA,etc. fluent signing key generation.
  • Fluent storing of key sets into keystore.
  • KeyStore querying for keys by alias, key type, key metadata, etc.
  • KeyStore manipulation - duplicating, changing key protection password, etc.
  • Key metadata persistence directly inside KeyStore.

Using library

Maven

Add dependency (Uses BouncyCastle security provider):

<dependency>
    <groupId>de.adorsys.keymanagement</groupId>
    <artifactId>juggler-bouncycastle</artifactId>
    <version>0.0.6</version>
</dependency>

API description

API flow diagram

API flow

Getting access to services

All services are available through Juggler interface. To obtain instance of it one should call following:

Juggler juggler = DaggerBCJuggler.builder().build();

This call will provide you with default Juggler implementation.

Juggler is composed of 5 services representing different kind of operations:

  • generateKeys() to generate Secret/Private/Signing keys(or their set) from simple template
  • toKeystore() to persist generated key set into keystore
  • readKeys() to read keys from Java keystore and query them by alias/metadata/type/...
  • decode() to decode key bytes read from keystore into i.e. String for PBE raw keys
  • serializeDeserialize() to serialize/deserialize KeyStore to/from byte array.

API examples

Generate keystore

Example:Generate keystore

// Obtain Juggler service instance:
BCJuggler juggler = DaggerBCJuggler.builder().build();

// We want our keystore to have:
KeySetTemplate template = KeySetTemplate.builder()
        .providedKey(ProvidedKey.with().alias("MY-KEY").key(stubSecretKey()).build()) // One provided key (i.e. existing) that has alias `MY-KEY`
        .generatedSecretKey(Secret.with().prefix("SEC").build()) // One generated secret key that has alias `SEC` + random UUID
        .generatedSigningKey(Signing.with().algo("DSA").alias("SIGN").build()) // One generated signing key that has alias `SIGN` + random UUID and uses DSA algorithm
        .generatedEncryptionKeys(Encrypting.with().prefix("ENC").build().repeat(10)) // Ten generated private keys (with certificates) that have alias `ENC` + random UUID
        .build();

// Provide key protection password:
Supplier<char[]> password = "PASSWORD!"::toCharArray;
// Generate key set
KeySet keySet = juggler.generateKeys().fromTemplate(template);
// Generate KeyStore
KeyStore store = juggler.toKeystore().generate(keySet, password);
// Validate that keystore has 13 keys:
// One provided, one secret, one signing, ten private
assertThat(countKeys(store)).isEqualTo(13);

Change keystore password or clone it

Example:Clone keystore and change key password

// Obtain Juggler service instance:
BCJuggler juggler = DaggerBCJuggler.builder().build();

// We want our keystore to have:
KeySetTemplate template = KeySetTemplate.builder()
        .providedKey(ProvidedKey.with().alias("MY-KEY").key(stubSecretKey()).build()) // One provided key (i.e. existing) that has alias `MY-KEY`
        .generatedEncryptionKeys(Encrypting.with().prefix("ENC").build().repeat(10)) // Ten generated private keys (with certificates) that have alias `ENC` + random UUID
        .build();

// Provide key protection password:
Supplier<char[]> password = "PASSWORD!"::toCharArray;
// Generate key set
KeySet keySet = juggler.generateKeys().fromTemplate(template);
// Generate KeyStore with each key protected with `PASSWORD!` password
KeyStore store = juggler.toKeystore().generate(keySet, password);
// Clone generated KeyStore:
Supplier<char[]> newPassword = "NEW_PASSWORD!"::toCharArray;
// Create key set from old keystore that has new password `NEW_PASSWORD!`:
KeySet clonedSet = juggler.readKeys()
        .fromKeyStore(store, id -> password.get())
        .copyToKeySet(id -> newPassword.get());
// Generate cloned KeyStore with each key protected with `NEW_PASSWORD!` password (provided on key set)
KeyStore newKeystore = juggler.toKeystore().generate(clonedSet, () -> null);

// Validate old keystore has same key count as new keystore:
assertThat(countKeys(store)).isEqualTo(countKeys(newKeystore));
// Validate old keystore has key password `PASSWORD!`
assertThat(store.getKey("MY-KEY", "PASSWORD!".toCharArray())).isNotNull();
// Validate new keystore has key password `NEW_PASSWORD!`
assertThat(newKeystore.getKey("MY-KEY", "NEW_PASSWORD!".toCharArray())).isNotNull();

Store your own char[] or String securely inside Java Keystore

It is possible your own char sequence in encrypted form inside Keystore using password-based-encryption. This way you can store any data in form of SecretKey within java KeyStore.

Example:Store your own char array securely in KeyStore

// Obtain Juggler service instance:
BCJuggler juggler = DaggerBCJuggler.builder().build();
// Generate PBE (password-based encryption) raw key (only transformed to be stored in keystore,
// encryption IS PROVIDED by keystore - i.e. BCFKS or UBER keystore provide it):
Supplier<char[]> keyPassword =  "WOW"::toCharArray;
ProvidedKey key = juggler.generateKeys().secretRaw(
        Pbe.with()
                .alias("AES-KEY") // with alias `AES-KEY` if we will save it to keystore from KeySet
                .data("MY SECRET DATA Тест!".toCharArray()) // This data will be encrypted inside KeyStore when stored
                .password(keyPassword) // Password that will be used to protect key in KeyStore
                .build()
);

// Send key to keystore
KeyStore ks = juggler.toKeystore().generate(KeySet.builder().key(key).build());

// Read key back
SecretKeySpec keyFromKeyStore = (SecretKeySpec) ks.getKey("AES-KEY", keyPassword.get());
// Note that BouncyCastle keys are encoded in PKCS12 byte format - UTF-16 big endian + 2 0's padding
assertThat(juggler.decode().decodeAsString(keyFromKeyStore.getEncoded())).isEqualTo("MY SECRET DATA Тест!");

Generate secret key

Example:Generate secret key

// Obtain Juggler service instance:
BCJuggler juggler = DaggerBCJuggler.builder().build();
// Generate key:
Key key = juggler.generateKeys().secret(
        Secret.with()
                .alias("AES-KEY") // with alias `AES-KEY` if we will save it to keystore from KeySet
                .algo("AES") // for AES encryption
                .keySize(128) // for AES-128 encryption
                .build()
).getKey();

assertThat(key.getAlgorithm()).isEqualTo("AES");
assertThat(key.getEncoded()).hasSize(16); // 16 * 8 (sizeof byte) = 128 bits

Open and analyze keystore

Example:Query keystore

// Obtain Juggler service
BCJuggler juggler = DaggerBCJuggler.builder().build();


KeySetTemplate template = KeySetTemplate.builder()
        .generatedSecretKey(Secret.with().prefix("SEC").build()) // Secret key to be generated with name `SEC` + random UUID
        .generatedSigningKey(Signing.with().algo("DSA").alias("SIGN-1").build()) // DSA-based signing key with name `SIGN-1`
        .generatedEncryptionKey(Encrypting.with().alias("ENC-1").build()) // Private key with name `ENC-1`
        .generatedEncryptionKeys(Encrypting.with().prefix("GEN").build().repeat(10)) // Ten private keys with name `GEN` + random UUID
        .build();

// Generate key set from template:
KeySet keySet = juggler.generateKeys().fromTemplate(template);
// Key protection password:
Supplier<char[]> password = "PASSWORD!"::toCharArray;
// Create KeyStore
KeyStore store = juggler.toKeystore().generate(keySet, password);
// Open KeyStore-view to query it:
KeyStoreView source = juggler.readKeys().fromKeyStore(store, id -> password.get());
// Acquire Key-Entry view, so we can query for KeyEntry entities
EntryView<Query<KeyEntry>> entryView = source.entries();
// Query for fact that KeyStore has 13 keys in total:
assertThat(entryView.all()).hasSize(13);
// Query for fact that KeyStore has 1 key with name `SEC`
assertThat(entryView.retrieve("SELECT * FROM keys WHERE alias = 'ENC-1'").toCollection()).hasSize(1);
// Query for fact that KeyStore has 10 keys with prefix `GEN`
assertThat(entryView.retrieve("SELECT * FROM keys WHERE alias LIKE 'GEN%'").toCollection()).hasSize(10);
// Query for fact that KeyStore has 1 secret key:
assertThat(entryView.retrieve("SELECT * FROM keys WHERE is_secret = true").toCollection()).hasSize(1);

// Query for fact that KeyStore has 1 secret key:
assertThat(entryView.privateKeys()).hasSize(12);
// Query for fact that KeyStore has 1 secret key:
assertThat(entryView.secretKeys()).hasSize(1);
// Query for fact that KeyStore has 0 trusted certs:
assertThat(entryView.trustedCerts()).hasSize(0);

Persist key with metadata into keystore

Example:Save metadata to keystore

// Obtain Juggler service
BCJuggler juggler = DaggerBCJuggler.builder()
        .metadataPersister(new WithPersister()) // enable metadata persistence
        .metadataConfig(
                MetadataPersistenceConfig.builder()
                        .metadataClass(KeyExpirationMetadata.class) // define metadata class
                        .build()
        )
        .build();

// Key set template that is going to be saved into KeyStore
KeySetTemplate template = KeySetTemplate.builder()
        // One private key that can be used for encryption:
        .generatedEncryptionKey(
                Encrypting.with()
                        .alias("ENC-KEY-1") // key with alias `ENC-KEY-1` in KeyStore
                        .metadata(new KeyExpirationMetadata(Instant.now())) // Associated metadata with this key, pretend it is `expired` key
                        .build()
        )
        .build();

// Generate key set:
KeySet keySet = juggler.generateKeys().fromTemplate(template);
// Key protection password:
Supplier<char[]> password = "PASSWORD!"::toCharArray;
// Generate new KeyStore, it will have metadata in it
KeyStore ks = juggler.toKeystore().generate(keySet, password);
// Open KeyStore view to query it:
KeyStoreView source = juggler.readKeys().fromKeyStore(ks, id -> password.get());
// Open alias view to query key alias by metadata
AliasView<Query<KeyAlias>> view = source.aliases();
// Assert that key has been expired
assertThat(
        view.retrieve(
                and(
                        has(META), // Key has metadata
                        lessThan(
                                attribute(key -> ((KeyExpirationMetadata) key.getMeta()).getExpiresAfter()), // Key expiration date
                                Instant.now() // current date, so that if expiresAfter < now() key is expired
                        )
                )
        ).toCollection()
).hasSize(1);

Update key in keystore based on its metadata

Example:Rotate expired key in keystore

// Obtain Juggler service
BCJuggler juggler = DaggerBCJuggler.builder()
        .metadataPersister(new WithPersister()) // enable metadata persistence
        .metadataConfig(
                MetadataPersistenceConfig.builder()
                        .metadataClass(KeyValidity.class) // define metadata class
                        .build()
        )
        .build();

// Key protection password:
Supplier<char[]> password = "PASSWORD!"::toCharArray;

// Lazy key template:
Function<Instant, Encrypting> keyTemplate = expiryDate -> Encrypting.with()
        .alias("ENC-KEY-1") // key with alias `ENC-KEY-1` in KeyStore// Associated metadata with this key, pretend it is `expired` key
        .metadata(new KeyValidity(expiryDate))
        .password(password)
        .build();

// Key set template that is going to be saved into KeyStore
KeySetTemplate template = KeySetTemplate.builder()
        // One private key that can be used for encryption:
        .generatedEncryptionKey(
                keyTemplate.apply(Instant.now().minusSeconds(10)) // Will pretend that key has expired
        )
        .build();

// Generate key set:
KeySet keySet = juggler.generateKeys().fromTemplate(template); // Key metadata will indicate that key has expired
// Generate new KeyStore, it will have metadata in it
KeyStore ks = juggler.toKeystore().generate(keySet, () -> null);
// Open KeyStore view to query it:
KeyStoreView source = juggler.readKeys().fromKeyStore(ks, id -> password.get());
// Open alias view to query key alias by metadata
AliasView<Query<KeyAlias>> view = source.aliases();
// Find expired key:
KeyAlias expired = view.uniqueResult(KeyValidity.EXPIRED);
// replace expired key:
view.update(
        Collections.singleton(expired),
        Collections.singleton(
                juggler.generateKeys().encrypting(
                        keyTemplate.apply(Instant.now().plus(10, ChronoUnit.HOURS)) // Valid for 10 hours from now
                )
        )
);
// validate there is only one `ENC-KEY-1` key
assertThat(view.retrieve(equal(A_ID, "ENC-KEY-1")).toCollection()).hasSize(1);
// and this key is NOT expired
assertThat(view.retrieve(KeyValidity.EXPIRED).toCollection()).hasSize(0);

Project details

Main service provider - Juggler is built using Dagger2 framework. This allows user to re-compose this service in his own project by providing replacing modules.

JavaDoc

You can read JavaDoc here

keystore-management's People

Contributors

dependabot[bot] avatar jatiim avatar max402 avatar mme-adorsys avatar valb3r avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

keystore-management's Issues

bug on [email protected] with CQEngine

Caused by: java.lang.IllegalStateException: Could not resolve sufficient generic type information from the given function of type: de.adorsys.keymanagement.core.view.EntryViewImpl$$Lambda$1355/000000002649A4F0, resolved: [class net.jodah.typetools.TypeResolver$Unknown, class net.jodah.typetools.TypeResolver$Unknown]. If the function you supplied was created from a lambda expression, then it's likely that the host JVM does not allow the generic type information to be read from lambda expressions. Alternatively, if you supplied a class-based implementation of the function, then you must ensure that you specified the generic types of the function when it was compiled. As a workaround, you can use the counterpart methods in QueryFactory which allow the generic types to be specified explicitly. at com.googlecode.cqengine.query.QueryFactory.validateSimpleFunctionGenericTypes(QueryFactory.java:1084) at com.googlecode.cqengine.query.QueryFactory.resolveSimpleFunctionGenericTypes(QueryFactory.java:1070) at com.googlecode.cqengine.query.QueryFactory.attribute(QueryFactory.java:848) at de.adorsys.keymanagement.core.view.EntryViewImpl.<clinit>(EntryViewImpl.java:35)

https://github.com/npgall/cqengine/blob/master/documentation/LambdaAttributes.md

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.