{"id":18951304,"url":"https://github.com/adorsys/keystore-management","last_synced_at":"2025-04-13T10:13:25.402Z","repository":{"id":35497977,"uuid":"216526613","full_name":"adorsys/keystore-management","owner":"adorsys","description":"Generate keys and keystores using fluent-like API instead of dealing with JCA intricacies","archived":false,"fork":false,"pushed_at":"2024-05-17T15:30:53.000Z","size":930,"stargazers_count":20,"open_issues_count":1,"forks_count":11,"subscribers_count":4,"default_branch":"develop","last_synced_at":"2025-04-13T10:13:16.852Z","etag":null,"topics":["fluent-api","java","java-keystore","jca","key-generation","key-management","keystore"],"latest_commit_sha":null,"homepage":"https://adorsys.github.io/keystore-management/","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/adorsys.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"security-provider-adapters/bouncycastle-adapter/pom.xml","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-10-21T09:16:28.000Z","updated_at":"2024-05-29T08:26:13.000Z","dependencies_parsed_at":"2022-07-27T13:49:14.371Z","dependency_job_id":"35faba38-0ab3-45b2-b2fd-b7823bb55c5b","html_url":"https://github.com/adorsys/keystore-management","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adorsys%2Fkeystore-management","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adorsys%2Fkeystore-management/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adorsys%2Fkeystore-management/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adorsys%2Fkeystore-management/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adorsys","download_url":"https://codeload.github.com/adorsys/keystore-management/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248695482,"owners_count":21146956,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fluent-api","java","java-keystore","jca","key-generation","key-management","keystore"],"created_at":"2024-11-08T13:27:27.441Z","updated_at":"2025-04-13T10:13:25.366Z","avatar_url":"https://github.com/adorsys.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![codecov](https://codecov.io/gh/adorsys/keystore-management/branch/develop/graph/badge.svg)](https://codecov.io/gh/adorsys/keystore-management)\n[![Maintainability](https://codeclimate.com/github/adorsys/keystore-management.png)](https://codeclimate.com/github/adorsys/keystore-management)\n\n#  Project overview\nThis library allows to generate keys and keystores using fluent-like API instead of dealing with JCA intricacies.\nAdditionally to key persistence it provides capability of persisting key metadata directly within Java-KeyStore.\nQuerying keys and their metadata is done using CQEngine under the hood - this allows writing complex queries.\nFor example one can query for `key instanceof SecretKey`.\n\n#  Problems solved with this library\n\n-  AES,RSA,etc. fluent encrypting key generation.\n-  RSA,DSA,etc. fluent signing key generation.\n-  Fluent storing of key sets into keystore.\n-  KeyStore querying for keys by alias, key type, key metadata, etc.\n-  KeyStore manipulation - duplicating, changing key protection password, etc.\n-  Key metadata persistence directly inside KeyStore.\n\n# Using library\n\n## Maven\n\nAdd dependency (Uses BouncyCastle security provider):\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ede.adorsys.keymanagement\u003c/groupId\u003e\n    \u003cartifactId\u003ejuggler-bouncycastle\u003c/artifactId\u003e\n    \u003cversion\u003e0.0.6\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n#  API description\n\n### API flow diagram\n\n![API flow](http://www.plantuml.com/plantuml/proxy?src=https://raw.githubusercontent.com/adorsys/keystore-management/develop/docs/img/flow.puml\u0026fmt=svg\u0026vvv=5\u0026sanitize=true)\n\n### Getting access to services\n\nAll services are available through `Juggler` interface. To obtain instance of it one should call following:\n```groovy\nJuggler juggler = DaggerBCJuggler.builder().build();\n```\nThis call will provide you with default Juggler implementation.\n\n`Juggler` is composed of 5 services representing different kind of operations:\n- `generateKeys()` to generate Secret/Private/Signing keys(or their set) from simple template\n- `toKeystore()` to persist generated key set into keystore\n- `readKeys()` to read keys from Java keystore and query them by alias/metadata/type/...\n- `decode()` to decode key bytes read from keystore into i.e. String for PBE raw keys\n- `serializeDeserialize()` to serialize/deserialize KeyStore to/from byte array.\n\n## API examples\n\u003c!--\nTo update snippets you can use embed.sh\nMacOS: Install gnused and gnugrep:\n`brew install gnu-sed`\n`brew install grep`\n\nExample script usage:\n./embed.sh Example README.md \u003e README-tmp.md \u0026\u0026 mv README-tmp.md README.md\n\nTODO: Migrate to AsciiDoc for automatic snippet embedding.\n--\u003e\n\n### Generate keystore\n[Example:Generate keystore](juggler/juggler-bouncycastle/src/test/java/de/adorsys/keymanagement/examples/NewKeyStoreTest.java#L29-L50)\n```groovy\n// Obtain Juggler service instance:\nBCJuggler juggler = DaggerBCJuggler.builder().build();\n\n// We want our keystore to have:\nKeySetTemplate template = KeySetTemplate.builder()\n        .providedKey(ProvidedKey.with().alias(\"MY-KEY\").key(stubSecretKey()).build()) // One provided key (i.e. existing) that has alias `MY-KEY`\n        .generatedSecretKey(Secret.with().prefix(\"SEC\").build()) // One generated secret key that has alias `SEC` + random UUID\n        .generatedSigningKey(Signing.with().algo(\"DSA\").alias(\"SIGN\").build()) // One generated signing key that has alias `SIGN` + random UUID and uses DSA algorithm\n        .generatedEncryptionKeys(Encrypting.with().prefix(\"ENC\").build().repeat(10)) // Ten generated private keys (with certificates) that have alias `ENC` + random UUID\n        .build();\n\n// Provide key protection password:\nSupplier\u003cchar[]\u003e password = \"PASSWORD!\"::toCharArray;\n// Generate key set\nKeySet keySet = juggler.generateKeys().fromTemplate(template);\n// Generate KeyStore\nKeyStore store = juggler.toKeystore().generate(keySet, password);\n// Validate that keystore has 13 keys:\n// One provided, one secret, one signing, ten private\nassertThat(countKeys(store)).isEqualTo(13);\n```\n\n### Change keystore password or clone it\n[Example:Clone keystore and change key password](juggler/juggler-bouncycastle/src/test/java/de/adorsys/keymanagement/examples/CloneKeyStoreAndChangeKeyPasswordTest.java#L28-L59)\n```groovy\n// Obtain Juggler service instance:\nBCJuggler juggler = DaggerBCJuggler.builder().build();\n\n// We want our keystore to have:\nKeySetTemplate template = KeySetTemplate.builder()\n        .providedKey(ProvidedKey.with().alias(\"MY-KEY\").key(stubSecretKey()).build()) // One provided key (i.e. existing) that has alias `MY-KEY`\n        .generatedEncryptionKeys(Encrypting.with().prefix(\"ENC\").build().repeat(10)) // Ten generated private keys (with certificates) that have alias `ENC` + random UUID\n        .build();\n\n// Provide key protection password:\nSupplier\u003cchar[]\u003e password = \"PASSWORD!\"::toCharArray;\n// Generate key set\nKeySet keySet = juggler.generateKeys().fromTemplate(template);\n// Generate KeyStore with each key protected with `PASSWORD!` password\nKeyStore store = juggler.toKeystore().generate(keySet, password);\n// Clone generated KeyStore:\nSupplier\u003cchar[]\u003e newPassword = \"NEW_PASSWORD!\"::toCharArray;\n// Create key set from old keystore that has new password `NEW_PASSWORD!`:\nKeySet clonedSet = juggler.readKeys()\n        .fromKeyStore(store, id -\u003e password.get())\n        .copyToKeySet(id -\u003e newPassword.get());\n// Generate cloned KeyStore with each key protected with `NEW_PASSWORD!` password (provided on key set)\nKeyStore newKeystore = juggler.toKeystore().generate(clonedSet, () -\u003e null);\n\n// Validate old keystore has same key count as new keystore:\nassertThat(countKeys(store)).isEqualTo(countKeys(newKeystore));\n// Validate old keystore has key password `PASSWORD!`\nassertThat(store.getKey(\"MY-KEY\", \"PASSWORD!\".toCharArray())).isNotNull();\n// Validate new keystore has key password `NEW_PASSWORD!`\nassertThat(newKeystore.getKey(\"MY-KEY\", \"NEW_PASSWORD!\".toCharArray())).isNotNull();\n```\n\n### Store your own char[] or String securely inside Java Keystore\n\nIt is possible your own char sequence in encrypted form inside Keystore using password-based-encryption. This way\nyou can store any data in form of SecretKey within java KeyStore.\n\n[Example:Store your own char array securely in KeyStore](juggler/juggler-bouncycastle/src/test/java/de/adorsys/keymanagement/examples/GeneratePbeKeyTest.java#L24-L45)\n```groovy\n// Obtain Juggler service instance:\nBCJuggler juggler = DaggerBCJuggler.builder().build();\n// Generate PBE (password-based encryption) raw key (only transformed to be stored in keystore,\n// encryption IS PROVIDED by keystore - i.e. BCFKS or UBER keystore provide it):\nSupplier\u003cchar[]\u003e keyPassword =  \"WOW\"::toCharArray;\nProvidedKey key = juggler.generateKeys().secretRaw(\n        Pbe.with()\n                .alias(\"AES-KEY\") // with alias `AES-KEY` if we will save it to keystore from KeySet\n                .data(\"MY SECRET DATA Тест!\".toCharArray()) // This data will be encrypted inside KeyStore when stored\n                .password(keyPassword) // Password that will be used to protect key in KeyStore\n                .build()\n);\n\n// Send key to keystore\nKeyStore ks = juggler.toKeystore().generate(KeySet.builder().key(key).build());\n\n// Read key back\nSecretKeySpec keyFromKeyStore = (SecretKeySpec) ks.getKey(\"AES-KEY\", keyPassword.get());\n// Note that BouncyCastle keys are encoded in PKCS12 byte format - UTF-16 big endian + 2 0's padding\nassertThat(juggler.decode().decodeAsString(keyFromKeyStore.getEncoded())).isEqualTo(\"MY SECRET DATA Тест!\");\n```\n\n\n### Generate secret key\n[Example:Generate secret key](juggler/juggler-bouncycastle/src/test/java/de/adorsys/keymanagement/examples/GenerateSecretKeyTest.java#L18-L32)\n```groovy\n// Obtain Juggler service instance:\nBCJuggler juggler = DaggerBCJuggler.builder().build();\n// Generate key:\nKey key = juggler.generateKeys().secret(\n        Secret.with()\n                .alias(\"AES-KEY\") // with alias `AES-KEY` if we will save it to keystore from KeySet\n                .algo(\"AES\") // for AES encryption\n                .keySize(128) // for AES-128 encryption\n                .build()\n).getKey();\n\nassertThat(key.getAlgorithm()).isEqualTo(\"AES\");\nassertThat(key.getEncoded()).hasSize(16); // 16 * 8 (sizeof byte) = 128 bits\n```\n\n### Open and analyze keystore\n[Example:Query keystore](juggler/juggler-bouncycastle/src/test/java/de/adorsys/keymanagement/examples/QueryKeyStoreTest.java#L27-L64)\n```groovy\n// Obtain Juggler service\nBCJuggler juggler = DaggerBCJuggler.builder().build();\n\n\nKeySetTemplate template = KeySetTemplate.builder()\n        .generatedSecretKey(Secret.with().prefix(\"SEC\").build()) // Secret key to be generated with name `SEC` + random UUID\n        .generatedSigningKey(Signing.with().algo(\"DSA\").alias(\"SIGN-1\").build()) // DSA-based signing key with name `SIGN-1`\n        .generatedEncryptionKey(Encrypting.with().alias(\"ENC-1\").build()) // Private key with name `ENC-1`\n        .generatedEncryptionKeys(Encrypting.with().prefix(\"GEN\").build().repeat(10)) // Ten private keys with name `GEN` + random UUID\n        .build();\n\n// Generate key set from template:\nKeySet keySet = juggler.generateKeys().fromTemplate(template);\n// Key protection password:\nSupplier\u003cchar[]\u003e password = \"PASSWORD!\"::toCharArray;\n// Create KeyStore\nKeyStore store = juggler.toKeystore().generate(keySet, password);\n// Open KeyStore-view to query it:\nKeyStoreView source = juggler.readKeys().fromKeyStore(store, id -\u003e password.get());\n// Acquire Key-Entry view, so we can query for KeyEntry entities\nEntryView\u003cQuery\u003cKeyEntry\u003e\u003e entryView = source.entries();\n// Query for fact that KeyStore has 13 keys in total:\nassertThat(entryView.all()).hasSize(13);\n// Query for fact that KeyStore has 1 key with name `SEC`\nassertThat(entryView.retrieve(\"SELECT * FROM keys WHERE alias = 'ENC-1'\").toCollection()).hasSize(1);\n// Query for fact that KeyStore has 10 keys with prefix `GEN`\nassertThat(entryView.retrieve(\"SELECT * FROM keys WHERE alias LIKE 'GEN%'\").toCollection()).hasSize(10);\n// Query for fact that KeyStore has 1 secret key:\nassertThat(entryView.retrieve(\"SELECT * FROM keys WHERE is_secret = true\").toCollection()).hasSize(1);\n\n// Query for fact that KeyStore has 1 secret key:\nassertThat(entryView.privateKeys()).hasSize(12);\n// Query for fact that KeyStore has 1 secret key:\nassertThat(entryView.secretKeys()).hasSize(1);\n// Query for fact that KeyStore has 0 trusted certs:\nassertThat(entryView.trustedCerts()).hasSize(0);\n```\n\n### Persist key with metadata into keystore\n[Example:Save metadata to keystore](juggler/juggler-bouncycastle/src/test/java/de/adorsys/keymanagement/examples/PersistMetadataToKeyStoreTest.java#L33-L77)\n```groovy\n// Obtain Juggler service\nBCJuggler juggler = DaggerBCJuggler.builder()\n        .metadataPersister(new WithPersister()) // enable metadata persistence\n        .metadataConfig(\n                MetadataPersistenceConfig.builder()\n                        .metadataClass(KeyExpirationMetadata.class) // define metadata class\n                        .build()\n        )\n        .build();\n\n// Key set template that is going to be saved into KeyStore\nKeySetTemplate template = KeySetTemplate.builder()\n        // One private key that can be used for encryption:\n        .generatedEncryptionKey(\n                Encrypting.with()\n                        .alias(\"ENC-KEY-1\") // key with alias `ENC-KEY-1` in KeyStore\n                        .metadata(new KeyExpirationMetadata(Instant.now())) // Associated metadata with this key, pretend it is `expired` key\n                        .build()\n        )\n        .build();\n\n// Generate key set:\nKeySet keySet = juggler.generateKeys().fromTemplate(template);\n// Key protection password:\nSupplier\u003cchar[]\u003e password = \"PASSWORD!\"::toCharArray;\n// Generate new KeyStore, it will have metadata in it\nKeyStore ks = juggler.toKeystore().generate(keySet, password);\n// Open KeyStore view to query it:\nKeyStoreView source = juggler.readKeys().fromKeyStore(ks, id -\u003e password.get());\n// Open alias view to query key alias by metadata\nAliasView\u003cQuery\u003cKeyAlias\u003e\u003e view = source.aliases();\n// Assert that key has been expired\nassertThat(\n        view.retrieve(\n                and(\n                        has(META), // Key has metadata\n                        lessThan(\n                                attribute(key -\u003e ((KeyExpirationMetadata) key.getMeta()).getExpiresAfter()), // Key expiration date\n                                Instant.now() // current date, so that if expiresAfter \u003c now() key is expired\n                        )\n                )\n        ).toCollection()\n).hasSize(1);\n```\n\n### Update key in keystore based on its metadata\n[Example:Rotate expired key in keystore](juggler/juggler-bouncycastle/src/test/java/de/adorsys/keymanagement/examples/RotateKeyBasedOnMetadataTest.java#L37-L89)\n```groovy\n// Obtain Juggler service\nBCJuggler juggler = DaggerBCJuggler.builder()\n        .metadataPersister(new WithPersister()) // enable metadata persistence\n        .metadataConfig(\n                MetadataPersistenceConfig.builder()\n                        .metadataClass(KeyValidity.class) // define metadata class\n                        .build()\n        )\n        .build();\n\n// Key protection password:\nSupplier\u003cchar[]\u003e password = \"PASSWORD!\"::toCharArray;\n\n// Lazy key template:\nFunction\u003cInstant, Encrypting\u003e keyTemplate = expiryDate -\u003e Encrypting.with()\n        .alias(\"ENC-KEY-1\") // key with alias `ENC-KEY-1` in KeyStore// Associated metadata with this key, pretend it is `expired` key\n        .metadata(new KeyValidity(expiryDate))\n        .password(password)\n        .build();\n\n// Key set template that is going to be saved into KeyStore\nKeySetTemplate template = KeySetTemplate.builder()\n        // One private key that can be used for encryption:\n        .generatedEncryptionKey(\n                keyTemplate.apply(Instant.now().minusSeconds(10)) // Will pretend that key has expired\n        )\n        .build();\n\n// Generate key set:\nKeySet keySet = juggler.generateKeys().fromTemplate(template); // Key metadata will indicate that key has expired\n// Generate new KeyStore, it will have metadata in it\nKeyStore ks = juggler.toKeystore().generate(keySet, () -\u003e null);\n// Open KeyStore view to query it:\nKeyStoreView source = juggler.readKeys().fromKeyStore(ks, id -\u003e password.get());\n// Open alias view to query key alias by metadata\nAliasView\u003cQuery\u003cKeyAlias\u003e\u003e view = source.aliases();\n// Find expired key:\nKeyAlias expired = view.uniqueResult(KeyValidity.EXPIRED);\n// replace expired key:\nview.update(\n        Collections.singleton(expired),\n        Collections.singleton(\n                juggler.generateKeys().encrypting(\n                        keyTemplate.apply(Instant.now().plus(10, ChronoUnit.HOURS)) // Valid for 10 hours from now\n                )\n        )\n);\n// validate there is only one `ENC-KEY-1` key\nassertThat(view.retrieve(equal(A_ID, \"ENC-KEY-1\")).toCollection()).hasSize(1);\n// and this key is NOT expired\nassertThat(view.retrieve(KeyValidity.EXPIRED).toCollection()).hasSize(0);\n```\n\n#  Project details\nMain service provider - `Juggler` is built using Dagger2 framework. This allows user to re-compose this service\nin his own project by providing replacing modules.\n\n# JavaDoc\nYou can read JavaDoc [here](https://adorsys.github.io/keystore-management/javadoc/latest/index.html)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadorsys%2Fkeystore-management","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadorsys%2Fkeystore-management","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadorsys%2Fkeystore-management/lists"}