{"id":21177609,"url":"https://github.com/tersesystems/securitybuilder","last_synced_at":"2025-07-20T15:34:34.909Z","repository":{"id":49154720,"uuid":"151899243","full_name":"tersesystems/securitybuilder","owner":"tersesystems","description":"Fluent builders with typesafe API for the JCA","archived":false,"fork":false,"pushed_at":"2021-06-26T01:12:37.000Z","size":269,"stargazers_count":47,"open_issues_count":0,"forks_count":3,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-07-10T00:38:41.249Z","etag":null,"topics":["builder-pattern","fluent","java","jca"],"latest_commit_sha":null,"homepage":"","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/tersesystems.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-10-07T02:28:31.000Z","updated_at":"2025-05-04T21:24:20.000Z","dependencies_parsed_at":"2022-09-09T10:00:49.745Z","dependency_job_id":null,"html_url":"https://github.com/tersesystems/securitybuilder","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/tersesystems/securitybuilder","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tersesystems%2Fsecuritybuilder","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tersesystems%2Fsecuritybuilder/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tersesystems%2Fsecuritybuilder/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tersesystems%2Fsecuritybuilder/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tersesystems","download_url":"https://codeload.github.com/tersesystems/securitybuilder/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tersesystems%2Fsecuritybuilder/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266151526,"owners_count":23884436,"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":["builder-pattern","fluent","java","jca"],"created_at":"2024-11-20T17:16:36.267Z","updated_at":"2025-07-20T15:34:34.861Z","avatar_url":"https://github.com/tersesystems.png","language":"Java","readme":"[![Maven Central](https://img.shields.io/maven-central/v/com.tersesystems.securitybuilder/securitybuilder)](https://search.maven.org/artifact/com.tersesystems.securitybuilder/securitybuilder) [![Build Status](https://travis-ci.org/tersesystems/securitybuilder.svg?branch=master)](https://travis-ci.org/tersesystems/securitybuilder)\n\n# Security Builders\n\nThis library implements a set of \"fluent\" API builders for the `java.security` classes, and provides more typesafe, intuitive API to access trust stores, key stores and keys.  The primary purpose of this library is to make small tasks easy, and provide better integration with the JSSE stack.  \n\nAlso check out [pgpainless](https://github.com/pgpainless/pgpainless) which does roughly the same thing with PGP API.\n\n## Installation\n\n### Maven\n\nIn your pom.xml:\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.tersesystems.securitybuilder\u003c/groupId\u003e\n    \u003cartifactId\u003esecuritybuilder\u003c/artifactId\u003e\n    \u003cversion\u003e1.0.1\u003c/version\u003e\u003c!-- see badge for latest version --\u003e\n\u003c/dependency\u003e\n```\n\n### sbt\n\n```scala\nlibraryDependencies += \"com.tersesystems.securitybuilder\" % \"securitybuilder\" % \"1.0.1\"\n```\n\n## Usage\n\nThe primary use of this package is to set up test X.509 certificates, private keys and trust stores.  The [Java Cryptography Architecture](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html) lays out how to create and initialize certificates, keystores, and so on, but typically does so in frustrating ways.  \n \nThe assumption is that you'll be working with Java 1.8 but with decent algorithms, so there are a number of preset defaults.  The builders are thread-safe and only build when you pull the trigger, but assume immutable input, so don't pass in arrays or lists that you are still fiddling with.\n\nAll the classes are in `com.tersesystems.securitybuilder` package.\n\n```java\nimport com.tersesystems.securitybuilder.*;\n```\n\nIn general, if you're just using the JCA, there are some [based off Latacora's Cryptographic Right Answers](https://latacora.singles/2018/04/03/cryptographic-right-answers.html):\n\n* Use RSA with 2048 bit key length and SHA-2 for public and private keys.\n* Use AES-GCM for encryption but **SEE WARNING BELOW**, and never reuse the IV.  There is no provable difference between AES-128 and AES-256, so [don't worry about it](http://www.daemonology.net/blog/2009-06-11-cryptographic-right-answers.html) and use AES-256.  \n* If you're going over the network, you generally want full on TLS, so use JSSE with [debugjsse](https://github.com/tersesystems/debugjsse) as the provider to see what's going on under the hood.  Be aware that SSLEngine/SSLSocket does not know that you're using HTTPS, so you need to [define hostname verification](https://tersesystems.com/blog/2014/03/23/fixing-hostname-verification/) yourself by setting `sslParameters.setEndpointIdentificationAlgorithm(\"HTTPS\")`. \n* Use an HMAC with at least SHA256, and a secret key that has at least 96 bits of entropy -- `EntropySource.salt()` uses 256 bits.\n* Use a MessageDigest with at least SHA256.\n* Use PBKDF2 with a SHA-2 HMAC [if you have to](https://pthree.org/2016/06/28/lets-talk-password-hashing/), but if you can use [jBCrypt](http://www.mindrot.org/projects/jBCrypt/) \nor [scrypt](https://github.com/wg/scrypt) go with that.\n* There's no real need to use your own SecureRandom, and you don't need to use `useInstanceStrong`, the entropy pool is the same and [you may get blocking](https://tersesystems.com/blog/2015/12/17/the-right-way-to-use-securerandom/).  Use `EntropySource`.\n\n### WARNING\n\nPlease be aware that some of the algorithms in the JCA are way, way out of date.\n\nIf you need a cryptography API, **DON'T USE THE JCA!**  Even with these builders, building your own crypto using a low level library is like [juggling chainsaws in the dark](https://www.usenix.org/sites/default/files/conference/protected-files/hotsec15_slides_green.pdf).  In particular, low level libraries don't do key management and key rotation very well.\n\nUse [Google Tink](https://github.com/google/tink/blob/master/docs/JAVA-HOWTO.md) instead, which has support for [storing keysets](https://github.com/google/tink/blob/master/docs/JAVA-HOWTO.md#storing-keysets), [symmetric key encryption](https://github.com/google/tink/blob/master/docs/JAVA-HOWTO.md#symmetric-key-encryption), [digital signatures](https://github.com/google/tink/blob/master/docs/JAVA-HOWTO.md#digitial-signatures), [envelope encryption](https://github.com/google/tink/blob/master/docs/JAVA-HOWTO.md#envelope-encryption) and [key rotation](https://github.com/google/tink/blob/master/docs/JAVA-HOWTO.md#key-rotation). \n\nGoogle Tink doesn't do everything: in that case, I recommend looking for a fallback professional high-level library rather than rolling your own.  This will typically involve an binding on top of a C library like [lazysodium](https://github.com/terl/lazysodium-java) on top of [libsodium](https://download.libsodium.org/doc/), or a specialized crypto framework like [Noise-Java](https://github.com/rweather/noise-java) implementing the [Noise Protocol Framework](http://noiseprotocol.org/).\n\n## JSSE (Java TLS Classes)\n\n### X509CertificateCreator\n\nCreates an X509Certificate or a chain of X509Certificate.  \n\nVery useful for building up certificates if you use `chain()`.\n\n```java\npublic class X509CertificateCreatorTest {\n  @Test\n  public void testFunctionalStyle() throws Exception {\n    FinalStage\u003cRSAKeyPair\u003e keyPairCreator = KeyPairCreator.creator().withRSA().withKeySize(2048);\n    RSAKeyPair rootKeyPair = keyPairCreator.create();\n    RSAKeyPair intermediateKeyPair = keyPairCreator.create();\n    RSAKeyPair eePair = keyPairCreator.create();\n\n    IssuerStage\u003cRSAPrivateKey\u003e creator =\n        X509CertificateCreator.creator().withSHA256withRSA().withDuration(Duration.ofDays(365));\n\n    String issuer = \"CN=letsencrypt.derp,O=Root CA\";\n    X509Certificate[] chain =\n        creator\n            .withRootCA(issuer, rootKeyPair, 2)\n            .chain(\n                rootKeyPair.getPrivate(),\n                rootCreator -\u003e\n                    rootCreator\n                        .withPublicKey(intermediateKeyPair.getPublic())\n                        .withSubject(\"OU=intermediate CA\")\n                        .withCertificateAuthorityExtensions(0)\n                        .chain(\n                            intermediateKeyPair.getPrivate(),\n                            intCreator -\u003e\n                                intCreator\n                                    .withPublicKey(eePair.getPublic())\n                                    .withSubject(\"CN=tersesystems.com\")\n                                    .withEndEntityExtensions()\n                                    .chain()))\n            .create();\n\n    PrivateKeyStore privateKeyStore =\n        PrivateKeyStore.create(\"tersesystems.com\", eePair.getPrivate(), chain);\n    TrustStore trustStore = TrustStore.create(singletonList(chain[2]), cert -\u003e \"letsencrypt.derp\");\n\n    try {\n      final PKIXCertPathValidatorResult result = CertificateChainValidator.validator()\n          .withAnchor(new TrustAnchor(issuer, rootKeyPair.getPublic(), null))\n          .withCertificates(chain)\n          .validate();\n      final PublicKey subjectPublicKey = result.getPublicKey();\n      assertThat(subjectPublicKey).isEqualTo(eePair.getPublic());\n    } catch (final CertPathValidatorException cpve) {\n      fail(\"Cannot test exception\", cpve);\n    }\n\n    SSLContext sslContext =\n        SSLContextBuilder.builder()\n            .withTLS()\n            .withKeyManager(\n                KeyManagerBuilder.builder()\n                    .withSunX509()\n                    .withPrivateKeyStore(privateKeyStore)\n                    .build())\n            .withTrustManager(\n                TrustManagerBuilder.builder()\n                    .withDefaultAlgorithm()\n                    .withTrustStore(trustStore)\n                    .build())\n            .build();\n    assertThat(sslContext).isNotNull();\n  }\n}\n```\n\nAdmittedly this doesn't look very simple, but you should see the code it replaces.\n\n### CertificateBuilder\n\nBuilds a `java.security.Certificate` from a source.  \n\nIf you use `withX509()`, it will give you an `X509Certificate`.\n\n```java\npublic class CertificateBuilderTest {\n  @Test\n  public void testX509Certificate() {\n    final InputStream inputStream = getClass().getResourceAsStream(\"/playframework.pem\");\n    try {\n      final X509Certificate x509Certificate =\n          CertificateBuilder.builder()\n            .withX509()\n            .withInputStream(inputStream)\n            .build();\n      assertThat(x509Certificate.getSigAlgName()).isEqualTo(\"SHA256withECDSA\");\n    } catch (final CertificateException e) {\n      fail(e.getMessage(), e);\n    }\n  }\n}\n```\n\n### KeyManagerBuilder\n\nBuilds a [`KeyManager`](https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#KeyManagerFactory) from input.  If you use `withNewSunX509()`, then you get a `X509ExtendedKeyManager` that can differentiate between RSA / DSA keys, pick out unexpired keys, and use password specific entries out of the store (if you use the keystore builder defined below).   See [Key Managers and Key Stores](https://tersesystems.com/blog/2018/09/08/keymanagers-and-keystores/) for the gory details.\n\nRecommend using with [debugjsse](https://github.com/tersesystems/debugjsse) provider.\n\n```java\npublic class KeyManagerBuilderTest {\n\n  @Test\n  public void testKeyManagerWithKeyStore() {\n    try {\n      final KeyStore keyStore = KeyStoreBuilder.empty();\n      final X509ExtendedKeyManager keyManager =\n          KeyManagerBuilder.builder()\n              .withNewSunX509()\n              .withKeyStore(keyStore, \"\".toCharArray())\n              .build();\n      assertThat(keyManager.getPrivateKey(\"derp\")).isNull();\n    } catch (final GeneralSecurityException e) {\n      fail(e.getMessage(), e);\n    }\n  }\n}\n```\n\n### TrustManagerBuilder\n\nBuilds a [`TrustManager`](https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#TrustManagerFactory) from input.\n\nRecommend using with [debugjsse](https://github.com/tersesystems/debugjsse) provider.\n\n```java\npublic class TrustManagerBuilderTest {\n  @Test\n  void builderWithKeyStore() throws Exception {\n    final KeyStore keyStore = KeyStoreBuilder.empty();\n    final X509ExtendedTrustManager trustManager =\n        TrustManagerBuilder.builder().withDefaultAlgorithm().withKeyStore(keyStore).build();\n    assertThat(trustManager.getAcceptedIssuers()).isEmpty();\n  }\n}\n```\n\n### SSLContextBuilder\n\nBuild a [`SSLContext`](https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#SSLContext).  \n\nYou will typically want to combine this with `TrustManagerBuilder` and `KeyManagerBuilder`.\n\nRecommend using with [debugjsse](https://github.com/tersesystems/debugjsse) provider.\n\n```java\npublic class SSLContextBuilderTest {\n\n  @Test\n  public void testSSLContextBuilderWithTLS() {\n    try {\n      final SSLContext sslContext = SSLContextBuilder.builder().withTLS().build();\n      sslContext.createSSLEngine();\n    } catch (final GeneralSecurityException e) {\n      fail(e.getMessage(), e);\n    }\n  }\n\n  @Test\n  public void testSSLContextBuilderWithTLSAndKeyManager() {\n    try {\n      final X509ExtendedKeyManager km =\n          KeyManagerBuilder.builder().withNewSunX509().withDefaultKeyStoreAndPassword().build();\n\n      final SSLContext sslContext =\n          SSLContextBuilder.builder().withTLS().withKeyManager(km).build();\n      sslContext.createSSLEngine();\n    } catch (final GeneralSecurityException e) {\n      fail(e.getMessage(), e);\n    }\n  }\n}\n```\n\n### CertificateChainValidator\n\nValidates a certificate chain using a PKIX [`CertPathValidator`](https://docs.oracle.com/javase/8/docs/api/java/security/cert/CertPathValidator.html).  See [Java PKI Programmer's Guide](https://docs.oracle.com/javase/8/docs/technotes/guides/security/certpath/CertPathProgGuide.html) for details, but you can safely ignore most if not all of it.\n\n```java\npublic class CertificateChainValidatorTest {\n  public void testCertificate(Certificate[] chain, X509Certificate rootCertificate) {\n    try {\n      final PKIXCertPathValidatorResult result = CertificateChainValidator.validator()\n          .withTrustedCertificates(rootCertificate)\n          .withCertificates(chain)\n          .validate();\n      final PublicKey subjectPublicKey = result.getPublicKey();\n      assertThat(subjectPublicKey).isEqualTo(eePair.getPublic());\n    } catch (final CertPathValidatorException cpve) {\n      fail(\"Cannot test exception\", cpve);\n    }\n  }\n}\n```\n\n## KeyStores\n\nKey stores are used for [key management](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#KeyManagement).  The `java.security.KeyStore` has three wrappers, depending on purpose: `PrivateKeyStore`, `TrustStore`, and `SecretKeyStore`.  They all extend `AbstractKeyStore`, and are written to be a drop in for `java.util.Map`.  See [blog post](https://tersesystems.com/blog/2018/07/28/building-java-keystores/) for gory details.\n\n### PrivateKeyStore\n\nSets up a private keystore that is set up the way that the default SunX509 keymanager expects -- that is, all the private keys have the same password.  You work with `PrivateKeyEntry` and never have to provide the password as a parameter.\n\n```java\npublic class PrivateKeyStoreTest {\n  \n  @Test\n  public void testAdd() {\n    try {\n      final char[] password = \"\".toCharArray();\n      final KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());\n      keyStore.load(null);\n      final PrivateKeyStore privateKeyStore = PrivateKeyStore.create(keyStore, password);\n      final RSAKeyPair rsaKeyPair = KeyPairCreator.creator().withRSA().withKeySize(2048).build();\n\n      final X509Certificate rsaCertificate =\n          X509CertificateCreator.builder()\n              .withSHA256withRSA()\n              .withNotBeforeNow()\n              .withDuration(Duration.ofDays(365))\n              .withRootCA(\"CN=example.com\", rsaKeyPair, 2)\n              .build();\n      final PrivateKeyEntry entry =\n          new PrivateKeyEntry(rsaKeyPair.getPrivate(), new Certificate[] {rsaCertificate});\n      privateKeyStore.put(\"alias1\", entry);\n\n      // PrivateKey doesn't override equals!\n      assertThat(Arrays.equals(privateKeyStore.get(\"alias1\").getPrivateKey().getEncoded(), (entry.getPrivateKey().getEncoded()))).isTrue();\n    } catch (final Exception e) {\n      fail(e.getMessage());\n    }\n  }\n}\n```\n\n### TrustStore\n\n`TrustStore` is a wrapper around [`KeyStore`](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#KeyStore) for `TrustedCertificateEntry`.\n\n```java\npublic class TrustStoreTest {\n  @Test\n  void testSize() {\n    try {\n      final KeyStore keyStore = generateStore();\n      final TrustStore trustStore = TrustStore.create(keyStore);\n\n      final RSAKeyPair rsaKeyPair = KeyPairCreator.creator().withRSA().withKeySize(2048).build();\n      final DSAKeyPair dsaKeyPair = KeyPairCreator.creator().withDSA().withKeySize(1024).build();\n\n      final X509Certificate rsaCertificate =\n          X509CertificateCreator.builder()\n              .withSHA256withRSA()\n              .withDuration(Duration.ofDays(365))\n              .withRootCA(\"CN=example.com\", rsaKeyPair, 2)\n              .create();\n\n      final X509Certificate dsaCertificate =\n          X509CertificateCreator.builder()\n              .withSignatureAlgorithm(\"SHA256withDSA\")\n              .withDuration(Duration.ofDays(365))\n              .withRootCA(\"CN=example.com\", dsaKeyPair.getKeyPair(), 2)\n              .create();\n\n      trustStore.put(\"rsaentry\", new TrustedCertificateEntry(rsaCertificate));\n      trustStore.put(\"dsaentry\", new TrustedCertificateEntry(dsaCertificate));\n\n      assertThat(trustStore.size()).isEqualTo(2);\n    } catch (final Exception e) {\n      fail(e.getMessage());\n    }\n  }\n}\n```\n\n### SecretKeyStore\n\nA [`KeyStore`](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#KeyStore) that contains only `SecretKeyEntry`.  \n\nUse this with a KeyStore format of type PKCS12.\n\n```java\npublic class SecretKeyStoreTest {\n  @Test\n  void testSize() {\n    try {\n      String password = \"hello world\".toCharArray();\n      byte[] salt = EntropySource.salt();\n\n      final Map\u003cString, ProtectionParameter\u003e passwordMap =\n          Collections.singletonMap(\"username\", new PasswordProtection(password));\n      final SecretKeyStore secretKeyStore = generateSecretKeyStore(passwordMap);\n  \n      PBEKey secretKey = PasswordBuilder.builder()\n        .withPBKDF2WithHmacSHA512()\n        .withPassword(password)\n        .withIterations(10000)\n        .withSalt(salt)\n        .withKeyLength(64 * 8)\n        .build();\n    \n      secretKeyStore.put(\"username\", new SecretKeyEntry(secretKey));\n      assertThat(secretKeyStore.size()).isEqualTo(1);\n    } catch (final KeyStoreException\n        | IOException\n        | NoSuchAlgorithmException\n        | CertificateException\n        | InvalidKeySpecException e) {\n      fail(e);\n    }\n  }\n}\n```\n\n### KeyStoreBuilder\n\nBuilds a [`KeyStore`](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#KeyStore).\n\n```java\npublic class KeyStoreBuilderTest {\n\n  @Test\n  public void testKeyStoreBuilderWithPathAndNoPassword() {\n    try {\n      final Path tempPath = Files.createTempFile(null, null);\n      final KeyStore keyStore = KeyStoreBuilder.empty();\n      try (OutputStream outputStream = Files.newOutputStream(tempPath)) {\n        keyStore.store(outputStream, \"\".toCharArray());\n      }\n\n      final KeyStore keyStoreFromPath =\n          KeyStoreBuilder.builder().withDefaultType().withPath(tempPath).withNoPassword().build();\n      assertThat(keyStoreFromPath.getType()).isEqualTo(KeyStore.getDefaultType());\n    } catch (final Exception e) {\n      fail(e.getMessage(), e);\n    }\n  }\n}\n```\n\n### KeyStoreDefaults\n\nAllows access to the default `KeyStore` used for CA certificates and for the [private key store location](https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#CustomizingStores).\n\n### KeyManagerKeyStoreBuilder\n\nBuilds a `KeyStore.Builder`, using a keystore builder that is able to send different passwords to the \"NewSunX509\" keymanager.  \n\n**The out of the box `KeyStore.Builder` API does not do this!**  See [blog post](https://tersesystems.com/blog/2018/09/08/keymanagers-and-keystores/) for details.\n\n```java\npublic class DifferentPasswordsTest {\n\n  @Test\n  public void testWithBuilder() throws GeneralSecurityException, IOException {\n    final char[] password1 = \"password1\".toCharArray();\n    final char[] password2 = \"password2\".toCharArray();\n    final Map\u003cString, ProtectionParameter\u003e passwordsMap = new HashMap\u003c\u003e();\n    passwordsMap.put(\"rsaentry\", new PasswordProtection(password1));\n    passwordsMap.put(\"dsaentry\", new PasswordProtection(password2));\n\n    final KeyStore keyStore = generateStore();\n    final KeyStore.Builder builder =\n        KeyManagerKeyStoreBuilder.newInstance(keyStore, passwordsMap::get);\n\n    final KeyManagerFactory kmf = KeyManagerFactory.getInstance(\"NewSunX509\");\n    kmf.init(new KeyStoreBuilderParameters(builder));\n    final X509ExtendedKeyManager keyManager = (X509ExtendedKeyManager) kmf.getKeyManagers()[0];\n\n    final String rsaAlias = keyManager.chooseServerAlias(\"RSA\", null, null);\n    assertThat(rsaAlias).contains(\"rsaentry\");\n    final PrivateKey rsaPrivateKey = keyManager.getPrivateKey(rsaAlias);\n    assertThat(rsaPrivateKey).isNotNull(); // can get password\n\n    final String dsaAlias = keyManager.chooseServerAlias(\"DSA\", null, null);\n    assertThat(dsaAlias).contains(\"dsaentry\");\n    final PrivateKey dsaPrivateKey = keyManager.getPrivateKey(dsaAlias);\n    assertThat(dsaPrivateKey).isNotNull(); // can get password\n  }\n}\n```\n\n## Key Builders and Creators\n\n### KeyPairCreator\n\nCreates a new [`KeyPair`](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#KeyPair) containing `PublicKey` and `PrivateKey` using a [`KeyPairGenerator`](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#KeyPairGenerator). \n \nIf you use `withRSA`, `withDSA` or `withEC` then you get back `RSAKeyPair` etc.\n\n```java\nclass KeyPairCreatorTest {\n  @Test\n  void testWithAlgorithm() throws GeneralSecurityException {\n    final KeyPair keyPair = KeyPairCreator.creator().withAlgorithm(\"RSA\").withKeySize(2048).create();\n    Assertions.assertThat(keyPair.getPublic().getAlgorithm()).isEqualTo(\"RSA\");\n  }\n\n  @Test\n  void testWithRSA() throws GeneralSecurityException {\n    final RSAKeyPair keyPair = KeyPairCreator.creator().withRSA().withKeySize(2048).create();\n    Assertions.assertThat(keyPair.getPublic().getAlgorithm()).isEqualTo(\"RSA\");\n  }\n\n  @Test\n  void testWithDSA() throws GeneralSecurityException {\n    final DSAKeyPair keyPair = KeyPairCreator.creator().withDSA().withKeySize(1024).create();\n    Assertions.assertThat(keyPair.getPublic().getAlgorithm()).isEqualTo(\"DSA\");\n  }\n\n  @Test\n  void testWithDH() throws GeneralSecurityException {\n    final DHKeyPair keyPair = KeyPairCreator.creator().withDH().withKeySize(1024).create();\n    Assertions.assertThat(keyPair.getPublic().getAlgorithm()).isEqualTo(\"DH\");\n  }\n\n  @Test\n  void testWithEC() throws GeneralSecurityException {\n    final ECKeyPair keyPair = KeyPairCreator.creator().withEC().withKeySize(224).create();\n    Assertions.assertThat(keyPair.getPublic().getAlgorithm()).isEqualTo(\"EC\");\n  }\n}\n```\n\n### EncodedKeySpecBuilder\n\nBuilds a [`EncodedKeySpec`](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#PKCS8EncodedKeySpec) from existing source material.\n \nYou can use either `PKCS8EncodedKeySpec`, commonly used for PEM encoded private keys, or `X509EncodedKeySpec`, used for PEM encoded X.509 certificates.\n\n```java\nclass PKCS8EncodedKeySpecBuilderTest {\n  @Test\n  public void testGeneration() throws Exception {\n    // Read a private key\n    final Reader reader = new InputStreamReader(getClass().getResourceAsStream(\"/private-key.pem\"));\n    final PKCS8EncodedKeySpec keySpec =\n        PKCS8EncodedKeySpecBuilder.builder().withReader(reader).withNoPassword().build();\n    assertThat(keySpec.getFormat()).isEqualTo(\"PKCS#8\");\n  }\n}\n```\n\n### PublicKeyBuilder\n\nBuilds a [`PublicKey`](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#Key). \n \nWill provide public key of the appropriate type using `withRSA`, `withDSA`, or `withEC` methods.\n\n```java\npublic class PublicKeyBuilderTest {\n\n  @Test\n  public void testRSAPublicKey() throws GeneralSecurityException {\n    final BigInteger modulus =\n        new BigInteger(\n            \"b4a7e46170574f16a97082b22be58b6a2a629798419\"\n                + \"be12872a4bdba626cfae9900f76abfb12139dce5de5\"\n                + \"6564fab2b6543165a040c606887420e33d91ed7ed7\",\n            16);\n    final BigInteger exp = new BigInteger(\"11\", 16);\n    final RSAPublicKeySpec rsaPublicKeySpec = new RSAPublicKeySpec(modulus, exp);\n    RSAPublicKey rsaPublicKey =\n        PublicKeyBuilder.builder().withRSA().withKeySpec(rsaPublicKeySpec).build();\n    assertThat(rsaPublicKey).isNotNull();\n  }\n}\n```\n\n### PrivateKeyBuilder\n\nBuilds a [`PrivateKey`](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#Key).  \n\nWill provide private key of the appropriate type using `withRSA`, `withDSA`, or `withEC` methods.\n\n```java\nclass PrivateKeyBuilderTest {\n\n  @Test\n  void builderWithRSA() throws GeneralSecurityException {\n    final RSAPrivateKey exampleKey =\n        (RSAPrivateKey)\n            KeyPairCreator.creator().withAlgorithm(\"RSA\").withKeySize(2048).build().getPrivate();\n    final RSAPrivateKeySpec rsaPrivateKeySpec =\n        new RSAPrivateKeySpec(exampleKey.getModulus(), exampleKey.getPrivateExponent());\n    final RSAPrivateKey privateKey =\n        PrivateKeyBuilder.builder().withRSA().withKeySpec(rsaPrivateKeySpec).build();\n\n    assertThat(privateKey).isNotNull();\n  }\n}\n```\n\n### SecretKeyBuilder\n\nBuilds a [`SecretKey`](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#SecretKey).\n\nThe algorithms are in \u003ca href=\"https://docs.oracle.com/javase/8/docs/technotes/guides/security/SunProviders.html#SunJCEProvider\"\u003eThe SunJCE Provider\u003c/a\u003e.\n\n```java\npublic class SecretKeyBuilderTest {\n  @Test\n  public void testSecretKeySpec() throws Exception {\n    byte[] aesKeyData = \"abc123\".getBytes();\n\n    SecretKey secretKey = SecretKeyBuilder.builder()\n        .withSecretKeySpec(\"AES\")\n        .withData(aesKeyData)\n        .build();\n\n    assertThat(secretKey.getAlgorithm()).isEqualTo(\"AES\");\n  }\n}\n```\n\n## MACs, Signatures, Passwords\n\n### MacBuilder\n\nBuilds an Message Authentication Code based on cryptographic hashing, aka [`Mac`](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#Mac).\n\n```java\npublic class MacBuilderTest {\n  @Test\n  void testMacBuild() throws GeneralSecurityException {\n    SecretKey key = new SecretKeySpec(\"privatekey\".getBytes(), \"HmacSHA256\");\n\n    Mac sha256Mac = MacBuilder.builder().withAlgorithm(\"HmacSHA256\").withKey(key).build();\n    String output = byteArrayToHex(sha256Mac.doFinal(\"test\".getBytes()));\n\n    assertThat(sha256Mac.getAlgorithm()).isEqualTo(\"HmacSHA256\");\n    assertThat(output).isEqualTo(\"27f0d5331806fb9f21247b19bee883a7cfe54c069d6e28edccc2cff8e78c4a74\");\n  }\n\n  @Test\n  void testSecretKeySpec() throws GeneralSecurityException {\n    Mac sha256Mac = MacBuilder.builder().withSecretKeySpec(\"HmacSHA256\").withString(\"privatekey\").build();\n    String output = byteArrayToHex(sha256Mac.doFinal(\"test\".getBytes()));\n\n    assertThat(sha256Mac.getAlgorithm()).isEqualTo(\"HmacSHA256\");\n    assertThat(output).isEqualTo(\"27f0d5331806fb9f21247b19bee883a7cfe54c069d6e28edccc2cff8e78c4a74\");\n  }\n\n  @Test\n  void testHmac() throws GeneralSecurityException {\n    Mac sha256Mac = MacBuilder.builder().withHmacSHA256().withString(\"privatekey\").build();\n    String output = byteArrayToHex(sha256Mac.doFinal(\"test\".getBytes()));\n\n    assertThat(sha256Mac.getAlgorithm()).isEqualTo(\"HmacSHA256\");\n    assertThat(output).isEqualTo(\"27f0d5331806fb9f21247b19bee883a7cfe54c069d6e28edccc2cff8e78c4a74\");\n  }\n}\n```\n\n### MessageDigestBuilder\n\nBuilds a [`MessageDigest`](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#MessageDigest).\n\nThese are intentionally curtailed so you don't pick out weak MessageDigest algorithms.\n\n```java\npublic class MessageDigestBuilderTest {\n  @Test\n  public void testSha512() throws NoSuchAlgorithmException {\n    assertThat(MessageDigestBuilder.sha512().getAlgorithm()).isEqualTo(\"SHA-512\");\n  }\n}\n```\n\n### SignatureBuilder\n\nBuilds a [`Signature`](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#Signature). for either signing or verifying. \n\n```java\npublic class SignatureBuilderTest {\n\n  @Test\n  public void testSignature() {\n    try {\n      final KeyPair\u003c?, ?\u003e keyPair =\n          KeyPairCreator.creator().withAlgorithm(\"RSA\").withKeySize(2048).build();\n      final PrivateKey privateKey = keyPair.getPrivate();\n      final PublicKey publicKey = keyPair.getPublic();\n\n      final Signature signingSignature =\n          SignatureBuilder.builder().withAlgorithm(\"SHA256withRSA\").signing(privateKey).build();\n      final byte[] digest = signingSignature.sign();\n\n      final Signature verifySignature =\n          SignatureBuilder.builder().withAlgorithm(\"SHA256withRSA\").verifying(publicKey).build();\n      assertThat(verifySignature.verify(digest)).isEqualTo(true);\n    } catch (final Exception e) {\n      Fail.fail(e.getMessage(), e);\n    }\n  }\n}\n```\n\n### EntropySource\n\nPulls from SecureRandom `/dev/urandom`, using the recommended number of random bits.  See [blog post](https://tersesystems.com/blog/2015/12/17/the-right-way-to-use-securerandom/) for details.\n\n```java\npublic class EntropySource {\n  /**\n   * Provides an initialization vector for GCM.\n   */\n  public static byte[] gcmIV() {\n    return nextBytes(DEFAULT_GCM_IV_LENGTH);\n  }\n\n  /**\n   * Provides a salt, which must be unique but is not private.\n   */\n  public static byte[] salt() {\n    return nextBytes(DEFAULT_SALT_LENGTH);\n  }\n}\n```\n\n## Odds and Ends\n\nFinally, there's some code which is useful in a pinch but which doesn't really go anywhere else.  \n\n### AuthenticatedEncryptionBuilder\n\nMakes generating an AES-GCM cipher a bit easier.  You [always](https://blog.cryptographyengineering.com/2012/05/19/how-to-choose-authenticated-encryption/) want to use an authenticated encryption mode.\n\nAgain, you're better off using Google Tink's [symmetric encryption](https://github.com/google/tink/blob/master/docs/JAVA-HOWTO.md#symmetric-key-encryption) if you're doing encryption -- and if you're going over the network, you generally want full on TLS.  See the usage and warnings up top.\n\n```java\npublic class AuthenticatedEncryptionBuilderTest {\n  @Test\n  public void testCipher() throws GeneralSecurityException {\n    final SecretKey aesSecretKey = SecretKeyGenerator.generate().withAES().withKeySize(128).build();\n    final SecretKeySpec secretKeySpec = new SecretKeySpec(aesSecretKey.getEncoded(), aesSecretKey.getAlgorithm());\n    final IvStage builder = AuthenticatedEncryptionBuilder.builder().withSecretKey(secretKeySpec);\n\n    byte[] gcmIV = EntropySource.gcmIV();\n    byte[] inputData = \"input text\".getBytes(UTF_8);\n\n    byte[] encryptedData = builder.withIv(gcmIV).encrypt().doFinal(inputData);\n    byte[] decryptedData = builder.withIv(gcmIV).decrypt().doFinal(encryptedData);\n\n    String decryptString = new String(decryptedData, UTF_8);\n    assertThat(decryptString).isEqualTo(\"input text\");\n  }\n}\n```\n\n### PasswordBuilder\n\nA specialized secret key builder for encrypting passwords.  \n\nUse PBKDF2 with a SHA-2 HMAC [if you have to](https://pthree.org/2016/06/28/lets-talk-password-hashing/), but if you can use [jBCrypt](http://www.mindrot.org/projects/jBCrypt/) or [scrypt](https://github.com/wg/scrypt), go with that.\n\n```java\npublic class PasswordBuilderTest {\n\n  @Test\n  public void testPasswordSpec() throws Exception {\n    byte[] salt = EntropySource.salt();\n\n    PBEKey passwordBasedEncryptionKey = PasswordBuilder.builder()\n        .withPBKDF2WithHmacSHA512()\n        .withPassword(\"hello world\".toCharArray())\n        .withIterations(1000)\n        .withSalt(salt)\n        .withKeyLength(64 * 8)\n        .build();\n\n    byte[] encryptedPassword = passwordBasedEncryptionKey.getEncoded();\n    assertThat(passwordBasedEncryptionKey.getAlgorithm()).isEqualTo(\"PBKDF2WithHmacSHA512\");\n  }\n}\n```\n\n### KeyAgreementBuilder\n\nCreates a [KeyAgreement](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#KeyAgreement) instance.\n\nThis is typically used with Diffie-Hellman, most commonly found in SSH.  Use [jsch](http://www.jcraft.com/jsch/) if you want SSH, otherwise you're better off using a high level crypto library like those described in the WARNING section up top.\n\nThe [canonical DH key exchange](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#DH2Ex):\n\n```java\npublic class KeyAgreementBuilderTest {\n  @Test\n  public void testKeyAgreementParams() throws GeneralSecurityException, IOException {\n    // Alice creates her own DH key pair with 2048-bit key size\n    DHKeyPair aliceKpair = KeyPairCreator.creator().withDH().withKeySize(2048).create();\n\n    // Alice creates and initializes her DH KeyAgreement object\n    KeyAgreement aliceKeyAgree = KeyAgreementBuilder.builder()\n        .withDH()\n        .withKey(aliceKpair.getPrivate())\n        .build();\n\n    // Alice encodes her public key, and sends it over to Bob.\n    byte[] alicePubKeyEnc = aliceKpair.getPublic().getEncoded();\n\n    //* Let's turn over to Bob. Bob has received Alice's public key\n    //* in encoded format.\n    //* He instantiates a DH public key from the encoded key material.\n    DHPublicKey alicePubKey = PublicKeyBuilder.builder().withDH()\n        .withKeySpec(new X509EncodedKeySpec(alicePubKeyEnc)).build();\n\n    //* Bob gets the DH parameters associated with Alice's public key.\n    //* He must use the same parameters when he generates his own key\n    //* pair.\n    DHParameterSpec dhParamFromAlicePubKey = alicePubKey.getParams();\n\n    // Bob creates his own DH key pair\n    DHKeyPair bobKpair = KeyPairCreator.creator().withDH().withKeySpec(dhParamFromAlicePubKey)\n        .create();\n\n    // Bob creates and initializes his DH KeyAgreement object\n    KeyAgreement bobKeyAgree = KeyAgreementBuilder.builder().withDH().withKey(bobKpair.getPrivate())\n        .build();\n\n    // Bob encodes his public key, and sends it over to Alice.\n    byte[] bobPubKeyEnc = bobKpair.getPublic().getEncoded();\n\n    //* Alice uses Bob's public key for the first (and only) phase\n    //* of her version of the DH protocol.\n    //* Before she can do so, she has to instantiate a DH public key\n    //* from Bob's encoded key material.\n    DHPublicKey bobPubKey = PublicKeyBuilder.builder().withDH()\n        .withKeySpec(new X509EncodedKeySpec(bobPubKeyEnc)).build();\n    aliceKeyAgree.doPhase(bobPubKey, true);\n\n    //* Bob uses Alice's public key for the first (and only) phase\n    //* of his version of the DH protocol.\n    bobKeyAgree.doPhase(alicePubKey, true);\n\n    // At this stage, both Alice and Bob have completed the DH key\n    // agreement protocol. Both generate the (same) shared secret.\n    byte[] aliceSharedSecret = aliceKeyAgree.generateSecret();\n    byte[] bobSharedSecret = new byte[aliceSharedSecret.length];\n    bobKeyAgree.generateSecret(bobSharedSecret, 0);\n    assertThat(Arrays.equals(aliceSharedSecret, bobSharedSecret)).isTrue();\n\n    // Now let's create a SecretKey object using the shared secret\n    // and use it for encryption.\n    SecretKeySpec bobAesKey = new SecretKeySpec(bobSharedSecret, 0, 16, \"AES\");\n    SecretKeySpec aliceAesKey = new SecretKeySpec(aliceSharedSecret, 0, 16, \"AES\");\n\n    // Bob encrypts, using AES in GCM mode\n    final byte[] iv = EntropySource.gcmIV();\n    Cipher bobCipher = AuthenticatedEncryptionBuilder.builder().withSecretKey(bobAesKey).withIv(iv)\n        .encrypt();\n    byte[] cleartext = \"This is just an example\".getBytes();\n    byte[] ciphertext = bobCipher.doFinal(cleartext);\n\n    // Alice decrypts, using AES in GCM mode\n    Cipher aliceCipher = AuthenticatedEncryptionBuilder.builder().withSecretKey(aliceAesKey).withIv(iv).decrypt();\n    byte[] recovered = aliceCipher.doFinal(ciphertext);\n    assertThat(Arrays.equals(cleartext, recovered)).isTrue();\n  }\n}\n```\n","funding_links":[],"categories":["安全","项目","Projects","Frameworks and Libs"],"sub_categories":["安全","Security","Java"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftersesystems%2Fsecuritybuilder","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftersesystems%2Fsecuritybuilder","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftersesystems%2Fsecuritybuilder/lists"}