diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..7830e12 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,53 @@ +name: Publish Artifacts + +on: + release: + types: [created] + +permissions: + actions: write + +jobs: + build: + name: Build + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + jdk: [17] + fail-fast: false + runs-on: ${{ matrix.os }} + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set up JDK ${{ matrix.jdk }} + uses: spring-io/spring-gradle-build-action@v2 + with: + java-version: ${{ matrix.jdk }} + distribution: temurin + - name: Build with Gradle + run: ./gradlew check + - name: Test Reports + uses: mikepenz/action-junit-report@v3 + if: success() || failure() + with: + report_paths: '**/build/test-results/test/TEST-*.xml' + + publish: + name: Sign and Publish Artifact + runs-on: ubuntu-latest + needs: [build] + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set up JDK + uses: spring-io/spring-gradle-build-action@v2 + with: + java-version: 17 + distribution: temurin + - name: Gradle Publish + env: + GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }} + GPG_SIGNING_SECRET: ${{ secrets.GPG_SIGNING_SECRET }} + OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} + run: ./gradlew publish --stacktrace diff --git a/build.gradle b/build.gradle index d12a134..7d98a06 100644 --- a/build.gradle +++ b/build.gradle @@ -3,6 +3,7 @@ plugins { id 'io.freefair.lombok' version '6.5.0.3' apply(false) id 'io.spring.javaformat' version '0.0.39' apply(false) id 'io.spring.dependency-management' version '1.1.0' apply(false) + id 'com.konfigyr.crypto.deploy' apply(false) } subprojects { @@ -12,13 +13,14 @@ subprojects { apply plugin: 'io.freefair.lombok' apply plugin: 'io.spring.javaformat' apply plugin: 'io.spring.dependency-management' + apply plugin: 'com.konfigyr.crypto.deploy' ext { set('springVersion', '3.1.2') } group = 'com.konfigyr' - version = '0.0.1' + version = '1.0.0-RC1' sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 @@ -36,6 +38,8 @@ subprojects { } dependencies { + implementation 'com.google.code.findbugs:jsr305:3.0.2' + checkstyle 'io.spring.javaformat:spring-javaformat-checkstyle:0.0.39' annotationProcessor 'org.springframework.boot:spring-boot-autoconfigure-processor' @@ -51,6 +55,11 @@ subprojects { } } + java { + withJavadocJar() + withSourcesJar() + } + test { useJUnitPlatform() } diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle new file mode 100644 index 0000000..83edfa8 --- /dev/null +++ b/buildSrc/build.gradle @@ -0,0 +1,23 @@ +apply plugin: 'java' +apply plugin: 'java-gradle-plugin' + +repositories { + mavenCentral() + + maven { + url 'https://plugins.gradle.org/m2/' + } +} + +dependencies { + implementation gradleApi() +} + +gradlePlugin { + plugins { + deploy { + id = 'com.konfigyr.crypto.deploy' + implementationClass = 'com.konfigyr.crypto.publish.DeployPlugin' + } + } +} diff --git a/buildSrc/src/main/java/com/konfigyr/crypto/publish/DeployExtension.java b/buildSrc/src/main/java/com/konfigyr/crypto/publish/DeployExtension.java new file mode 100644 index 0000000..44feba7 --- /dev/null +++ b/buildSrc/src/main/java/com/konfigyr/crypto/publish/DeployExtension.java @@ -0,0 +1,54 @@ +package com.konfigyr.crypto.publish; + +import org.gradle.api.model.ObjectFactory; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.ProviderFactory; + +/** + * @author : vladimir.spasic@ebf.com + * @since : 04.09.23, Mon + **/ +public abstract class DeployExtension { + + static final String NAME = "deploy"; + + private final Property signingKey; + + private final Property signingSecret; + + private final Property repositoryUsername; + + private final Property repositoryPassword; + + public DeployExtension(ObjectFactory factory, ProviderFactory providers) { + signingKey = factory.property(String.class).value(providers.environmentVariable("GPG_SIGNING_KEY")); + signingSecret = factory.property(String.class).value(providers.environmentVariable("GPG_SIGNING_SECRET")); + repositoryUsername = factory.property(String.class).value(providers.environmentVariable("OSSRH_USERNAME")); + repositoryPassword = factory.property(String.class).value(providers.environmentVariable("OSSRH_PASSWORD")); + } + + public Property signingKey() { + return signingKey; + } + + public Property signingSecret() { + return signingSecret; + } + + public Property repositoryUsername() { + return repositoryUsername; + } + + public Property repositoryPassword() { + return repositoryPassword; + } + + public boolean hasRepositoryCredentials() { + return repositoryUsername.isPresent() && repositoryPassword.isPresent(); + } + + public boolean hasSigningCredentials() { + return signingKey.isPresent() && signingSecret.isPresent(); + } + +} diff --git a/buildSrc/src/main/java/com/konfigyr/crypto/publish/DeployPlugin.java b/buildSrc/src/main/java/com/konfigyr/crypto/publish/DeployPlugin.java new file mode 100644 index 0000000..a46a9ef --- /dev/null +++ b/buildSrc/src/main/java/com/konfigyr/crypto/publish/DeployPlugin.java @@ -0,0 +1,129 @@ +package com.konfigyr.crypto.publish; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.dsl.RepositoryHandler; +import org.gradle.api.artifacts.repositories.MavenArtifactRepository; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.plugins.JavaPluginExtension; +import org.gradle.api.publish.Publication; +import org.gradle.api.publish.PublishingExtension; +import org.gradle.api.publish.VariantVersionMappingStrategy; +import org.gradle.api.publish.VersionMappingStrategy; +import org.gradle.api.publish.maven.MavenPom; +import org.gradle.api.publish.maven.MavenPublication; +import org.gradle.api.publish.maven.plugins.MavenPublishPlugin; +import org.gradle.plugins.signing.SigningExtension; +import org.gradle.plugins.signing.SigningPlugin; + +import javax.annotation.Nonnull; +import java.net.URI; + +/** + * @author : vladimir.spasic@ebf.com + * @since : 04.09.23, Mon + **/ +public class DeployPlugin implements Plugin { + + @Override + public void apply(@Nonnull Project project) { + project.getPlugins().apply(MavenPublishPlugin.class); + project.getPlugins().apply(SigningPlugin.class); + + project.getExtensions().create(DeployExtension.NAME, DeployExtension.class, + project.getObjects(), project.getProviders()); + + customizeJavaPlugin(project); + customizePublishExtension(project); + } + + private void customizeJavaPlugin(Project project) { + project.getPlugins().withType(JavaPlugin.class, it -> { + final JavaPluginExtension extension = project.getExtensions().getByType(JavaPluginExtension.class); + extension.withJavadocJar(); + extension.withSourcesJar(); + }); + } + + private void customizePublishExtension(Project project) { + final PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class); + publishing.repositories(repositories -> customizeRepositories(repositories, project)); + + final MavenPublication publication = publishing.getPublications().create("maven", MavenPublication.class); + publication.from(project.getComponents().findByName("java")); + publication.versionMapping(this::customizeVersionMappings); + + customizePom(publication.getPom(), project); + customizeSigningExtension(publication, project); + } + + private void customizeSigningExtension(Publication publication, Project project) { + final DeployExtension extension = project.getExtensions().getByType(DeployExtension.class); + + if (extension.hasSigningCredentials()) { + final SigningExtension signing = project.getExtensions().getByType(SigningExtension.class); + signing.sign(publication); + signing.useInMemoryPgpKeys(extension.signingKey().get(), extension.signingSecret().get()); + } + } + + private void customizeRepositories(RepositoryHandler repositories, Project project) { + final DeployExtension extension = project.getExtensions().getByType(DeployExtension.class); + + repositories.maven(repository -> { + repository.setName("oss-sonatype-snapshot"); + repository.setUrl(URI.create("https://s01.oss.sonatype.org/content/repositories/snapshots/")); + customizeRepositoryCredentials(repository, extension); + }); + + repositories.maven(repository -> { + repository.setName("oss-sonatype-release"); + repository.setUrl(URI.create("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/")); + customizeRepositoryCredentials(repository, extension); + }); + } + + private void customizeRepositoryCredentials(MavenArtifactRepository repository, DeployExtension extension) { + if (extension.hasRepositoryCredentials()) { + repository.credentials(credentials -> { + credentials.setUsername(extension.repositoryUsername().get()); + credentials.setPassword(extension.repositoryPassword().get()); + }); + } + } + + private void customizeVersionMappings(VersionMappingStrategy mappings) { + mappings.usage("java-api", strategy -> strategy.fromResolutionOf("runtimeClasspath")); + mappings.usage("java-runtime", VariantVersionMappingStrategy::fromResolutionResult); + } + + private void customizePom(MavenPom pom, Project project) { + pom.getUrl().set("https://github.com/konfigyr/konfigyr-crypto"); + pom.getName().set(project.provider(project::getName)); + pom.getDescription().set(project.provider(project::getDescription)); + pom.organization(org -> { + org.getName().set("Konfigyr"); + org.getUrl().set("https://konfigyr.com"); + }); + pom.developers(developers -> developers.developer(developer -> { + developer.getId().set("vspasic"); + developer.getName().set("Vladimir Spasic"); + developer.getEmail().set("vladimir.spasic.86@gmail.com"); + developer.getRoles().add("Project lead"); + })); + pom.issueManagement(issue -> { + issue.getSystem().set("Github"); + issue.getUrl().set("https://github.com/konfigyr/konfigyr-crypto/issues"); + }); + pom.scm(scm -> { + scm.getDeveloperConnection().set("scm:git:ssh://git@github.com/konfigyr/konfigyr-crypto.git"); + scm.getConnection().set("scm:git:git://github.com/konfigyr/konfigyr-crypto.git"); + scm.getUrl().set("https://github.com/konfigyr/konfigyr-crypto"); + scm.getTag().set("Github"); + }); + pom.licenses(licences -> licences.license(licence -> { + licence.getName().set("The Apache License, Version 2.0"); + licence.getUrl().set("https://www.apache.org/licenses/LICENSE-2.0.txt"); + })); + } +} diff --git a/konfigyr-crypto-api/build.gradle b/konfigyr-crypto-api/build.gradle index 231e953..c3390c2 100644 --- a/konfigyr-crypto-api/build.gradle +++ b/konfigyr-crypto-api/build.gradle @@ -1,5 +1,9 @@ +description = 'Core library of the Konfigyr Crypto library that defines an extensible API for working with crypto keys' + dependencies { - api 'org.springframework.boot:spring-boot-starter' + compileOnly 'org.springframework.boot:spring-boot-starter' api 'jakarta.validation:jakarta.validation-api' -} \ No newline at end of file + + testImplementation 'org.springframework.boot:spring-boot-starter' +} diff --git a/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/AbstractKeyEncryptionKey.java b/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/AbstractKeyEncryptionKey.java index 675fa84..f5aa8b0 100644 --- a/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/AbstractKeyEncryptionKey.java +++ b/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/AbstractKeyEncryptionKey.java @@ -20,10 +20,22 @@ **/ public abstract class AbstractKeyEncryptionKey implements KeyEncryptionKey { + /** + * Unique {@link KeyEncryptionKey} identifier. + */ protected final String id; + /** + * Name of the {@link KeyEncryptionKeyProvider} that owns the + * {@link KeyEncryptionKey}. + */ protected final String provider; + /** + * Constructor used to set up the required {@link KeyEncryptionKey} identifiers. + * @param id unique key identifier, can't be {@literal null} + * @param provider key provider name, can't be {@literal null} + */ protected AbstractKeyEncryptionKey(String id, String provider) { this.id = id; this.provider = provider; diff --git a/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/Algorithm.java b/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/Algorithm.java index bf80212..e534e9e 100644 --- a/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/Algorithm.java +++ b/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/Algorithm.java @@ -23,13 +23,13 @@ public interface Algorithm extends Serializable { /** - * @return algorithm name, never {@link null}. + * @return algorithm name, never {@literal null}. */ @NonNull String name(); /** - * @return key type used by the algorithm, never {@link null}. + * @return key type used by the algorithm, never {@literal null}. */ @NonNull KeyType type(); @@ -37,7 +37,7 @@ public interface Algorithm extends Serializable { /** * Collection of {@link KeysetOperation operations} this {@link Algorithm} can * perform. - * @return supported operations, never {@link null}. + * @return supported operations, never {@literal null}. */ @NonNull Set operations(); diff --git a/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/EncryptedKeyset.java b/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/EncryptedKeyset.java index f957a1d..764a312 100644 --- a/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/EncryptedKeyset.java +++ b/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/EncryptedKeyset.java @@ -121,6 +121,9 @@ public InputStream getInputStream() { return builder(keyset).keyEncryptionKey(keyset.getKeyEncryptionKey()).build(data); } + /** + * Builder class used to create new instances of the {@link EncryptedKeyset}. + */ @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public static final class Builder { diff --git a/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/KeyEncryptionKeyProvider.java b/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/KeyEncryptionKeyProvider.java index 61f70ad..f18b0dc 100644 --- a/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/KeyEncryptionKeyProvider.java +++ b/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/KeyEncryptionKeyProvider.java @@ -19,10 +19,26 @@ **/ public interface KeyEncryptionKeyProvider { + /** + * Creates a new instance of the {@link KeyEncryptionKeyProvider} with a given name a + * collection of {@link KeyEncryptionKey} which it owns. + * @param name provider name, can't be {@literal null} + * @param keys keys that the provider should own, can't be {@literal null} + * @return encryption key provider, never {@literal null} + */ + @NonNull static KeyEncryptionKeyProvider of(String name, KeyEncryptionKey... keys) { return new SimpleKeyEncryptionKeyProvider(name, Set.of(keys)); } + /** + * Creates a new instance of the {@link KeyEncryptionKeyProvider} with a given name a + * collection of {@link KeyEncryptionKey} which it owns. + * @param name provider name, can't be {@literal null} + * @param keys keys that the provider should own, can't be {@literal null} + * @return encryption key provider, never {@literal null} + */ + @NonNull static KeyEncryptionKeyProvider of(String name, Collection keys) { return new SimpleKeyEncryptionKeyProvider(name, keys); } @@ -31,7 +47,7 @@ static KeyEncryptionKeyProvider of(String name, Collection key * Name is considered as the main identifier of a provider. When using multiple * provider implementations within one application it is important that the names are * unique. - * @return provider name, never {@link null} + * @return provider name, never {@literal null} */ @NonNull String getName(); @@ -41,7 +57,7 @@ static KeyEncryptionKeyProvider of(String name, Collection key * {@link EncryptedKeyset}. * @param encryptedKeyset keyset for which the KEK should be provided, can't be * {@literal null} - * @return the KEK for this keyset, never {@link null} + * @return the KEK for this keyset, never {@literal null} * @throws com.konfigyr.crypto.CryptoException.KeyEncryptionKeyNotFoundException when * the provider could not resolve the {@link KeyEncryptionKey} with the given * identifier @@ -54,7 +70,7 @@ static KeyEncryptionKeyProvider of(String name, Collection key * Retrieves and constructs the {@link KeyEncryptionKey} used to decrypt or encrypt * the Data Encryption Keys by the provider name. * @param id the identifier of the {@link KeyEncryptionKey}, can't be {@literal null} - * @return matching key encryption key, never {@link null} + * @return matching key encryption key, never {@literal null} * @throws com.konfigyr.crypto.CryptoException.KeyEncryptionKeyNotFoundException when * the provider could not resolve the {@link KeyEncryptionKey} with the given * identifier diff --git a/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/Keyset.java b/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/Keyset.java index d74e63e..2385ebc 100644 --- a/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/Keyset.java +++ b/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/Keyset.java @@ -32,7 +32,7 @@ public interface Keyset extends KeysetDefinition { /** - * @return key encryption key that generated this keyset, never {@link null}. + * @return key encryption key that generated this keyset, never {@literal null}. */ @NonNull KeyEncryptionKey getKeyEncryptionKey(); diff --git a/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/KeysetDefinition.java b/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/KeysetDefinition.java index 2f59a57..3800b47 100644 --- a/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/KeysetDefinition.java +++ b/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/KeysetDefinition.java @@ -23,7 +23,7 @@ public interface KeysetDefinition { /** * Name that uniquely identifies the {@link KeysetDefinition}. - * @return keyset name, never {@link null}. + * @return keyset name, never {@literal null}. */ @NonNull String getName(); @@ -32,7 +32,7 @@ public interface KeysetDefinition { * Cryptographic algorithm that is used by this key set to perform * {@link KeysetOperation operations}. * @return algorithm that is used by this {@link KeysetDefinition}, never - * {@link null}. + * {@literal null}. * @see Algorithm */ @NonNull @@ -40,7 +40,7 @@ public interface KeysetDefinition { /** * Interval that defines the key rotation frequency. - * @return rotation frequency, never {@link null}. + * @return rotation frequency, never {@literal null}. * @see Keyset#rotate() */ @NonNull @@ -49,20 +49,46 @@ public interface KeysetDefinition { /** * Time when this keyset should be rotated. This would mean that the current primary * key would be exchanged with a new one. - * @return next rotation time, never {@link null}. + * @return next rotation time, never {@literal null}. * @see Keyset#rotate() */ @NonNull Instant getNextRotationTime(); + /** + * Creates a new definition of the {@link Keyset} that should be created. The definition would use + * a default value of 90 days for a rotation frequency. + * + * @param name name of the keyset, can't be {@literal null} + * @param algorithm algorithm that should be used by the keyset, can't be {@literal null} + * @return keyset definition with a 90 days rotation frequency + */ static KeysetDefinition of(@NonNull String name, @NonNull Algorithm algorithm) { return of(name, algorithm, Duration.ofDays(90)); } + /** + * Creates a new definition of the {@link Keyset} that should be created with a custom rotation frequency. + * + * @param name name of the keyset, can't be {@literal null} + * @param algorithm algorithm that should be used by the keyset, can't be {@literal null} + * @param rotationInterval rotation frequency of the keyset, can't be {@literal null} + * @return keyset definition with a custom rotation frequency + */ static KeysetDefinition of(@NonNull String name, @NonNull Algorithm algorithm, @NonNull Duration rotationInterval) { return of(name, algorithm, rotationInterval, Instant.now().plus(rotationInterval)); } + /** + * Creates a new definition of the {@link Keyset} that should be created with a custom rotation frequency and + * next rotation time. + * + * @param name name of the keyset, can't be {@literal null} + * @param algorithm algorithm that should be used by the keyset, can't be {@literal null} + * @param rotationInterval rotation frequency of the keyset, can't be {@literal null} + * @param nextRotationTime timestamp when this keyset should be rotated, can't be {@literal null} + * @return keyset definition with a custom rotation frequency and time + */ static KeysetDefinition of(@NonNull String name, @NonNull Algorithm algorithm, @NonNull Duration rotationInterval, @NonNull Instant nextRotationTime) { return new SimpleKeysetDefinition(name, algorithm, rotationInterval, nextRotationTime); diff --git a/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/KeysetFactory.java b/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/KeysetFactory.java index a987178..6b69cc8 100644 --- a/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/KeysetFactory.java +++ b/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/KeysetFactory.java @@ -16,7 +16,7 @@ *

* Before the start of the implementation of this interface it is important to consider * how should your cryptographic library implement the {@link Keyset}, {@link Algorithm} - * and {@link KeysetFactory interfaces. + * and {@link KeysetFactory} interfaces. *

* What is it that can be used to unique identify both a {@link KeysetDefinition} and * {@link EncryptedKeyset} uniqely to your cryptographic library? @@ -62,6 +62,7 @@ public interface KeysetFactory { * @param kek Key encryption key used to wrap or unwrap the private key material, * can't be {@literal null}. * @param definition definition of a keyset to be created, can't be {@literal null} + * @return generated keyset, never {@literal null} * @throws IOException when there is an issue while generating the private key * material. */ @@ -71,6 +72,7 @@ public interface KeysetFactory { * Creates a new {@link EncryptedKeyset} that should be stored by the * {@link KeysetRepository}. * @param keyset keyset to be wrapped, can't be {@literal null} + * @return encrytped keyset, never {@literal null} * @throws IOException when there is an issue while wrapping the private key material. */ EncryptedKeyset create(@NonNull Keyset keyset) throws IOException; @@ -81,6 +83,7 @@ public interface KeysetFactory { * @param kek Key encryption key used to unwrap the encrypted private key material, * can't be {@literal null}. * @param encryptedKeyset encrypted keyset to be unwrapped, can't be {@literal null} + * @return decrypted keyset, never {@literal null} * @throws IOException when there is an issue while unwrapping the encrypted private * key material. */ diff --git a/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/KeysetStore.java b/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/KeysetStore.java index 259c38b..9970d15 100644 --- a/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/KeysetStore.java +++ b/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/KeysetStore.java @@ -14,7 +14,7 @@ *

  • Key rotation
  • * * - *

    Key selection

    + *

    Key selection

    * * Library should provide a way for developers to choose which cryptographic algorithms * should they use within their application. This is defined by the @@ -31,7 +31,7 @@ * this does not happen by introducing a list of {@link KeysetOperation} that one * {@link Keyset} can perform. * - *

    Key storage

    + *

    Key storage

    * * Choosing where {@link Keyset cryptographic keys} are stored within the application is * not easy task. This library provides a way to define your custom storage of @@ -49,9 +49,10 @@ * to your application in a secure way and that you never store it in the same place where * you store your {@link EncryptedKeyset eDEKs}. * - *

    Key rotation

    It is important to understand that your {@link Keyset Data - * Encryption Keys} should be changed (or rotated) based on a number of different - * criteria: + *

    Key rotation

    + * + * It is important to understand that your {@link Keyset Data Encryption Keys} should be + * changed (or rotated) based on a number of different criteria: *
      *
    • If the previous key is known (or suspected) to have been compromised
    • *
    • After a specified period of time has elapsed (known as the crypto period)
    • diff --git a/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/SimpleKeyEncryptionKeyProvider.java b/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/SimpleKeyEncryptionKeyProvider.java index 8a2a2b0..9eda0bb 100644 --- a/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/SimpleKeyEncryptionKeyProvider.java +++ b/konfigyr-crypto-api/src/main/java/com/konfigyr/crypto/SimpleKeyEncryptionKeyProvider.java @@ -11,7 +11,7 @@ * Simple implementation of the {@link KeyEncryptionKeyProvider} that contains a list of * {@link KeyEncryptionKey Key Encryption Keys} for which it is responsible for. * - * @author : vladimir.spasic@ebf.com + * @author : Vladimir Spasic * @since : 31.08.23, Thu **/ class SimpleKeyEncryptionKeyProvider implements KeyEncryptionKeyProvider { diff --git a/konfigyr-crypto-api/src/main/java/com/konfigyr/io/ByteArray.java b/konfigyr-crypto-api/src/main/java/com/konfigyr/io/ByteArray.java index abadb40..f691452 100644 --- a/konfigyr-crypto-api/src/main/java/com/konfigyr/io/ByteArray.java +++ b/konfigyr-crypto-api/src/main/java/com/konfigyr/io/ByteArray.java @@ -34,6 +34,11 @@ public record ByteArray(byte[] array) implements InputStreamSource, Serializable private static final ByteArray EMPTY = new ByteArray(new byte[0]); + /** + * Creates a new {@link ByteArray} by copying the data from the given array of bytes. + * + * @param array byte array data + */ public ByteArray(byte[] array) { this.array = Arrays.copyOf(array, array.length); } @@ -49,6 +54,7 @@ public static ByteArray empty() { /** * Creates a new {@link ByteArray} instance from the given {@link ByteBuffer}. + * @param buffer byte buffer to be wrapped, can't be {@literal null} * @return byte array, never {@literal null} */ @NonNull @@ -58,16 +64,21 @@ public static ByteArray from(@NonNull ByteBuffer buffer) { /** * Creates a new {@link ByteArray} instance from the given {@link DataBuffer}. + * @param buffer data buffer to be wrapped, can't be {@literal null} * @return byte array, never {@literal null} */ @NonNull public static ByteArray from(@NonNull DataBuffer buffer) { - return from(buffer.asByteBuffer()); + final ByteBuffer bb = ByteBuffer.allocate(buffer.capacity()); + buffer.toByteBuffer(bb); + + return new ByteArray(bb.array()); } /** * Creates a new {@link ByteArray} instance from the given string. The string is * converted to bytes using the {@link StandardCharsets#UTF_8} charset. + * @param data raw string to be wrapped, can't be {@literal null} * @return byte array, never {@literal null} */ @NonNull @@ -78,6 +89,9 @@ public static ByteArray fromString(@NonNull String data) { /** * Creates a new {@link ByteArray} instance from the given string. The string is * converted to bytes using the given {@link StandardCharsets} charset. + * @param data raw string to be wrapped, can't be {@literal null} + * @param charset character encoding used to encode the string, can't be + * {@literal null} * @return byte array, never {@literal null} */ @NonNull @@ -89,6 +103,7 @@ public static ByteArray fromString(@NonNull String data, @NonNull Charset charse * Creates a new {@link ByteArray} instance from the given Base64 URL Safe encoded * string. * @return byte array, never {@literal null} + * @param data Base64 encoded string to be wrapped, can't be {@literal null} * @throws IllegalArgumentException when the Base64 string is invalid */ @NonNull diff --git a/konfigyr-crypto-api/src/test/java/com/konfigyr/crypto/KeyEncryptionKeyProviderTest.java b/konfigyr-crypto-api/src/test/java/com/konfigyr/crypto/KeyEncryptionKeyProviderTest.java index fe24251..390d098 100644 --- a/konfigyr-crypto-api/src/test/java/com/konfigyr/crypto/KeyEncryptionKeyProviderTest.java +++ b/konfigyr-crypto-api/src/test/java/com/konfigyr/crypto/KeyEncryptionKeyProviderTest.java @@ -10,7 +10,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; /** - * @author : vladimir.spasic@ebf.com + * @author : Vladimir Spasic * @since : 31.08.23, Thu **/ class KeyEncryptionKeyProviderTest { @@ -77,4 +77,4 @@ private static KeyEncryptionKey mockKey(String provider, String id) { return key; } -} \ No newline at end of file +} diff --git a/konfigyr-crypto-jdbc/build.gradle b/konfigyr-crypto-jdbc/build.gradle index 754ef4e..0a2e8e2 100644 --- a/konfigyr-crypto-jdbc/build.gradle +++ b/konfigyr-crypto-jdbc/build.gradle @@ -1,6 +1,9 @@ +description = 'Konfigyr Crypto library that uses Spring Data JDBC to store your encrypted key material' + dependencies { api project(':konfigyr-crypto-api') - api 'org.springframework.boot:spring-boot-starter-jdbc' + compileOnly 'org.springframework.boot:spring-boot-starter-jdbc' testImplementation 'org.hsqldb:hsqldb' -} \ No newline at end of file + testImplementation 'org.springframework.boot:spring-boot-starter-jdbc' +} diff --git a/konfigyr-crypto-tink/build.gradle b/konfigyr-crypto-tink/build.gradle index eab6749..b8b865a 100644 --- a/konfigyr-crypto-tink/build.gradle +++ b/konfigyr-crypto-tink/build.gradle @@ -1,4 +1,10 @@ +description = 'Konfigyr Crypto library that uses Google Tink as an implementation of the Keysets' + dependencies { api project(':konfigyr-crypto-api') - api 'com.google.crypto.tink:tink:1.10.0' -} \ No newline at end of file + compileOnly 'com.google.crypto.tink:tink:1.10.0' + compileOnly 'org.springframework.boot:spring-boot-starter' + + testImplementation 'com.google.crypto.tink:tink:1.10.0' + testImplementation 'org.springframework.boot:spring-boot-starter' +} diff --git a/konfigyr-crypto-tink/src/main/java/com/konfigyr/crypto/tink/TinkKeyEncryptionKey.java b/konfigyr-crypto-tink/src/main/java/com/konfigyr/crypto/tink/TinkKeyEncryptionKey.java index eaf102a..3c63e41 100644 --- a/konfigyr-crypto-tink/src/main/java/com/konfigyr/crypto/tink/TinkKeyEncryptionKey.java +++ b/konfigyr-crypto-tink/src/main/java/com/konfigyr/crypto/tink/TinkKeyEncryptionKey.java @@ -75,10 +75,17 @@ interface AeadFactory { } + /** + * Builder class used to construct a Tink based {@link KeyEncryptionKey}. + */ public static final class Builder { private final String provider; + /** + * Creates a new builder with the {@link com.konfigyr.crypto.KeyEncryptionKeyProvider} name. + * @param provider key provider name + */ private Builder(String provider) { this.provider = provider; }