├── .release-trigger ├── .gitattributes ├── lowkey-vault-client ├── src │ ├── main │ │ ├── resources │ │ │ └── .gitkeep │ │ └── java │ │ │ └── com │ │ │ └── github │ │ │ └── nagyesta │ │ │ └── lowkeyvault │ │ │ └── http │ │ │ ├── management │ │ │ ├── LowkeyVaultException.java │ │ │ ├── EpochSecondsDeserializer.java │ │ │ ├── EpochSecondsSerializer.java │ │ │ ├── VaultModel.java │ │ │ ├── LowkeyVaultManagementClient.java │ │ │ └── impl │ │ │ │ └── UriUtil.java │ │ │ └── ContentLengthHeaderRemover.java │ └── test │ │ ├── resources │ │ └── .gitkeep │ │ └── java │ │ └── com │ │ └── github │ │ └── nagyesta │ │ └── lowkeyvault │ │ └── http │ │ ├── ContentLengthHeaderRemoverTest.java │ │ └── management │ │ └── RecoveryLevelTest.java └── lombok.config ├── lowkey-vault-docker └── src │ ├── main │ ├── java │ │ └── .gitkeep-java │ └── resources │ │ └── .gitkeep │ ├── test │ ├── resources │ │ ├── application.properties │ │ ├── certs │ │ │ ├── rsa-localhost.p12 │ │ │ ├── rsa-example-com.p12 │ │ │ ├── ec521-ec-localhost.p12 │ │ │ └── ec521-ec-localhost.pem │ │ ├── logback-test.xml │ │ ├── json │ │ │ └── backups │ │ │ │ ├── jsonBackupOct-128-72.json │ │ │ │ ├── jsonBackupOct-192-72.json │ │ │ │ ├── jsonBackupOct-256-72.json │ │ │ │ ├── jsonBackupEc-256-72.json │ │ │ │ ├── jsonBackupEc-256k-72.json │ │ │ │ ├── jsonBackupEc-384-72.json │ │ │ │ └── jsonBackupEc-521-72.json │ │ └── com │ │ │ └── github │ │ │ └── nagyesta │ │ │ └── lowkeyvault │ │ │ ├── keys │ │ │ └── RandomData.feature │ │ │ └── secrets │ │ │ ├── ListSecrets.feature │ │ │ └── ListDeletedSecrets.feature │ └── java │ │ └── com │ │ └── github │ │ └── nagyesta │ │ └── lowkeyvault │ │ └── sequential │ │ └── RunSequentialCucumberTest.java │ └── docker │ └── Dockerfile ├── lowkey-vault-testcontainers └── src │ ├── main │ ├── resources │ │ └── .gitkeep │ └── java │ │ └── com │ │ └── github │ │ └── nagyesta │ │ └── lowkeyvault │ │ └── testcontainers │ │ ├── StoreType.java │ │ └── ContainerDependency.java │ └── test │ └── resources │ ├── config.properties │ ├── cert.jks │ ├── lowkey-vault-keystore.p12 │ ├── empty-import.json │ └── logback-test.xml ├── .idea ├── icon.png └── codeStyles │ └── codeStyleConfig.xml ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties └── verification-metadata-clean.xml ├── lowkey-vault-app ├── src │ ├── test │ │ ├── resources │ │ │ ├── test-application.properties │ │ │ ├── cert │ │ │ │ ├── ec.p12 │ │ │ │ ├── rsa.p12 │ │ │ │ ├── ec.pem │ │ │ │ └── invalid-ec.pem │ │ │ ├── META-INF │ │ │ │ └── services │ │ │ │ │ └── org.junit.jupiter.api.extension.Extension │ │ │ ├── key │ │ │ │ ├── import │ │ │ │ │ ├── aes-import-missing-k.json │ │ │ │ │ ├── aes-import-missing-kty.json │ │ │ │ │ ├── ec-import-missing-d.json │ │ │ │ │ ├── ec-import-missing-x.json │ │ │ │ │ ├── ec-import-missing-y.json │ │ │ │ │ ├── ec-import-missing-crv.json │ │ │ │ │ ├── aes-import-valid.json │ │ │ │ │ ├── ec-import-valid.json │ │ │ │ │ ├── rsa-import-missing-d.json │ │ │ │ │ └── rsa-import-missing-n.json │ │ │ │ └── rotation │ │ │ │ │ ├── invalid-rotation-policy-missing-actions.json │ │ │ │ │ ├── invalid-rotation-policy-empty-actions.json │ │ │ │ │ ├── invalid-rotation-policy-null-trigger.json │ │ │ │ │ ├── valid-rotation-policy-minimum.json │ │ │ │ │ ├── invalid-rotation-policy-missing-attributes.json │ │ │ │ │ ├── invalid-rotation-policy-missing-created.json │ │ │ │ │ ├── invalid-rotation-policy-missing-updated.json │ │ │ │ │ ├── invalid-rotation-policy-missing-expiry.json │ │ │ │ │ ├── valid-rotation-policy-full.json │ │ │ │ │ └── invalid-rotation-policy-null-action-type.json │ │ │ ├── logback-test.xml │ │ │ └── template │ │ │ │ ├── no-placeholder.json │ │ │ │ ├── host-placeholder-input.json.hbs │ │ │ │ ├── port-placeholder-input.json.hbs │ │ │ │ ├── all-placeholder-input.json.hbs │ │ │ │ └── time-placeholder-input.json.hbs │ │ └── java │ │ │ └── com │ │ │ └── github │ │ │ └── nagyesta │ │ │ └── lowkeyvault │ │ │ ├── service │ │ │ ├── key │ │ │ │ ├── impl │ │ │ │ │ └── KeyCreationInputTest.java │ │ │ │ └── id │ │ │ │ │ └── KeyEntityIdTest.java │ │ │ └── certificate │ │ │ │ └── impl │ │ │ │ └── DefaultCertificateLifetimeActionPolicyTest.java │ │ │ ├── controller │ │ │ ├── BaseVaultConfiguration.java │ │ │ └── common │ │ │ │ ├── util │ │ │ │ └── CertificateRequestMapperUtilTest.java │ │ │ │ └── PingControllerTest.java │ │ │ ├── mapper │ │ │ └── v7_3 │ │ │ │ └── certificate │ │ │ │ ├── CertificateEntityToV73ModelConverterTest.java │ │ │ │ ├── CertificateEntityToV73PropertiesModelConverterTest.java │ │ │ │ └── CertificateEntityToV73IssuancePolicyModelConverterTest.java │ │ │ ├── openapi │ │ │ └── ExamplesTest.java │ │ │ ├── model │ │ │ ├── common │ │ │ │ └── ApiConstantsTest.java │ │ │ ├── v7_3 │ │ │ │ ├── certificate │ │ │ │ │ └── IssuerParameterModelTest.java │ │ │ │ └── key │ │ │ │ │ ├── KeyLifetimeActionTypeModelTest.java │ │ │ │ │ └── constants │ │ │ │ │ └── LifetimeActionTypeTest.java │ │ │ └── v7_2 │ │ │ │ └── key │ │ │ │ └── constants │ │ │ │ ├── EncryptionAlgorithmTest.java │ │ │ │ ├── KeyCurveNameTest.java │ │ │ │ └── KeyOperationTest.java │ │ │ └── HashUtil.java │ └── main │ │ ├── resources │ │ └── cert │ │ │ └── keystore.p12 │ │ └── java │ │ └── com │ │ └── github │ │ └── nagyesta │ │ └── lowkeyvault │ │ ├── model │ │ ├── v7_2 │ │ │ ├── key │ │ │ │ ├── validator │ │ │ │ │ ├── BaseKey.java │ │ │ │ │ ├── EcKey.java │ │ │ │ │ ├── OctKey.java │ │ │ │ │ ├── RsaKey.java │ │ │ │ │ └── ValidImportKey.java │ │ │ │ ├── KeyPropertiesModel.java │ │ │ │ ├── BackupListContainer.java │ │ │ │ ├── KeyVerifyResult.java │ │ │ │ ├── KeyVaultKeyItemModel.java │ │ │ │ ├── KeyVaultKeyModel.java │ │ │ │ ├── request │ │ │ │ │ ├── UpdateKeyRequest.java │ │ │ │ │ ├── ImportKeyRequest.java │ │ │ │ │ └── KeySignParameters.java │ │ │ │ ├── DeletedKeyVaultKeyModel.java │ │ │ │ └── DeletedKeyVaultKeyItemModel.java │ │ │ ├── secret │ │ │ │ ├── SecretPropertiesModel.java │ │ │ │ ├── request │ │ │ │ │ ├── UpdateSecretRequest.java │ │ │ │ │ └── CreateSecretRequest.java │ │ │ │ ├── KeyVaultSecretItemModel.java │ │ │ │ ├── KeyVaultSecretModel.java │ │ │ │ ├── DeletedKeyVaultSecretModel.java │ │ │ │ └── DeletedKeyVaultSecretItemModel.java │ │ │ ├── common │ │ │ │ ├── BaseBackupModel.java │ │ │ │ └── BaseBackupListItem.java │ │ │ └── BasePropertiesUpdateModel.java │ │ ├── v7_3 │ │ │ ├── key │ │ │ │ ├── validator │ │ │ │ │ ├── Restore.java │ │ │ │ │ ├── Update.java │ │ │ │ │ ├── ExpiryPeriod.java │ │ │ │ │ └── ExpiryPeriodValidator.java │ │ │ │ ├── RandomBytesRequest.java │ │ │ │ ├── RandomBytesResponse.java │ │ │ │ ├── constants │ │ │ │ │ └── LifetimeActionType.java │ │ │ │ ├── KeyLifetimeActionTypeModel.java │ │ │ │ └── KeyRotationPolicyModel.java │ │ │ └── certificate │ │ │ │ ├── CertificateSecretModel.java │ │ │ │ ├── CertificatePropertiesModel.java │ │ │ │ ├── UpdateCertificateRequest.java │ │ │ │ ├── GenerateCertificateRequest.java │ │ │ │ ├── SubjectAlternativeNames.java │ │ │ │ ├── CreateCertificateRequest.java │ │ │ │ ├── IssuerParameterModel.java │ │ │ │ ├── X509CertificateModel.java │ │ │ │ ├── KeyVaultCertificateItemModel.java │ │ │ │ ├── CertificateKeyModel.java │ │ │ │ ├── CertificateLifetimeActionModel.java │ │ │ │ ├── CertificateImportRequest.java │ │ │ │ ├── KeyVaultPendingCertificateModel.java │ │ │ │ ├── CertificatePolicyModel.java │ │ │ │ └── DeletedKeyVaultCertificateModel.java │ │ ├── OpenIdConfiguration.java │ │ ├── json │ │ │ └── util │ │ │ │ ├── Base64CertSerializer.java │ │ │ │ ├── Base64CertDeserializer.java │ │ │ │ ├── Base64ZipKeySerializer.java │ │ │ │ ├── Base64ZipSecretSerializer.java │ │ │ │ ├── Base64ZipCertificateSerializer.java │ │ │ │ ├── Base64ZipKeyDeserializer.java │ │ │ │ ├── CertificateLifetimeActionSerializer.java │ │ │ │ ├── Base64ZipSecretDeserializer.java │ │ │ │ ├── EpochSecondsDeserializer.java │ │ │ │ ├── Base64ZipCertificateDeserializer.java │ │ │ │ ├── EpochSecondsSerializer.java │ │ │ │ ├── CertificateLifetimeActionDeserializer.java │ │ │ │ └── Base64Serializer.java │ │ ├── common │ │ │ ├── ErrorModel.java │ │ │ ├── DeletedModel.java │ │ │ ├── backup │ │ │ │ ├── SecretBackupListItem.java │ │ │ │ ├── KeyBackupListItem.java │ │ │ │ ├── SecretBackupList.java │ │ │ │ ├── CertificateBackupList.java │ │ │ │ ├── KeyBackupModel.java │ │ │ │ ├── SecretBackupModel.java │ │ │ │ ├── KeyBackupList.java │ │ │ │ └── CertificateBackupModel.java │ │ │ └── KeyVaultItemListModel.java │ │ └── management │ │ │ ├── VaultBackupListModel.java │ │ │ └── VaultBackupModel.java │ │ ├── service │ │ ├── common │ │ │ ├── TimeAware.java │ │ │ ├── VersionedEntityMultiMap.java │ │ │ ├── BaseVaultFake.java │ │ │ └── ReadOnlyVersionedEntityMultiMap.java │ │ ├── key │ │ │ ├── ReadOnlyAesKeyVaultKeyEntity.java │ │ │ ├── ReadOnlyAsymmetricKeyVaultKeyEntity.java │ │ │ ├── LifetimeAction.java │ │ │ ├── KeyLifetimeAction.java │ │ │ ├── ReadOnlyEcKeyVaultKeyEntity.java │ │ │ ├── ReadOnlyRsaKeyVaultKeyEntity.java │ │ │ ├── LifetimeActionTrigger.java │ │ │ ├── impl │ │ │ │ ├── KeyCreationInput.java │ │ │ │ ├── OctKeyCreationInput.java │ │ │ │ ├── EcKeyCreationInput.java │ │ │ │ ├── RsaKeyCreationInput.java │ │ │ │ ├── DefaultKeyRotationPolicy.java │ │ │ │ └── KeyLifetimeActionTrigger.java │ │ │ ├── ReadOnlyDeletedEntity.java │ │ │ ├── util │ │ │ │ └── PeriodUtil.java │ │ │ ├── id │ │ │ │ ├── VersionedKeyEntityId.java │ │ │ │ └── KeyEntityId.java │ │ │ ├── RotationPolicy.java │ │ │ └── ReadOnlyRotationPolicy.java │ │ ├── exception │ │ │ ├── NotFoundException.java │ │ │ ├── AlreadyExistsException.java │ │ │ └── CryptoException.java │ │ ├── secret │ │ │ ├── ReadOnlyKeyVaultSecretEntity.java │ │ │ ├── id │ │ │ │ ├── SecretEntityId.java │ │ │ │ └── VersionedSecretEntityId.java │ │ │ └── SecretVaultFake.java │ │ ├── EntityId.java │ │ ├── certificate │ │ │ ├── LifetimeActionPolicy.java │ │ │ ├── CertificateLifetimeActionTrigger.java │ │ │ ├── id │ │ │ │ ├── VersionedCertificateEntityId.java │ │ │ │ └── CertificateEntityId.java │ │ │ ├── ReadOnlyLifetimeActionPolicy.java │ │ │ ├── CertificateLifetimeActionActivity.java │ │ │ └── impl │ │ │ │ ├── CertAuthorityType.java │ │ │ │ └── CertificateAlgorithm.java │ │ └── vault │ │ │ ├── VaultService.java │ │ │ └── VaultFake.java │ │ ├── template │ │ ├── backup │ │ │ ├── BackupContext.java │ │ │ ├── TimeHelperSource.java │ │ │ ├── BackupTemplateProcessor.java │ │ │ └── VaultImporterProperties.java │ │ └── HandlebarsTemplateProcessor.java │ │ ├── mapper │ │ ├── common │ │ │ ├── ApiVersionAwareConverter.java │ │ │ ├── AliasAwareConverter.java │ │ │ ├── RecoveryAwareConverter.java │ │ │ ├── BasePropertiesModelConverter.java │ │ │ └── VaultFakeToVaultModelConverter.java │ │ ├── v7_2 │ │ │ ├── key │ │ │ │ ├── BaseJsonWebKeyImportRequestConverter.java │ │ │ │ ├── KeyEntityToV72KeyVersionItemModelConverter.java │ │ │ │ └── AesJsonWebKeyImportRequestConverter.java │ │ │ └── secret │ │ │ │ └── SecretEntityToV72SecretVersionItemModelConverter.java │ │ └── v7_3 │ │ │ ├── certificate │ │ │ ├── CertificateEntityToV73IssuancePolicyModelConverter.java │ │ │ ├── CertificateEntityToV73PolicyModelConverter.java │ │ │ └── CertificateEntityToV73CertificateVersionItemModelConverter.java │ │ │ └── key │ │ │ └── RsaPrivateKeyToJsonWebKeyImportRequestConverter.java │ │ ├── controller │ │ └── PingController.java │ │ └── context │ │ └── ApiVersionAware.java └── lombok.config ├── .github ├── assets │ ├── LowkeyVault-icon.png │ ├── LowkeyVault-logo.png │ ├── LowkeyVault-social.png │ ├── LowkeyVault-logo-full.png │ ├── LowkeyVault-Wallpaper-1920x1080.png │ ├── LowkeyVaultArchitecture-Classes.png │ └── LowkeyVaultArchitecture-Test-components.png ├── ISSUE_TEMPLATE │ ├── question.md │ ├── feature_request.md │ └── bug_report.md ├── release.yml └── workflows │ └── gradle-oss-index-scan.yml ├── settings.gradle.kts ├── .whitesource ├── gradle.properties ├── config └── ossindex │ ├── exclusions.txt │ └── ossIndexAudit.gradle.kts ├── pull_request_template.md ├── .lift.toml ├── .gitignore └── LICENSE /.release-trigger: -------------------------------------------------------------------------------- 1 | 1765227301 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | text eol=lf 2 | *.p12 binary 3 | -------------------------------------------------------------------------------- /lowkey-vault-client/src/main/resources/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lowkey-vault-client/src/test/resources/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lowkey-vault-docker/src/main/java/.gitkeep-java: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lowkey-vault-docker/src/main/resources/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lowkey-vault-testcontainers/src/main/resources/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.idea/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nagyesta/lowkey-vault/HEAD/.idea/icon.png -------------------------------------------------------------------------------- /lowkey-vault-docker/src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | logging.level.root=INFO 2 | -------------------------------------------------------------------------------- /lowkey-vault-testcontainers/src/test/resources/config.properties: -------------------------------------------------------------------------------- 1 | logging.level.root=INFO 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nagyesta/lowkey-vault/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/test-application.properties: -------------------------------------------------------------------------------- 1 | LOWKEY_DEBUG_REQUEST_LOG=true 2 | app.token.port=8080 3 | -------------------------------------------------------------------------------- /.github/assets/LowkeyVault-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nagyesta/lowkey-vault/HEAD/.github/assets/LowkeyVault-icon.png -------------------------------------------------------------------------------- /.github/assets/LowkeyVault-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nagyesta/lowkey-vault/HEAD/.github/assets/LowkeyVault-logo.png -------------------------------------------------------------------------------- /.github/assets/LowkeyVault-social.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nagyesta/lowkey-vault/HEAD/.github/assets/LowkeyVault-social.png -------------------------------------------------------------------------------- /.github/assets/LowkeyVault-logo-full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nagyesta/lowkey-vault/HEAD/.github/assets/LowkeyVault-logo-full.png -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/cert/ec.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nagyesta/lowkey-vault/HEAD/lowkey-vault-app/src/test/resources/cert/ec.p12 -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/cert/rsa.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nagyesta/lowkey-vault/HEAD/lowkey-vault-app/src/test/resources/cert/rsa.p12 -------------------------------------------------------------------------------- /.github/assets/LowkeyVault-Wallpaper-1920x1080.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nagyesta/lowkey-vault/HEAD/.github/assets/LowkeyVault-Wallpaper-1920x1080.png -------------------------------------------------------------------------------- /.github/assets/LowkeyVaultArchitecture-Classes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nagyesta/lowkey-vault/HEAD/.github/assets/LowkeyVaultArchitecture-Classes.png -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/resources/cert/keystore.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nagyesta/lowkey-vault/HEAD/lowkey-vault-app/src/main/resources/cert/keystore.p12 -------------------------------------------------------------------------------- /lowkey-vault-testcontainers/src/test/resources/cert.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nagyesta/lowkey-vault/HEAD/lowkey-vault-testcontainers/src/test/resources/cert.jks -------------------------------------------------------------------------------- /.github/assets/LowkeyVaultArchitecture-Test-components.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nagyesta/lowkey-vault/HEAD/.github/assets/LowkeyVaultArchitecture-Test-components.png -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension: -------------------------------------------------------------------------------- 1 | com.github.nagyesta.abortmission.booster.jupiter.extension.AbortMissionExtension 2 | -------------------------------------------------------------------------------- /lowkey-vault-docker/src/test/resources/certs/rsa-localhost.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nagyesta/lowkey-vault/HEAD/lowkey-vault-docker/src/test/resources/certs/rsa-localhost.p12 -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/key/import/aes-import-missing-k.json: -------------------------------------------------------------------------------- 1 | { 2 | "key": { 3 | "kty": "oct" 4 | }, 5 | "tags": { 6 | "purpose": "unit test" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /lowkey-vault-docker/src/test/resources/certs/rsa-example-com.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nagyesta/lowkey-vault/HEAD/lowkey-vault-docker/src/test/resources/certs/rsa-example-com.p12 -------------------------------------------------------------------------------- /lowkey-vault-docker/src/test/resources/certs/ec521-ec-localhost.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nagyesta/lowkey-vault/HEAD/lowkey-vault-docker/src/test/resources/certs/ec521-ec-localhost.p12 -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /lowkey-vault-testcontainers/src/test/resources/lowkey-vault-keystore.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nagyesta/lowkey-vault/HEAD/lowkey-vault-testcontainers/src/test/resources/lowkey-vault-keystore.p12 -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "lowkey-vault" 2 | include("lowkey-vault-app") 3 | include("lowkey-vault-client") 4 | include("lowkey-vault-docker") 5 | include("lowkey-vault-testcontainers") 6 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/validator/BaseKey.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.key.validator; 2 | 3 | public interface BaseKey { 4 | } 5 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/validator/Restore.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.key.validator; 2 | 3 | public interface Restore { 4 | } 5 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/validator/Update.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.key.validator; 2 | 3 | public interface Update { 4 | } 5 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/validator/EcKey.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.key.validator; 2 | 3 | public interface EcKey extends BaseKey { 4 | } 5 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/validator/OctKey.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.key.validator; 2 | 3 | public interface OctKey extends BaseKey { 4 | } 5 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/validator/RsaKey.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.key.validator; 2 | 3 | public interface RsaKey extends BaseKey { 4 | } 5 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/key/import/aes-import-missing-kty.json: -------------------------------------------------------------------------------- 1 | { 2 | "key": { 3 | "k": "K1RcwUk8MyFzolfX42k7FltUeCGCU7cPTCla7g9MS2c" 4 | }, 5 | "tags": { 6 | "purpose": "unit test" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /lowkey-vault-app/lombok.config: -------------------------------------------------------------------------------- 1 | # This file is generated by the 'io.freefair.lombok' Gradle plugin 2 | config.stopBubbling = true 3 | lombok.addLombokGeneratedAnnotation = true 4 | lombok.nonNull.exceptionType = IllegalArgumentException 5 | -------------------------------------------------------------------------------- /lowkey-vault-client/lombok.config: -------------------------------------------------------------------------------- 1 | # This file is generated by the 'io.freefair.lombok' Gradle plugin 2 | config.stopBubbling = true 3 | lombok.addLombokGeneratedAnnotation = true 4 | lombok.nonNull.exceptionType = IllegalArgumentException 5 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/common/TimeAware.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.common; 2 | 3 | public interface TimeAware { 4 | 5 | void timeShift(int offsetSeconds); 6 | } 7 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/key/rotation/invalid-rotation-policy-missing-actions.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https:/localhost:8443/keys/key-name/rotationpolicy", 3 | "attributes": { 4 | "expiryTime": "P4M", 5 | "created": 1482188947, 6 | "updated": 1482188948 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.whitesource: -------------------------------------------------------------------------------- 1 | { 2 | "scanSettings": { 3 | "baseBranches": [] 4 | }, 5 | "checkRunSettings": { 6 | "vulnerableCheckRunConclusionLevel": "failure", 7 | "displayMode": "diff" 8 | }, 9 | "issueSettings": { 10 | "minSeverityLevel": "LOW", 11 | "issueType": "DEPENDENCY" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/key/rotation/invalid-rotation-policy-empty-actions.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https:/localhost:8443/keys/key-name/rotationpolicy", 3 | "lifetimeActions": [], 4 | "attributes": { 5 | "expiryTime": "P4M", 6 | "created": 1482188947, 7 | "updated": 1482188948 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/key/import/ec-import-missing-d.json: -------------------------------------------------------------------------------- 1 | { 2 | "key": { 3 | "kty": "EC", 4 | "crv": "P-256", 5 | "x": "bgaA-ohoODwHW-g6idvSTETho-mkFYmsQ2tYcNgYxbQ", 6 | "y": "1fa3lOetqDuLmdXIahRFKegv5MSmdxewHT4YVvBZxrg" 7 | }, 8 | "tags": { 9 | "purpose": "unit test" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/KeyPropertiesModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.key; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.v7_2.BasePropertiesModel; 4 | 5 | public final class KeyPropertiesModel 6 | extends BasePropertiesModel { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/ReadOnlyAesKeyVaultKeyEntity.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.key; 2 | 3 | public interface ReadOnlyAesKeyVaultKeyEntity 4 | extends ReadOnlyKeyVaultKeyEntity { 5 | 6 | byte[] getK(); 7 | 8 | int getKeySize(); 9 | } 10 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/key/import/ec-import-missing-x.json: -------------------------------------------------------------------------------- 1 | { 2 | "key": { 3 | "kty": "EC", 4 | "crv": "P-256", 5 | "d": "AIr3-iApan7BvLYEHhXmScSQNn-B5N7-Bs5Hvi9_JUrP", 6 | "y": "1fa3lOetqDuLmdXIahRFKegv5MSmdxewHT4YVvBZxrg" 7 | }, 8 | "tags": { 9 | "purpose": "unit test" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/key/import/ec-import-missing-y.json: -------------------------------------------------------------------------------- 1 | { 2 | "key": { 3 | "kty": "EC", 4 | "crv": "P-256", 5 | "d": "AIr3-iApan7BvLYEHhXmScSQNn-B5N7-Bs5Hvi9_JUrP", 6 | "x": "bgaA-ohoODwHW-g6idvSTETho-mkFYmsQ2tYcNgYxbQ" 7 | }, 8 | "tags": { 9 | "purpose": "unit test" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/BackupListContainer.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.key; 2 | 3 | import java.util.List; 4 | 5 | public interface BackupListContainer { 6 | 7 | List getVersions(); 8 | 9 | void setVersions(List versions); 10 | } 11 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/secret/SecretPropertiesModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.secret; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.v7_2.BasePropertiesModel; 4 | 5 | public final class SecretPropertiesModel 6 | extends BasePropertiesModel { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /lowkey-vault-testcontainers/src/main/java/com/github/nagyesta/lowkeyvault/testcontainers/StoreType.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.testcontainers; 2 | 3 | public enum StoreType { 4 | 5 | /** 6 | * Java key store. 7 | */ 8 | JKS, 9 | /** 10 | * PKCS12 store. 11 | */ 12 | PKCS12 13 | } 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Do you need to find out something but can't find it in our readme? Please feel 4 | free to ask! 5 | title: "" 6 | labels: question 7 | assignees: "" 8 | --- 9 | 10 | #### Question 11 | 12 | Please ask your question as clearly as possible to let us find a better answer... 13 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/ReadOnlyAsymmetricKeyVaultKeyEntity.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.key; 2 | 3 | import java.security.KeyPair; 4 | 5 | public interface ReadOnlyAsymmetricKeyVaultKeyEntity 6 | extends ReadOnlyKeyVaultKeyEntity { 7 | 8 | KeyPair getKey(); 9 | } 10 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/key/import/ec-import-missing-crv.json: -------------------------------------------------------------------------------- 1 | { 2 | "key": { 3 | "kty": "EC", 4 | "d": "AIr3-iApan7BvLYEHhXmScSQNn-B5N7-Bs5Hvi9_JUrP", 5 | "x": "bgaA-ohoODwHW-g6idvSTETho-mkFYmsQ2tYcNgYxbQ", 6 | "y": "1fa3lOetqDuLmdXIahRFKegv5MSmdxewHT4YVvBZxrg" 7 | }, 8 | "tags": { 9 | "purpose": "unit test" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # suppress inspection "SpellCheckingInspection" for whole file 2 | # suppress inspection "UnusedProperty" for whole file 3 | # rebuilds 1 4 | org.gradle.warning.mode=all 5 | org.gradle.daemon=true 6 | org.gradle.caching=true 7 | org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError 8 | org.gradle.dependency.verification.console=verbose 9 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/LifetimeAction.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.key; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.v7_3.key.constants.LifetimeActionType; 4 | 5 | public interface LifetimeAction { 6 | 7 | LifetimeActionType actionType(); 8 | 9 | LifetimeActionTrigger trigger(); 10 | } 11 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/key/import/aes-import-valid.json: -------------------------------------------------------------------------------- 1 | { 2 | "key": { 3 | "kty": "oct-HSM", 4 | "k": "K1RcwUk8MyFzolfX42k7FltUeCGCU7cPTCla7g9MS2c", 5 | "key_ops": [ 6 | "encrypt", "decrypt", "wrapKey", "unwrapKey" 7 | ] 8 | }, 9 | "properties": { 10 | "hsm": true 11 | }, 12 | "tags": { 13 | "purpose": "unit test" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/OpenIdConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import java.net.URI; 6 | 7 | public record OpenIdConfiguration( 8 | @JsonProperty("issuer") String issuer, 9 | @JsonProperty("jwks_uri") URI jwksUri) { 10 | } 11 | -------------------------------------------------------------------------------- /config/ossindex/exclusions.txt: -------------------------------------------------------------------------------- 1 | CVE-2016-1000027 2 | CVE-2022-38752 3 | CVE-2022-42003 4 | CVE-2022-31684 5 | CVE-2022-41854 6 | CVE-2022-1471 7 | sonatype-2022-6438 8 | CVE-2023-35116 9 | CVE-2023-42503 10 | CVE-2023-34062 11 | CVE-2023-34054 12 | CVE-2024-25710 13 | CVE-2024-26308 14 | CVE-2024-38828 15 | CVE-2025-48924 16 | CVE-2025-22227 17 | CVE-2025-55163 18 | CVE-2025-58056 19 | CVE-2025-58057 20 | -------------------------------------------------------------------------------- /config/ossindex/ossIndexAudit.gradle.kts: -------------------------------------------------------------------------------- 1 | buildscript { 2 | fun readExclusions(): MutableSet { 3 | return rootProject.file("config/ossindex/exclusions.txt").readLines() 4 | .stream() 5 | .toList() 6 | .filter { it.isNotBlank() } 7 | .toMutableSet() 8 | } 9 | 10 | extra.set("ossIndexExclusions", readExclusions()) 11 | } 12 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/template/backup/BackupContext.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.template.backup; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NonNull; 6 | 7 | @Data 8 | @AllArgsConstructor 9 | public class BackupContext { 10 | 11 | @NonNull 12 | private String host; 13 | 14 | private int port; 15 | } 16 | -------------------------------------------------------------------------------- /lowkey-vault-testcontainers/src/test/resources/empty-import.json: -------------------------------------------------------------------------------- 1 | { 2 | "vaults": [ 3 | { 4 | "attributes": { 5 | "baseUri": "https://localhost:8443", 6 | "recoveryLevel": "Recoverable+Purgeable", 7 | "recoverableDays": 90, 8 | "created": 1647809444, 9 | "deleted": null 10 | }, 11 | "keys": {}, 12 | "secrets": {} 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64CertSerializer.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.json.util; 2 | 3 | import java.util.Base64; 4 | 5 | public class Base64CertSerializer extends Base64Serializer { 6 | 7 | private static final Base64.Encoder ENCODER = Base64.getEncoder(); 8 | 9 | public Base64CertSerializer() { 10 | super(ENCODER); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/key/rotation/invalid-rotation-policy-null-trigger.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https:/localhost:8443/keys/key-name/rotationpolicy", 3 | "lifetimeActions": [ 4 | { 5 | "trigger": null, 6 | "action": { 7 | "type": "rotate" 8 | } 9 | } 10 | ], 11 | "attributes": { 12 | "expiryTime": "P4M", 13 | "created": 1482188947, 14 | "updated": 1482188948 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/common/ErrorModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.common; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public record ErrorModel(@JsonProperty("error") ErrorMessage error) { 6 | 7 | public static ErrorModel fromException(final Exception e) { 8 | return new ErrorModel(ErrorMessage.fromException(e)); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/key/import/ec-import-valid.json: -------------------------------------------------------------------------------- 1 | { 2 | "key": { 3 | "kty": "EC", 4 | "crv": "P-256", 5 | "d": "AIr3-iApan7BvLYEHhXmScSQNn-B5N7-Bs5Hvi9_JUrP", 6 | "x": "bgaA-ohoODwHW-g6idvSTETho-mkFYmsQ2tYcNgYxbQ", 7 | "y": "1fa3lOetqDuLmdXIahRFKegv5MSmdxewHT4YVvBZxrg", 8 | "key_ops": [ 9 | "sign", "verify" 10 | ] 11 | }, 12 | "tags": { 13 | "purpose": "unit test" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64CertDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.json.util; 2 | 3 | import java.util.Base64; 4 | 5 | public class Base64CertDeserializer extends Base64Deserializer { 6 | 7 | private static final Base64.Decoder DECODER = Base64.getDecoder(); 8 | 9 | public Base64CertDeserializer() { 10 | super(DECODER); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/key/rotation/valid-rotation-policy-minimum.json: -------------------------------------------------------------------------------- 1 | { 2 | "attributes": { 3 | "created": 1482188947, 4 | "updated": 1482188948 5 | }, 6 | "id": "https:/localhost:8443/keys/key-name/rotationpolicy", 7 | "lifetimeActions": [ 8 | { 9 | "action": { 10 | "type": "rotate" 11 | }, 12 | "trigger": { 13 | "timeAfterCreate": "P90D" 14 | } 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/KeyLifetimeAction.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.key; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.v7_3.key.constants.LifetimeActionType; 4 | import lombok.NonNull; 5 | 6 | public record KeyLifetimeAction( 7 | @NonNull LifetimeActionType actionType, 8 | @NonNull LifetimeActionTrigger trigger) implements LifetimeAction { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/template/HandlebarsTemplateProcessor.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.template; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * Interface for processing Handlebars templates. 7 | * 8 | * @param The context object. 9 | */ 10 | public interface HandlebarsTemplateProcessor { 11 | 12 | String processTemplate(String templateAsString, T context) throws IOException; 13 | } 14 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/certificate/CertificateSecretModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.certificate; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import jakarta.validation.constraints.NotNull; 5 | import lombok.Data; 6 | 7 | @Data 8 | public class CertificateSecretModel { 9 | 10 | @NotNull 11 | @JsonProperty("contentType") 12 | private String contentType; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /lowkey-vault-client/src/main/java/com/github/nagyesta/lowkeyvault/http/management/LowkeyVaultException.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.http.management; 2 | 3 | public class LowkeyVaultException 4 | extends RuntimeException { 5 | 6 | public LowkeyVaultException(final String message) { 7 | super(message); 8 | } 9 | 10 | public LowkeyVaultException(final String message, final Throwable cause) { 11 | super(message, cause); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/mapper/common/ApiVersionAwareConverter.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.mapper.common; 2 | 3 | import com.github.nagyesta.lowkeyvault.context.ApiVersionAware; 4 | import org.springframework.beans.factory.InitializingBean; 5 | import org.springframework.core.convert.converter.Converter; 6 | 7 | public interface ApiVersionAwareConverter 8 | extends Converter, ApiVersionAware, InitializingBean { 9 | } 10 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/ReadOnlyEcKeyVaultKeyEntity.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.key; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyCurveName; 4 | 5 | public interface ReadOnlyEcKeyVaultKeyEntity 6 | extends ReadOnlyAsymmetricKeyVaultKeyEntity { 7 | 8 | byte[] getX(); 9 | 10 | byte[] getY(); 11 | 12 | byte[] getD(); 13 | 14 | KeyCurveName getKeyCurveName(); 15 | } 16 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/certificate/CertificatePropertiesModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.certificate; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.v7_2.BasePropertiesModel; 4 | 5 | public final class CertificatePropertiesModel 6 | extends BasePropertiesModel { 7 | 8 | public CertificatePropertiesModel() { 9 | this.setRecoverableDays(null); 10 | this.setRecoveryLevel(null); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/exception/NotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.exception; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | @ResponseStatus(HttpStatus.NOT_FOUND) 7 | public class NotFoundException 8 | extends RuntimeException { 9 | 10 | public NotFoundException(final String message) { 11 | super(message); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | labels: 4 | - ignore-for-release 5 | categories: 6 | - title: New features 7 | labels: 8 | - enhancement 9 | - title: Bugs fixed 10 | labels: 11 | - bug 12 | - title: Maintenance 13 | labels: 14 | - documentation 15 | - chore 16 | - release 17 | - title: Dependency updates 18 | labels: 19 | - dependency 20 | - title: Other changes 21 | labels: 22 | - "*" 23 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/exception/AlreadyExistsException.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.exception; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | @ResponseStatus(HttpStatus.CONFLICT) 7 | public class AlreadyExistsException 8 | extends RuntimeException { 9 | 10 | public AlreadyExistsException(final String message) { 11 | super(message); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/key/rotation/invalid-rotation-policy-missing-attributes.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https:/localhost:8443/keys/key-name/rotationpolicy", 3 | "lifetimeActions": [ 4 | { 5 | "trigger": { 6 | "timeAfterCreate": "P90D" 7 | }, 8 | "action": { 9 | "type": "rotate" 10 | } 11 | }, 12 | { 13 | "trigger": { 14 | "timeBeforeExpiry": "P30D" 15 | }, 16 | "action": { 17 | "type": "notify" 18 | } 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/certificate/UpdateCertificateRequest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.certificate; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.Data; 5 | 6 | import java.util.Map; 7 | 8 | @Data 9 | public class UpdateCertificateRequest { 10 | 11 | @JsonProperty("attributes") 12 | private CertificatePropertiesModel attributes; 13 | @JsonProperty("tags") 14 | private Map tags; 15 | } 16 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/controller/PingController.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.controller; 2 | 3 | import org.springframework.http.ResponseEntity; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | @RestController 8 | public class PingController { 9 | 10 | @GetMapping(value = {"/ping"}) 11 | public ResponseEntity ping() { 12 | return ResponseEntity.ok("pong"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/RandomBytesRequest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.key; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import jakarta.validation.constraints.Min; 6 | import lombok.Data; 7 | 8 | @Data 9 | @JsonInclude(JsonInclude.Include.NON_NULL) 10 | public class RandomBytesRequest { 11 | 12 | @JsonProperty("count") 13 | @Min(1) 14 | private int count; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/exception/CryptoException.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.exception; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 7 | public class CryptoException 8 | extends RuntimeException { 9 | 10 | public CryptoException(final String message, final Throwable cause) { 11 | super(message, cause); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n%ex{3} 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/template/no-placeholder.json: -------------------------------------------------------------------------------- 1 | { 2 | "vaults": [ 3 | { 4 | "baseUri": "https://vault-a.localhost:8443/", 5 | "recoveryLevel": "CustomizedRecoverable+Purgeable", 6 | "recoverableDays": 42, 7 | "created": 10001, 8 | "deleted": null 9 | }, 10 | { 11 | "baseUri": "https://vault-b.localhost:8443/", 12 | "recoveryLevel": "CustomizedRecoverable", 13 | "recoverableDays": 10, 14 | "created": 9900, 15 | "deleted": 10200 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/ReadOnlyRsaKeyVaultKeyEntity.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.key; 2 | 3 | public interface ReadOnlyRsaKeyVaultKeyEntity 4 | extends ReadOnlyAsymmetricKeyVaultKeyEntity { 5 | 6 | byte[] getN(); 7 | 8 | byte[] getE(); 9 | 10 | int getKeySize(); 11 | 12 | byte[] getD(); 13 | 14 | byte[] getDp(); 15 | 16 | byte[] getDq(); 17 | 18 | byte[] getP(); 19 | 20 | byte[] getQ(); 21 | 22 | byte[] getQi(); 23 | } 24 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/template/host-placeholder-input.json.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "vaults": [ 3 | { 4 | "baseUri": "https://vault-a.{{host}}:8443/", 5 | "recoveryLevel": "CustomizedRecoverable+Purgeable", 6 | "recoverableDays": 42, 7 | "created": 10001, 8 | "deleted": null 9 | }, 10 | { 11 | "baseUri": "https://vault-b.{{host}}:8443/", 12 | "recoveryLevel": "CustomizedRecoverable", 13 | "recoverableDays": 10, 14 | "created": 9900, 15 | "deleted": 10200 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/common/DeletedModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.common; 2 | 3 | public interface DeletedModel { 4 | 5 | String getRecoveryId(); 6 | 7 | java.time.OffsetDateTime getDeletedDate(); 8 | 9 | java.time.OffsetDateTime getScheduledPurgeDate(); 10 | 11 | void setRecoveryId(String recoveryId); 12 | 13 | void setDeletedDate(java.time.OffsetDateTime deletedDate); 14 | 15 | void setScheduledPurgeDate(java.time.OffsetDateTime scheduledPurgeDate); 16 | } 17 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/template/port-placeholder-input.json.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "vaults": [ 3 | { 4 | "baseUri": "https://vault-a.localhost:{{port}}/", 5 | "recoveryLevel": "CustomizedRecoverable+Purgeable", 6 | "recoverableDays": 42, 7 | "created": 10001, 8 | "deleted": null 9 | }, 10 | { 11 | "baseUri": "https://vault-b.localhost:{{port}}/", 12 | "recoveryLevel": "CustomizedRecoverable", 13 | "recoverableDays": 10, 14 | "created": 9900, 15 | "deleted": 10200 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/secret/ReadOnlyKeyVaultSecretEntity.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.secret; 2 | 3 | import com.github.nagyesta.lowkeyvault.service.common.BaseVaultEntity; 4 | import com.github.nagyesta.lowkeyvault.service.secret.id.VersionedSecretEntityId; 5 | 6 | public interface ReadOnlyKeyVaultSecretEntity 7 | extends BaseVaultEntity { 8 | 9 | String getValue(); 10 | 11 | String getContentType(); 12 | 13 | VersionedSecretEntityId getId(); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/template/all-placeholder-input.json.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "vaults": [ 3 | { 4 | "baseUri": "https://vault-a.{{host}}:{{port}}/", 5 | "recoveryLevel": "CustomizedRecoverable+Purgeable", 6 | "recoverableDays": 42, 7 | "created": {{now 1}}, 8 | "deleted": null 9 | }, 10 | { 11 | "baseUri": "https://vault-b.{{host}}:{{port}}/", 12 | "recoveryLevel": "CustomizedRecoverable", 13 | "recoverableDays": 10, 14 | "created": {{now -100}}, 15 | "deleted": {{now 200}} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/template/time-placeholder-input.json.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "vaults": [ 3 | { 4 | "baseUri": "https://vault-a.localhost:8443/", 5 | "recoveryLevel": "CustomizedRecoverable+Purgeable", 6 | "recoverableDays": 42, 7 | "created": {{now 1001}}, 8 | "deleted": null 9 | }, 10 | { 11 | "baseUri": "https://vault-b.localhost:8443/", 12 | "recoveryLevel": "CustomizedRecoverable", 13 | "recoverableDays": 10, 14 | "created": {{now 900}}, 15 | "deleted": {{now 1200}} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /lowkey-vault-docker/src/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=${BUILDPLATFORM} eclipse-temurin:25.0.1_8-jre-alpine-3.22@sha256:b51543f89580c1ba70e441cfbc0cfc1635c3c16d2e2d77fec9d890342a3a8687 2 | LABEL maintainer="nagyesta@gmail.com" 3 | EXPOSE 8080 8443 4 | COPY lowkey-vault.jar /lowkey-vault.jar 5 | RUN \ 6 | chmod 555 "/lowkey-vault.jar" && \ 7 | mkdir "/import" && \ 8 | chmod 755 "/import" && \ 9 | mkdir "/config" && \ 10 | chmod 555 "/config" 11 | WORKDIR / 12 | CMD [ "sh", "-c", "ls /" ] 13 | ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /lowkey-vault.jar ${LOWKEY_ARGS}"] 14 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/KeyVerifyResult.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.key; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | @Data 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | @JsonInclude(JsonInclude.Include.NON_NULL) 13 | public class KeyVerifyResult { 14 | 15 | @JsonProperty("value") 16 | private boolean value; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/LifetimeActionTrigger.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.key; 2 | 3 | import com.github.nagyesta.lowkeyvault.service.key.constants.LifetimeActionTriggerType; 4 | 5 | import java.time.OffsetDateTime; 6 | import java.time.Period; 7 | 8 | public interface LifetimeActionTrigger { 9 | 10 | Period timePeriod(); 11 | 12 | LifetimeActionTriggerType triggerType(); 13 | 14 | boolean shouldTrigger(OffsetDateTime created, OffsetDateTime expiry); 15 | 16 | long rotateAfterDays(Period expiryPeriod); 17 | } 18 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/certificate/GenerateCertificateRequest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.certificate; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.Data; 5 | 6 | import java.util.Map; 7 | 8 | @Data 9 | public class GenerateCertificateRequest { 10 | 11 | @JsonProperty("attributes") 12 | private CertificatePropertiesModel attributes; 13 | @JsonProperty("policy") 14 | private CertificatePolicyModel policy; 15 | @JsonProperty("tags") 16 | private Map tags; 17 | } 18 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/key/rotation/invalid-rotation-policy-missing-created.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https:/localhost:8443/keys/key-name/rotationpolicy", 3 | "lifetimeActions": [ 4 | { 5 | "trigger": { 6 | "timeAfterCreate": "P90D" 7 | }, 8 | "action": { 9 | "type": "rotate" 10 | } 11 | }, 12 | { 13 | "trigger": { 14 | "timeBeforeExpiry": "P30D" 15 | }, 16 | "action": { 17 | "type": "notify" 18 | } 19 | } 20 | ], 21 | "attributes": { 22 | "expiryTime": "P4M", 23 | "updated": 1482188948 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/key/rotation/invalid-rotation-policy-missing-updated.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https:/localhost:8443/keys/key-name/rotationpolicy", 3 | "lifetimeActions": [ 4 | { 5 | "trigger": { 6 | "timeAfterCreate": "P90D" 7 | }, 8 | "action": { 9 | "type": "rotate" 10 | } 11 | }, 12 | { 13 | "trigger": { 14 | "timeBeforeExpiry": "P30D" 15 | }, 16 | "action": { 17 | "type": "notify" 18 | } 19 | } 20 | ], 21 | "attributes": { 22 | "expiryTime": "P4M", 23 | "created": 1482188947 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/key/rotation/invalid-rotation-policy-missing-expiry.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https:/localhost:8443/keys/key-name/rotationpolicy", 3 | "lifetimeActions": [ 4 | { 5 | "trigger": { 6 | "timeAfterCreate": "P90D" 7 | }, 8 | "action": { 9 | "type": "rotate" 10 | } 11 | }, 12 | { 13 | "trigger": { 14 | "timeBeforeExpiry": "P30D" 15 | }, 16 | "action": { 17 | "type": "notify" 18 | } 19 | } 20 | ], 21 | "attributes": { 22 | "created": 1482188947, 23 | "updated": 1482188948 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/key/rotation/valid-rotation-policy-full.json: -------------------------------------------------------------------------------- 1 | { 2 | "attributes": { 3 | "created": 1482188947, 4 | "expiryTime": "P4M", 5 | "updated": 1482188948 6 | }, 7 | "id": "https:/localhost:8443/keys/key-name/rotationpolicy", 8 | "lifetimeActions": [ 9 | { 10 | "action": { 11 | "type": "rotate" 12 | }, 13 | "trigger": { 14 | "timeAfterCreate": "P90D" 15 | } 16 | }, 17 | { 18 | "action": { 19 | "type": "notify" 20 | }, 21 | "trigger": { 22 | "timeBeforeExpiry": "P30D" 23 | } 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/key/rotation/invalid-rotation-policy-null-action-type.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "https:/localhost:8443/keys/key-name/rotationpolicy", 3 | "lifetimeActions": [ 4 | { 5 | "trigger": { 6 | "timeAfterCreate": "P90D" 7 | }, 8 | "action": { 9 | "type": null 10 | } 11 | }, 12 | { 13 | "trigger": { 14 | "timeBeforeExpiry": "P30D" 15 | }, 16 | "action": { 17 | "type": "notify" 18 | } 19 | } 20 | ], 21 | "attributes": { 22 | "expiryTime": "P4M", 23 | "created": 1482188947, 24 | "updated": 1482188948 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyCreationInput.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.key.impl; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyType; 4 | import lombok.Data; 5 | import lombok.NonNull; 6 | 7 | @Data 8 | public class KeyCreationInput { 9 | 10 | private KeyType keyType; 11 | 12 | private T keyParameter; 13 | 14 | public KeyCreationInput( 15 | @NonNull final KeyType keyType, 16 | final T keyParameter) { 17 | this.keyType = keyType; 18 | this.keyParameter = keyParameter; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/ReadOnlyDeletedEntity.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.key; 2 | 3 | import com.github.nagyesta.lowkeyvault.service.EntityId; 4 | 5 | import java.time.OffsetDateTime; 6 | import java.util.Optional; 7 | 8 | /** 9 | * Base interface of deleted vault entities. 10 | * 11 | * @param The type of the versioned ID identifying this entity. 12 | */ 13 | public interface ReadOnlyDeletedEntity { 14 | 15 | V getId(); 16 | 17 | Optional getDeletedDate(); 18 | 19 | Optional getScheduledPurgeDate(); 20 | } 21 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyCreationInputTest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.key.impl; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | class KeyCreationInputTest { 7 | 8 | @Test 9 | void testConstructorShouldThrowExceptionsWhenCalledWithNullKeyType() { 10 | //given 11 | 12 | //when 13 | //noinspection ConstantConditions 14 | Assertions.assertThrows(IllegalArgumentException.class, 15 | () -> new KeyCreationInput<>(null, null)); 16 | 17 | //then exception 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /pull_request_template.md: -------------------------------------------------------------------------------- 1 | **Issue:** N/A 2 | 3 | ### Description 4 | 5 | 6 | 7 | - 8 | 9 | ### Entry point 10 | 11 | 12 | 13 | - 14 | 15 | ### Checklists 16 | 17 | - [x] I have rebased my branch 18 | - [x] My commit message is meaningful 19 | - [ ] The commit messages use semantic versioning: `{major}`, `{minor}` or `{patch}` 20 | - [ ] The changes are focusing on the issue at hand 21 | - [ ] I have maintained or increased test coverage 22 | 23 | ### Notes 24 | 25 | - 26 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/secret/request/UpdateSecretRequest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.secret.request; 2 | 3 | 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.github.nagyesta.lowkeyvault.model.v7_2.BasePropertiesUpdateModel; 6 | import lombok.Data; 7 | 8 | import java.util.Map; 9 | 10 | @Data 11 | public class UpdateSecretRequest { 12 | 13 | @JsonProperty("contentType") 14 | private String contentType; 15 | @JsonProperty("attributes") 16 | private BasePropertiesUpdateModel properties; 17 | @JsonProperty("tags") 18 | private Map tags; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/certificate/SubjectAlternativeNames.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.certificate; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | 6 | import java.util.Set; 7 | 8 | public record SubjectAlternativeNames( 9 | @JsonProperty("dns_names") Set dnsNames, 10 | @JsonProperty("emails") Set emails, 11 | @JsonProperty("upns") Set upns) { 12 | 13 | @JsonCreator 14 | @SuppressWarnings({"java:S1186", "java:S6207"}) //default JSON creator 15 | public SubjectAlternativeNames { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/BaseVaultConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.controller; 2 | 3 | import com.github.nagyesta.lowkeyvault.service.vault.VaultService; 4 | import com.github.nagyesta.lowkeyvault.service.vault.impl.VaultServiceImpl; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.context.annotation.Profile; 8 | 9 | @Profile("vault") 10 | @Configuration 11 | public class BaseVaultConfiguration { 12 | 13 | @Bean 14 | public VaultService vaultService() { 15 | return new VaultServiceImpl(uri -> uri); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/EntityId.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service; 2 | 3 | import java.net.URI; 4 | import java.util.Optional; 5 | 6 | public interface EntityId { 7 | 8 | String entityType(); 9 | 10 | URI vault(); 11 | 12 | String id(); 13 | 14 | String version(); 15 | 16 | default String asString() { 17 | return entityType() + ":" + vault() + "/" + id() + "/" + Optional.ofNullable(version()).orElse("--"); 18 | } 19 | 20 | URI asUriNoVersion(URI vaultUri); 21 | 22 | URI asUri(URI vaultUri); 23 | 24 | URI asRecoveryUri(URI vaultUri); 25 | 26 | URI asUri(URI vaultUri, String query); 27 | } 28 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/certificate/LifetimeActionPolicy.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.certificate; 2 | 3 | import com.github.nagyesta.lowkeyvault.service.common.TimeAware; 4 | import lombok.NonNull; 5 | 6 | import java.time.OffsetDateTime; 7 | import java.util.Map; 8 | 9 | public interface LifetimeActionPolicy 10 | extends ReadOnlyLifetimeActionPolicy, TimeAware { 11 | 12 | void setCreatedOn(@NonNull OffsetDateTime createdOn); 13 | 14 | void setUpdatedOn(@NonNull OffsetDateTime updatedOn); 15 | 16 | void setLifetimeActions(Map lifetimeActions); 17 | } 18 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/mapper/v7_3/certificate/CertificateEntityToV73ModelConverterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.mapper.v7_3.certificate; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | class CertificateEntityToV73ModelConverterTest { 7 | 8 | @SuppressWarnings("DataFlowIssue") 9 | @Test 10 | void testConstructorShouldThrowExceptionWhenCalledWithNull() { 11 | //given 12 | 13 | //when 14 | Assertions.assertThrows(IllegalArgumentException.class, 15 | () -> new CertificateEntityToV73ModelConverter(null)); 16 | 17 | //then + exceptions 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/management/VaultBackupListModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.management; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import jakarta.validation.Valid; 6 | import jakarta.validation.constraints.NotNull; 7 | import jakarta.validation.constraints.Size; 8 | import lombok.Data; 9 | 10 | import java.util.List; 11 | 12 | @Data 13 | @JsonInclude(JsonInclude.Include.NON_NULL) 14 | public class VaultBackupListModel { 15 | 16 | @Valid 17 | @NotNull 18 | @Size(min = 1) 19 | @JsonProperty("vaults") 20 | private List vaults; 21 | } 22 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/mapper/v7_3/certificate/CertificateEntityToV73PropertiesModelConverterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.mapper.v7_3.certificate; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | class CertificateEntityToV73PropertiesModelConverterTest { 7 | 8 | @SuppressWarnings("DataFlowIssue") 9 | @Test 10 | void testConstructorShouldThrowExceptionWhenCalledWithNull() { 11 | //given 12 | 13 | //when 14 | Assertions.assertThrows(IllegalArgumentException.class, () -> new CertificateEntityToV73PropertiesModelConverter(null)); 15 | 16 | //then + exception 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lowkey-vault-client/src/main/java/com/github/nagyesta/lowkeyvault/http/ContentLengthHeaderRemover.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.http; 2 | 3 | import org.apache.http.HttpRequestInterceptor; 4 | import org.apache.http.protocol.HTTP; 5 | import org.apache.http.protocol.HttpContext; 6 | 7 | class ContentLengthHeaderRemover 8 | implements HttpRequestInterceptor { 9 | 10 | @Override 11 | public void process( 12 | final org.apache.http.HttpRequest request, 13 | final HttpContext context) { 14 | request.removeHeaders(HTTP.CONTENT_LEN); 15 | // fighting org.apache.http.protocol.RequestContent's ProtocolException("Content-Length header already present") 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/validator/ExpiryPeriod.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.key.validator; 2 | 3 | import jakarta.validation.Constraint; 4 | import jakarta.validation.Payload; 5 | 6 | import java.lang.annotation.Documented; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | 10 | @Retention(RetentionPolicy.RUNTIME) 11 | @Documented 12 | @Constraint(validatedBy = ExpiryPeriodValidator.class) 13 | public @interface ExpiryPeriod { 14 | 15 | String message() default "Expiry period is invalid."; 16 | 17 | Class[] groups() default { }; 18 | 19 | Class[] payload() default { }; 20 | } 21 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/mapper/v7_3/certificate/CertificateEntityToV73IssuancePolicyModelConverterTest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.mapper.v7_3.certificate; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | class CertificateEntityToV73IssuancePolicyModelConverterTest { 7 | 8 | @SuppressWarnings("DataFlowIssue") 9 | @Test 10 | void testConstructorShouldThrowExceptionWhenCalledWithNull() { 11 | //given 12 | 13 | //when 14 | Assertions.assertThrows(IllegalArgumentException.class, () -> new CertificateEntityToV73IssuancePolicyModelConverter(null)); 15 | 16 | //then + exception 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/cert/ec.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCAFlIRPFr7jjJA+9DAf 3 | pWb/gTypdclusJCFTTBK0qc8Nw== 4 | -----END PRIVATE KEY----- 5 | -----BEGIN CERTIFICATE----- 6 | MIIBRDCB6aADAgECAgR3UZEbMAwGCCqGSM49BAMCBQAwFzEVMBMGA1UEAxMMZWMu 7 | bG9jYWxob3N0MB4XDTIyMDkxMDE5MDA1NVoXDTIzMDkxMDE5MDA1NVowFzEVMBMG 8 | A1UEAxMMZWMubG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9QeN 9 | Y5gGwMQnCSUrFfJ1CQp8ngrTJn9ZzTusUY8Gh5JWennjFdzLIqJ4yhpSzGAl+/jn 10 | Gv3n+fBjt7mUZu2I76MhMB8wHQYDVR0OBBYEFA2YDS/W2/Dv5qJrmtbE7w+HUtL0 11 | MAwGCCqGSM49BAMCBQADSAAwRQIgMQAYrmTDkcxQgS33oHbw+H/7YEO43ZDqSOTr 12 | tn7PQa8CIQCf8JCfvoC0W67JsBRFPDNJEKBuNHVMOWuKjwrXaqynuQ== 13 | -----END CERTIFICATE----- 14 | -------------------------------------------------------------------------------- /lowkey-vault-testcontainers/src/main/java/com/github/nagyesta/lowkeyvault/testcontainers/ContainerDependency.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.testcontainers; 2 | 3 | import org.testcontainers.lifecycle.Startable; 4 | 5 | import java.util.Collections; 6 | import java.util.Map; 7 | import java.util.function.Function; 8 | 9 | public record ContainerDependency( 10 | T container, 11 | Function> secretSupplier) { 12 | 13 | ContainerDependency(final T container) { 14 | this(container, c -> Collections.emptyMap()); 15 | } 16 | 17 | public Map getSecrets() { 18 | return secretSupplier.apply(container); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipKeySerializer.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.json.util; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.common.backup.KeyBackupList; 4 | import tools.jackson.databind.ObjectMapper; 5 | 6 | public class Base64ZipKeySerializer 7 | extends AbstractBase64ZipSerializer { 8 | 9 | public Base64ZipKeySerializer() { 10 | this(new Base64Serializer(), new ObjectMapper()); 11 | } 12 | 13 | protected Base64ZipKeySerializer( 14 | final Base64Serializer base64Serializer, 15 | final ObjectMapper objectMapper) { 16 | super(base64Serializer, objectMapper); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/openapi/ExamplesTest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.openapi; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.lang.reflect.InvocationTargetException; 7 | 8 | class ExamplesTest { 9 | 10 | @Test 11 | void testConstructorShouldThrowExceptionWhenCalled() throws NoSuchMethodException { 12 | //given 13 | final var constructor = Examples.class.getDeclaredConstructor(); 14 | constructor.setAccessible(true); 15 | 16 | //when 17 | Assertions.assertThrows(InvocationTargetException.class, constructor::newInstance); 18 | 19 | //then + exception 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lowkey-vault-docker/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/KeyVaultKeyItemModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.key; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import lombok.Data; 6 | 7 | import java.util.Map; 8 | 9 | @Data 10 | @JsonInclude(JsonInclude.Include.NON_NULL) 11 | public class KeyVaultKeyItemModel { 12 | 13 | @JsonProperty("kid") 14 | private String keyId; 15 | 16 | @JsonProperty("attributes") 17 | private KeyPropertiesModel attributes; 18 | 19 | @JsonProperty("tags") 20 | private Map tags; 21 | 22 | @JsonProperty("managed") 23 | private Boolean managed; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/KeyVaultKeyModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.key; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import lombok.Data; 6 | 7 | import java.util.Map; 8 | 9 | @Data 10 | @JsonInclude(JsonInclude.Include.NON_NULL) 11 | public class KeyVaultKeyModel { 12 | 13 | @JsonProperty("key") 14 | private JsonWebKeyModel key; 15 | 16 | @JsonProperty("attributes") 17 | private KeyPropertiesModel attributes; 18 | 19 | @JsonProperty("tags") 20 | private Map tags; 21 | 22 | @JsonProperty("managed") 23 | private Boolean managed; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/validator/ValidImportKey.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.key.validator; 2 | 3 | import jakarta.validation.Constraint; 4 | import jakarta.validation.Payload; 5 | 6 | import java.lang.annotation.Documented; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | 10 | @Retention(RetentionPolicy.RUNTIME) 11 | @Documented 12 | @Constraint(validatedBy = ImportKeyValidator.class) 13 | public @interface ValidImportKey { 14 | 15 | String message() default "Json Web Key import request is invalid."; 16 | 17 | Class[] groups() default { }; 18 | 19 | Class[] payload() default { }; 20 | } 21 | -------------------------------------------------------------------------------- /lowkey-vault-testcontainers/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/common/ApiConstantsTest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.common; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.lang.reflect.InvocationTargetException; 7 | 8 | class ApiConstantsTest { 9 | 10 | @Test 11 | void testConstructorShouldThrowExceptionWhenCalled() throws NoSuchMethodException { 12 | //given 13 | final var constructor = ApiConstants.class.getDeclaredConstructor(); 14 | constructor.setAccessible(true); 15 | 16 | //when 17 | Assertions.assertThrows(InvocationTargetException.class, constructor::newInstance); 18 | 19 | //then + exception 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/certificate/CertificateLifetimeActionTrigger.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.certificate; 2 | 3 | import lombok.NonNull; 4 | 5 | import java.time.OffsetDateTime; 6 | 7 | public record CertificateLifetimeActionTrigger( 8 | @NonNull CertificateLifetimeActionTriggerType triggerType, 9 | int value) { 10 | 11 | public void validate(final int validityMonths) { 12 | triggerType.validate(validityMonths, value); 13 | } 14 | 15 | public long triggersAfterDays( 16 | final OffsetDateTime validityStart, 17 | final OffsetDateTime expiry) { 18 | return triggerType.triggersAfterDays(validityStart, expiry, value); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: It works fine but something is missing to make it better. Please tell us more! 4 | title: "" 5 | labels: enhancement 6 | assignees: "" 7 | --- 8 | 9 | #### Is your feature request related to a problem? Please describe 10 | 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | #### Describe the solution you'd like 14 | 15 | A clear and concise description of what you want to happen. 16 | 17 | #### Describe alternatives you've considered 18 | 19 | A clear and concise description of any alternative solutions or features you've considered. 20 | 21 | #### Additional context 22 | 23 | Add any other context or screenshots about the feature request here. 24 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/request/UpdateKeyRequest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.key.request; 2 | 3 | 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.github.nagyesta.lowkeyvault.model.v7_2.BasePropertiesUpdateModel; 6 | import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyOperation; 7 | import lombok.Data; 8 | 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | @Data 13 | public class UpdateKeyRequest { 14 | 15 | @JsonProperty("key_ops") 16 | private List keyOperations; 17 | 18 | @JsonProperty("attributes") 19 | private BasePropertiesUpdateModel properties; 20 | 21 | @JsonProperty("tags") 22 | private Map tags; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/secret/id/SecretEntityId.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.secret.id; 2 | 3 | import com.github.nagyesta.lowkeyvault.service.BaseEntityId; 4 | import com.github.nagyesta.lowkeyvault.service.EntityId; 5 | import lombok.NonNull; 6 | 7 | import java.net.URI; 8 | 9 | public class SecretEntityId 10 | extends BaseEntityId implements EntityId { 11 | 12 | public SecretEntityId( 13 | final URI vault, 14 | final String id) { 15 | this(vault, id, null); 16 | } 17 | 18 | public SecretEntityId( 19 | @NonNull final URI vault, 20 | @NonNull final String id, 21 | final String version) { 22 | super(vault, id, version, "secret"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipSecretSerializer.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.json.util; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.common.backup.SecretBackupList; 4 | import tools.jackson.databind.ObjectMapper; 5 | 6 | public class Base64ZipSecretSerializer 7 | extends AbstractBase64ZipSerializer { 8 | 9 | @SuppressWarnings("unused") //used from annotations 10 | public Base64ZipSecretSerializer() { 11 | this(new Base64Serializer(), new ObjectMapper()); 12 | } 13 | 14 | protected Base64ZipSecretSerializer( 15 | final Base64Serializer base64Serializer, 16 | final ObjectMapper objectMapper) { 17 | super(base64Serializer, objectMapper); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/secret/KeyVaultSecretItemModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.secret; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import lombok.Data; 6 | 7 | import java.util.Map; 8 | 9 | @Data 10 | @JsonInclude(JsonInclude.Include.NON_NULL) 11 | public class KeyVaultSecretItemModel { 12 | 13 | @JsonProperty("id") 14 | private String id; 15 | @JsonProperty("contentType") 16 | private String contentType; 17 | @JsonProperty("attributes") 18 | private SecretPropertiesModel attributes; 19 | @JsonProperty("tags") 20 | private Map tags; 21 | @JsonProperty("managed") 22 | private Boolean managed; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/util/PeriodUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.key.util; 2 | 3 | import lombok.NonNull; 4 | 5 | import java.time.OffsetDateTime; 6 | import java.time.Period; 7 | import java.time.temporal.ChronoUnit; 8 | 9 | public final class PeriodUtil { 10 | 11 | private PeriodUtil() { 12 | throw new IllegalCallerException("Utility cannot be instantiated."); 13 | } 14 | 15 | public static long asDays(@NonNull final Period period) { 16 | return asDays(period, OffsetDateTime.now()); 17 | } 18 | 19 | static long asDays( 20 | final Period period, 21 | final OffsetDateTime relativeTo) { 22 | return ChronoUnit.DAYS.between(relativeTo, relativeTo.plus(period)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/common/util/CertificateRequestMapperUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.controller.common.util; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.lang.reflect.InvocationTargetException; 7 | 8 | class CertificateRequestMapperUtilTest { 9 | 10 | @Test 11 | void testConstructorShouldThrowExceptionWhenCalled() throws NoSuchMethodException { 12 | //given 13 | final var constructor = CertificateRequestMapperUtil.class.getDeclaredConstructor(); 14 | constructor.setAccessible(true); 15 | 16 | //when 17 | Assertions.assertThrows(InvocationTargetException.class, constructor::newInstance); 18 | 19 | //then + exception 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/id/VersionedKeyEntityId.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.key.id; 2 | 3 | import com.github.nagyesta.lowkeyvault.service.BaseEntityId; 4 | import org.springframework.lang.NonNull; 5 | import org.springframework.util.Assert; 6 | 7 | import java.net.URI; 8 | 9 | public class VersionedKeyEntityId 10 | extends KeyEntityId { 11 | 12 | public VersionedKeyEntityId(@NonNull final URI vault, @NonNull final String id) { 13 | this(vault, id, BaseEntityId.generateVersion()); 14 | } 15 | 16 | public VersionedKeyEntityId(@NonNull final URI vault, @NonNull final String id, @NonNull final String version) { 17 | super(vault, id, version); 18 | Assert.notNull(version, "Version must not be null."); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/RotationPolicy.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.key; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.v7_3.key.constants.LifetimeActionType; 4 | import com.github.nagyesta.lowkeyvault.service.common.TimeAware; 5 | 6 | import java.time.OffsetDateTime; 7 | import java.time.Period; 8 | import java.util.Map; 9 | 10 | public interface RotationPolicy 11 | extends ReadOnlyRotationPolicy, TimeAware { 12 | 13 | void setLifetimeActions(Map lifetimeActions); 14 | 15 | void setCreatedOn(OffsetDateTime createdOn); 16 | 17 | void setUpdatedOn(OffsetDateTime updatedOn); 18 | 19 | void setExpiryTime(Period expiryTime); 20 | 21 | void validate(OffsetDateTime latestKeyVersionExpiry); 22 | } 23 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/mapper/common/AliasAwareConverter.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.mapper.common; 2 | 3 | import com.github.nagyesta.lowkeyvault.context.ApiVersionAware; 4 | import org.springframework.beans.factory.InitializingBean; 5 | import org.springframework.lang.NonNull; 6 | import org.springframework.lang.Nullable; 7 | 8 | import java.net.URI; 9 | 10 | /** 11 | * A converter that converts a source object of type {@code S} to a target of type {@code T} 12 | * while replacing vault base URI references in Ids. 13 | * 14 | * @param the source type 15 | * @param the target type 16 | */ 17 | public interface AliasAwareConverter 18 | extends ApiVersionAware, InitializingBean { 19 | 20 | @Nullable 21 | T convert(S source, @NonNull URI vaultUri); 22 | } 23 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipCertificateSerializer.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.json.util; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.common.backup.CertificateBackupList; 4 | import tools.jackson.databind.ObjectMapper; 5 | 6 | public class Base64ZipCertificateSerializer 7 | extends AbstractBase64ZipSerializer { 8 | 9 | @SuppressWarnings("unused") //used from annotations 10 | public Base64ZipCertificateSerializer() { 11 | this(new Base64Serializer(), new ObjectMapper()); 12 | } 13 | 14 | protected Base64ZipCertificateSerializer( 15 | final Base64Serializer base64Serializer, 16 | final ObjectMapper objectMapper) { 17 | super(base64Serializer, objectMapper); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/secret/SecretVaultFake.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.secret; 2 | 3 | import com.github.nagyesta.lowkeyvault.service.common.BaseVaultFake; 4 | import com.github.nagyesta.lowkeyvault.service.secret.id.SecretEntityId; 5 | import com.github.nagyesta.lowkeyvault.service.secret.id.VersionedSecretEntityId; 6 | import com.github.nagyesta.lowkeyvault.service.secret.impl.SecretCreateInput; 7 | 8 | public interface SecretVaultFake 9 | extends BaseVaultFake { 10 | 11 | VersionedSecretEntityId createSecretVersion(String secretName, SecretCreateInput input); 12 | 13 | VersionedSecretEntityId createSecretVersion(VersionedSecretEntityId entityId, SecretCreateInput input); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/certificate/CreateCertificateRequest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.certificate; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import jakarta.validation.Valid; 6 | import jakarta.validation.constraints.NotNull; 7 | import lombok.Data; 8 | 9 | import java.util.Map; 10 | 11 | @Data 12 | @JsonInclude(JsonInclude.Include.NON_NULL) 13 | public class CreateCertificateRequest { 14 | 15 | @JsonProperty("attributes") 16 | @Valid 17 | private CertificatePropertiesModel properties; 18 | @JsonProperty("policy") 19 | @NotNull 20 | @Valid 21 | private CertificatePolicyModel policy; 22 | @JsonProperty("tags") 23 | private Map tags; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/controller/common/PingControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.controller.common; 2 | 3 | import com.github.nagyesta.lowkeyvault.controller.PingController; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.http.HttpStatus; 7 | 8 | class PingControllerTest { 9 | 10 | @Test 11 | void testPingShouldReturnPongWhenCalled() { 12 | //given 13 | final var underTest = new PingController(); 14 | 15 | //when 16 | final var actual = underTest.ping(); 17 | 18 | //then 19 | Assertions.assertNotNull(actual); 20 | Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); 21 | Assertions.assertEquals("pong", actual.getBody()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/ReadOnlyRotationPolicy.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.key; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.v7_3.key.constants.LifetimeActionType; 4 | import com.github.nagyesta.lowkeyvault.service.key.id.KeyEntityId; 5 | 6 | import java.time.OffsetDateTime; 7 | import java.time.Period; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | public interface ReadOnlyRotationPolicy { 12 | 13 | KeyEntityId getId(); 14 | 15 | OffsetDateTime getCreatedOn(); 16 | 17 | OffsetDateTime getUpdatedOn(); 18 | 19 | Period getExpiryTime(); 20 | 21 | Map getLifetimeActions(); 22 | 23 | boolean isAutoRotate(); 24 | 25 | List missedRotations(OffsetDateTime keyCreation); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/OctKeyCreationInput.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.key.impl; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyType; 4 | import lombok.EqualsAndHashCode; 5 | import lombok.Getter; 6 | import lombok.ToString; 7 | import org.springframework.lang.NonNull; 8 | import org.springframework.util.Assert; 9 | 10 | @Getter 11 | @EqualsAndHashCode(callSuper = true) 12 | @ToString(callSuper = true) 13 | public class OctKeyCreationInput 14 | extends KeyCreationInput { 15 | 16 | public OctKeyCreationInput( 17 | @NonNull final KeyType keyType, 18 | final Integer keyParameter) { 19 | super(keyType, keyParameter); 20 | Assert.isTrue(keyType.isOct(), "KeyType must be OCT."); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/certificate/impl/DefaultCertificateLifetimeActionPolicyTest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.certificate.impl; 2 | 3 | import com.github.nagyesta.lowkeyvault.TestConstantsCertificates; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.Test; 6 | 7 | class DefaultCertificateLifetimeActionPolicyTest { 8 | 9 | @SuppressWarnings("DataFlowIssue") 10 | @Test 11 | void testConstructorShouldThrowExceptionWhenCalledWithNullIssuer() { 12 | //given 13 | 14 | //when 15 | Assertions.assertThrows(IllegalArgumentException.class, () -> new DefaultCertificateLifetimeActionPolicy( 16 | TestConstantsCertificates.VERSIONED_CERT_ENTITY_ID_1_VERSION_1, null)); 17 | 18 | //then + exception 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/certificate/id/VersionedCertificateEntityId.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.certificate.id; 2 | 3 | import com.github.nagyesta.lowkeyvault.service.BaseEntityId; 4 | import org.springframework.lang.NonNull; 5 | import org.springframework.util.Assert; 6 | 7 | import java.net.URI; 8 | 9 | public class VersionedCertificateEntityId extends CertificateEntityId { 10 | 11 | public VersionedCertificateEntityId(@NonNull final URI vault, @NonNull final String id) { 12 | this(vault, id, BaseEntityId.generateVersion()); 13 | } 14 | 15 | public VersionedCertificateEntityId(@NonNull final URI vault, @NonNull final String id, @NonNull final String version) { 16 | super(vault, id, version); 17 | Assert.notNull(version, "Version must not be null."); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/service/key/id/KeyEntityIdTest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.key.id; 2 | 3 | import com.github.nagyesta.lowkeyvault.TestConstantsKeys; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import static com.github.nagyesta.lowkeyvault.TestConstantsUri.HTTPS_LOCALHOST_8443; 8 | 9 | class KeyEntityIdTest { 10 | 11 | @Test 12 | void testAsRotationPolicyUriShouldReturnRotationPolicyUriWhenCalled() { 13 | //given 14 | final var underTest = TestConstantsKeys.UNVERSIONED_KEY_ENTITY_ID_1; 15 | 16 | //when 17 | final var actual = underTest.asRotationPolicyUri(HTTPS_LOCALHOST_8443); 18 | 19 | //then 20 | Assertions.assertEquals(underTest.asUri(HTTPS_LOCALHOST_8443, "rotationpolicy"), actual); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/common/backup/SecretBackupListItem.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.common.backup; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.github.nagyesta.lowkeyvault.model.v7_2.common.BaseBackupListItem; 5 | import com.github.nagyesta.lowkeyvault.model.v7_2.secret.SecretPropertiesModel; 6 | import jakarta.validation.constraints.NotNull; 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | import lombok.ToString; 10 | 11 | @Data 12 | @EqualsAndHashCode(callSuper = true) 13 | @ToString(callSuper = true) 14 | public class SecretBackupListItem 15 | extends BaseBackupListItem { 16 | @NotNull 17 | @JsonProperty("value") 18 | private String value; 19 | @JsonProperty("contentType") 20 | private String contentType; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipKeyDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.json.util; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.common.backup.KeyBackupList; 4 | import tools.jackson.databind.ObjectMapper; 5 | 6 | public class Base64ZipKeyDeserializer 7 | extends AbstractBase64ZipDeserializer { 8 | 9 | public Base64ZipKeyDeserializer() { 10 | this(new Base64Deserializer(), new ObjectMapper()); 11 | } 12 | 13 | protected Base64ZipKeyDeserializer( 14 | final Base64Deserializer base64Deserializer, 15 | final ObjectMapper objectMapper) { 16 | super(base64Deserializer, objectMapper); 17 | } 18 | 19 | @Override 20 | protected Class getType() { 21 | return KeyBackupList.class; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/secret/request/CreateSecretRequest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.secret.request; 2 | 3 | 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.github.nagyesta.lowkeyvault.model.v7_2.secret.SecretPropertiesModel; 6 | import jakarta.validation.constraints.NotBlank; 7 | import jakarta.validation.constraints.NotNull; 8 | import lombok.Data; 9 | 10 | import java.util.Map; 11 | 12 | @Data 13 | public class CreateSecretRequest { 14 | 15 | @JsonProperty("contentType") 16 | private String contentType; 17 | @NotNull 18 | @NotBlank 19 | @JsonProperty("value") 20 | private String value; 21 | @JsonProperty("attributes") 22 | private SecretPropertiesModel properties; 23 | @JsonProperty("tags") 24 | private Map tags = Map.of(); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/mapper/v7_2/key/BaseJsonWebKeyImportRequestConverter.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.mapper.v7_2.key; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.JsonWebKeyImportRequest; 4 | import org.springframework.core.convert.converter.Converter; 5 | 6 | import java.math.BigInteger; 7 | 8 | /** 9 | * Base class for converting import requests to keys or key pairs. 10 | * 11 | * @param return type. 12 | * @param

the input parameter (key size or curve name) 13 | */ 14 | public abstract class BaseJsonWebKeyImportRequestConverter 15 | implements Converter { 16 | 17 | public abstract P getKeyParameter(JsonWebKeyImportRequest request); 18 | 19 | protected BigInteger asInt(final byte[] inBytes) { 20 | return new BigInteger(1, inBytes); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/template/backup/TimeHelperSource.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.template.backup; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import java.time.OffsetDateTime; 6 | import java.time.ZoneOffset; 7 | import java.time.temporal.ChronoUnit; 8 | 9 | @Component 10 | @SuppressWarnings("java:S6829") //cannot add autowired to the default constructor 11 | public class TimeHelperSource { 12 | 13 | private final OffsetDateTime now; 14 | 15 | public TimeHelperSource() { 16 | this(OffsetDateTime.now(ZoneOffset.UTC).truncatedTo(ChronoUnit.SECONDS)); 17 | } 18 | 19 | TimeHelperSource(final OffsetDateTime now) { 20 | this.now = now; 21 | } 22 | 23 | public CharSequence now(final int offset) { 24 | return String.valueOf(now.plusSeconds(offset).toEpochSecond()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /gradle/verification-metadata-clean.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | true 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/request/ImportKeyRequest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.key.request; 2 | 3 | 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.github.nagyesta.lowkeyvault.model.v7_2.BasePropertiesUpdateModel; 6 | import jakarta.validation.Valid; 7 | import jakarta.validation.constraints.NotNull; 8 | import lombok.Data; 9 | 10 | import java.util.Map; 11 | 12 | @SuppressWarnings("checkstyle:MagicNumber") 13 | @Data 14 | public class ImportKeyRequest { 15 | 16 | @NotNull 17 | @Valid 18 | @JsonProperty("key") 19 | private JsonWebKeyImportRequest key; 20 | 21 | @JsonProperty("attributes") 22 | private BasePropertiesUpdateModel properties; 23 | 24 | @JsonProperty("Hsm") 25 | private Boolean hsm; 26 | 27 | @JsonProperty("tags") 28 | private Map tags; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/secret/KeyVaultSecretModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.secret; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import lombok.Data; 6 | 7 | import java.util.Map; 8 | 9 | @Data 10 | @JsonInclude(JsonInclude.Include.NON_NULL) 11 | public class KeyVaultSecretModel { 12 | 13 | @JsonProperty("id") 14 | private String id; 15 | @JsonProperty("kid") 16 | private String kid; 17 | @JsonProperty("value") 18 | private String value; 19 | @JsonProperty("contentType") 20 | private String contentType; 21 | @JsonProperty("attributes") 22 | private SecretPropertiesModel attributes; 23 | @JsonProperty("tags") 24 | private Map tags; 25 | @JsonProperty("managed") 26 | private Boolean managed; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/certificate/ReadOnlyLifetimeActionPolicy.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.certificate; 2 | 3 | import com.github.nagyesta.lowkeyvault.service.certificate.id.CertificateEntityId; 4 | 5 | import java.time.OffsetDateTime; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.function.UnaryOperator; 9 | 10 | public interface ReadOnlyLifetimeActionPolicy { 11 | 12 | CertificateEntityId getId(); 13 | 14 | OffsetDateTime getCreatedOn(); 15 | 16 | OffsetDateTime getUpdatedOn(); 17 | 18 | Map getLifetimeActions(); 19 | 20 | boolean isAutoRenew(); 21 | 22 | void validate(int validityMonths); 23 | 24 | List missedRenewalDays(OffsetDateTime validityStart, UnaryOperator createdToExpiryFunction); 25 | } 26 | -------------------------------------------------------------------------------- /lowkey-vault-client/src/test/java/com/github/nagyesta/lowkeyvault/http/ContentLengthHeaderRemoverTest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.http; 2 | 3 | import org.apache.http.HttpRequest; 4 | import org.apache.http.protocol.HTTP; 5 | import org.apache.http.protocol.HttpContext; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import static org.mockito.Mockito.*; 9 | 10 | class ContentLengthHeaderRemoverTest { 11 | 12 | @Test 13 | void testProcessShouldRemoveContentLengthWhenCalled() { 14 | //given 15 | final var underTest = new ContentLengthHeaderRemover(); 16 | final var request = mock(HttpRequest.class); 17 | final var context = mock(HttpContext.class); 18 | 19 | //when 20 | underTest.process(request, context); 21 | 22 | //then 23 | verify(request).removeHeaders(HTTP.CONTENT_LEN); 24 | verifyNoMoreInteractions(request, context); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/secret/id/VersionedSecretEntityId.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.secret.id; 2 | 3 | import com.github.nagyesta.lowkeyvault.service.BaseEntityId; 4 | import org.springframework.lang.NonNull; 5 | import org.springframework.util.Assert; 6 | 7 | import java.net.URI; 8 | 9 | public class VersionedSecretEntityId 10 | extends SecretEntityId { 11 | 12 | public VersionedSecretEntityId( 13 | @NonNull final URI vault, 14 | @NonNull final String id) { 15 | this(vault, id, BaseEntityId.generateVersion()); 16 | } 17 | 18 | public VersionedSecretEntityId( 19 | @NonNull final URI vault, 20 | @NonNull final String id, 21 | @NonNull final String version) { 22 | super(vault, id, version); 23 | Assert.notNull(version, "Version must not be null."); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-128-72.json: -------------------------------------------------------------------------------- 1 | { 2 | "versions": [ 3 | { 4 | "attributes": { 5 | "created": 1649503106, 6 | "enabled": true, 7 | "recoverableDays": 90, 8 | "recoveryLevel": "Recoverable+Purgeable", 9 | "updated": 1649503106 10 | }, 11 | "entityId": "jsonBackupOct-128-72", 12 | "entityVersion": "730490a0df9b4ac78ed675902077a18f", 13 | "keyMaterial": { 14 | "k": "sx32Vta2Zx1BsdQBY2l5iQ", 15 | "key_ops": [ 16 | "encrypt", 17 | "wrapKey" 18 | ], 19 | "kid": "https://keys-backup-jsonBackupOct-128-72.localhost:8443/keys/jsonBackupOct-128-72/730490a0df9b4ac78ed675902077a18f", 20 | "kty": "oct-HSM" 21 | }, 22 | "managed": false, 23 | "tags": {}, 24 | "vaultBaseUri": "https://keys-backup-jsonBackupOct-128-72.localhost:8443" 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/CertificateLifetimeActionSerializer.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.json.util; 2 | 3 | import com.github.nagyesta.lowkeyvault.service.certificate.CertificateLifetimeActionActivity; 4 | import tools.jackson.core.JsonGenerator; 5 | import tools.jackson.databind.SerializationContext; 6 | import tools.jackson.databind.ValueSerializer; 7 | 8 | public class CertificateLifetimeActionSerializer 9 | extends ValueSerializer { 10 | 11 | @Override 12 | public void serialize( 13 | final CertificateLifetimeActionActivity value, 14 | final JsonGenerator generator, 15 | final SerializationContext provider) { 16 | generator.writeStartObject(); 17 | generator.writeStringProperty("action_type", value.getValue()); 18 | generator.writeEndObject(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-192-72.json: -------------------------------------------------------------------------------- 1 | { 2 | "versions": [ 3 | { 4 | "attributes": { 5 | "created": 1649503106, 6 | "enabled": true, 7 | "recoverableDays": 90, 8 | "recoveryLevel": "Recoverable+Purgeable", 9 | "updated": 1649503106 10 | }, 11 | "entityId": "jsonBackupOct-192-72", 12 | "entityVersion": "55ccab8f94b244ab97d82899340c22dd", 13 | "keyMaterial": { 14 | "k": "fp2J-nnMUBZVxCDFdKDxjDJX0F_BIM8P", 15 | "key_ops": [ 16 | "encrypt", 17 | "wrapKey" 18 | ], 19 | "kid": "https://keys-backup-jsonBackupOct-192-72.localhost:8443/keys/jsonBackupOct-192-72/55ccab8f94b244ab97d82899340c22dd", 20 | "kty": "oct-HSM" 21 | }, 22 | "managed": false, 23 | "tags": {}, 24 | "vaultBaseUri": "https://keys-backup-jsonBackupOct-192-72.localhost:8443" 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/HashUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.HashAlgorithm; 4 | 5 | import java.security.MessageDigest; 6 | import java.security.NoSuchAlgorithmException; 7 | 8 | public final class HashUtil { 9 | 10 | private HashUtil() { 11 | } 12 | 13 | public static byte[] hash(final byte[] data, final HashAlgorithm algorithm) { 14 | try { 15 | return hash(data, algorithm.getAlgorithmName()); 16 | } catch (final NoSuchAlgorithmException e) { 17 | throw new IllegalArgumentException(e); 18 | } 19 | } 20 | 21 | private static byte[] hash(final byte[] data, final String algorithm) throws NoSuchAlgorithmException { 22 | final var md = MessageDigest.getInstance(algorithm); 23 | md.update(data); 24 | return md.digest(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lowkey-vault-docker/src/test/resources/json/backups/jsonBackupOct-256-72.json: -------------------------------------------------------------------------------- 1 | { 2 | "versions": [ 3 | { 4 | "attributes": { 5 | "created": 1649503106, 6 | "enabled": true, 7 | "recoverableDays": 90, 8 | "recoveryLevel": "Recoverable+Purgeable", 9 | "updated": 1649503106 10 | }, 11 | "entityId": "jsonBackupOct-256-72", 12 | "entityVersion": "19c6d13acb844fda8ccb14751252433a", 13 | "keyMaterial": { 14 | "k": "nmgrv95gVLVdVQ2xe-RpUf-Eog7y0lT22W3EoBU4-cc", 15 | "key_ops": [ 16 | "encrypt", 17 | "wrapKey" 18 | ], 19 | "kid": "https://keys-backup-jsonBackupOct-256-72.localhost:8443/keys/jsonBackupOct-256-72/19c6d13acb844fda8ccb14751252433a", 20 | "kty": "oct-HSM" 21 | }, 22 | "managed": false, 23 | "tags": {}, 24 | "vaultBaseUri": "https://keys-backup-jsonBackupOct-256-72.localhost:8443" 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/common/backup/KeyBackupListItem.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.common.backup; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.github.nagyesta.lowkeyvault.model.v7_2.common.BaseBackupListItem; 5 | import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyPropertiesModel; 6 | import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.JsonWebKeyImportRequest; 7 | import jakarta.validation.Valid; 8 | import jakarta.validation.constraints.NotNull; 9 | import lombok.Data; 10 | import lombok.EqualsAndHashCode; 11 | import lombok.ToString; 12 | 13 | @Data 14 | @EqualsAndHashCode(callSuper = true) 15 | @ToString(callSuper = true) 16 | public class KeyBackupListItem 17 | extends BaseBackupListItem { 18 | @Valid 19 | @NotNull 20 | @JsonProperty("keyMaterial") 21 | private JsonWebKeyImportRequest keyMaterial; 22 | } 23 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipSecretDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.json.util; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.common.backup.SecretBackupList; 4 | import tools.jackson.databind.ObjectMapper; 5 | 6 | public class Base64ZipSecretDeserializer 7 | extends AbstractBase64ZipDeserializer { 8 | 9 | @SuppressWarnings("unused") //used from annotations 10 | public Base64ZipSecretDeserializer() { 11 | this(new Base64Deserializer(), new ObjectMapper()); 12 | } 13 | 14 | protected Base64ZipSecretDeserializer( 15 | final Base64Deserializer base64Deserializer, 16 | final ObjectMapper objectMapper) { 17 | super(base64Deserializer, objectMapper); 18 | } 19 | 20 | @Override 21 | protected Class getType() { 22 | return SecretBackupList.class; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/vault/VaultService.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.vault; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.v7_2.common.constants.RecoveryLevel; 4 | 5 | import java.net.URI; 6 | import java.util.List; 7 | import java.util.Set; 8 | 9 | public interface VaultService { 10 | 11 | VaultFake findByUri(URI uri); 12 | 13 | VaultFake findByUriIncludeDeleted(URI uri); 14 | 15 | VaultFake create(URI uri); 16 | 17 | VaultFake create(URI baseUri, RecoveryLevel recoveryLevel, Integer recoverableDays, Set aliases); 18 | 19 | List list(); 20 | 21 | List listDeleted(); 22 | 23 | boolean delete(URI uri); 24 | 25 | void recover(URI uri); 26 | 27 | boolean purge(URI uri); 28 | 29 | void timeShift(int offsetSeconds, boolean regenerateCertificates); 30 | 31 | VaultFake updateAlias(URI baseUri, URI add, URI remove); 32 | } 33 | -------------------------------------------------------------------------------- /.lift.toml: -------------------------------------------------------------------------------- 1 | # Lift configuration 2 | # Reference https://help.sonatype.com/lift/configuration-reference 3 | 4 | # setup = 5 | # build = ENV= [target] 6 | build = "./gradlew build -x test -x dockerBuild -x dockerRun -x dockerStop" 7 | 8 | # importantRules = 9 | # ignoreRules = 10 | # ignoreFiles = 11 | 12 | ## tools = 13 | tools = ["infer", "findsecbugs"] 14 | 15 | # disableTools = 16 | disableTools = ["errorprone"] 17 | 18 | # customTools = 19 | # allow = 20 | # jdkVersion = 21 | jdkVersion = "17" 22 | 23 | # summaryComments = { 17 | 18 | @Valid 19 | @NotNull 20 | @Size(min = 1) 21 | @JsonProperty("versions") 22 | private List versions = List.of(); 23 | 24 | public void setVersions(final List versions) { 25 | this.versions = List.copyOf(versions); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/EpochSecondsDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.json.util; 2 | 3 | import tools.jackson.core.JsonParser; 4 | import tools.jackson.databind.DeserializationContext; 5 | import tools.jackson.databind.ValueDeserializer; 6 | 7 | import java.time.Instant; 8 | import java.time.OffsetDateTime; 9 | import java.time.ZoneOffset; 10 | import java.util.Optional; 11 | 12 | public class EpochSecondsDeserializer 13 | extends ValueDeserializer { 14 | 15 | @Override 16 | public OffsetDateTime deserialize( 17 | final JsonParser parser, 18 | final DeserializationContext context) { 19 | return Optional.ofNullable(parser.readValueAs(Long.class)) 20 | .map(Instant::ofEpochSecond) 21 | .map((Instant instant) -> OffsetDateTime.ofInstant(instant, ZoneOffset.UTC)) 22 | .orElse(null); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64ZipCertificateDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.json.util; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.common.backup.CertificateBackupList; 4 | import tools.jackson.databind.ObjectMapper; 5 | 6 | public class Base64ZipCertificateDeserializer extends AbstractBase64ZipDeserializer { 7 | 8 | @SuppressWarnings("unused") //used from annotations 9 | public Base64ZipCertificateDeserializer() { 10 | this(new Base64Deserializer(), new ObjectMapper()); 11 | } 12 | 13 | protected Base64ZipCertificateDeserializer( 14 | final Base64Deserializer base64Deserializer, 15 | final ObjectMapper objectMapper) { 16 | super(base64Deserializer, objectMapper); 17 | } 18 | 19 | @Override 20 | protected Class getType() { 21 | return CertificateBackupList.class; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/certificate/IssuerParameterModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.certificate; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.github.nagyesta.lowkeyvault.service.certificate.impl.CertAuthorityType; 6 | import lombok.Data; 7 | 8 | import java.util.Optional; 9 | 10 | @Data 11 | public class IssuerParameterModel { 12 | 13 | @JsonProperty("cert_transparency") 14 | private boolean certTransparency; 15 | @JsonProperty("cty") 16 | private String certType; 17 | @JsonProperty("name") 18 | private String issuer; 19 | 20 | @JsonCreator 21 | public IssuerParameterModel() { 22 | } 23 | 24 | public IssuerParameterModel(final CertAuthorityType issuer) { 25 | this(); 26 | this.issuer = Optional.ofNullable(issuer).map(CertAuthorityType::getValue).orElse(null); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/common/KeyVaultItemListModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.common; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import lombok.Data; 6 | import lombok.NonNull; 7 | import org.springframework.lang.Nullable; 8 | 9 | import java.net.URI; 10 | import java.util.List; 11 | import java.util.Optional; 12 | 13 | @Data 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | public class KeyVaultItemListModel { 16 | 17 | @JsonProperty("nextLink") 18 | private String nextLink; 19 | 20 | @JsonProperty("value") 21 | private List value; 22 | 23 | public KeyVaultItemListModel( 24 | @NonNull final List value, 25 | @Nullable final URI nextLinkUri) { 26 | this.value = List.copyOf(value); 27 | this.nextLink = Optional.ofNullable(nextLinkUri).map(URI::toString).orElse(null); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lowkey-vault-docker/src/test/resources/certs/ec521-ec-localhost.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MGkCAQAwEAYHKoZIzj0CAQYFK4EEACMEUjBQAgEBBEIBJ/Bcajc4nI1IyhE8A6ny 3 | JePp5s/K0LJydfNdlthqsvzIr1zQmjgfQCpCJgD6VHr6o5J1uQRnT5Cpi/swvQoX 4 | 8OmgBwYFK4EEACM= 5 | -----END PRIVATE KEY----- 6 | -----BEGIN CERTIFICATE----- 7 | MIIBzTCCASygAwIBAgIEKaADaTAMBggqhkjOPQQDBAUAMBcxFTATBgNVBAMTDGVj 8 | LmxvY2FsaG9zdDAeFw0yMjA5MTAxOTU1MzJaFw0yMzA5MTAxOTU1MzJaMBcxFTAT 9 | BgNVBAMTDGVjLmxvY2FsaG9zdDCBmzAQBgcqhkjOPQIBBgUrgQQAIwOBhgAEAUaq 10 | /pHrWi9EktVrW41A4oYXPLsV3e4yhqZvck1Txr6ElvAOIWVHinZ8vmrnfTGJ7YH/ 11 | DpnLGwYC+V+W/CCk/s4KAA3fnWyu/kIlPxt5IXdJ6KLVhLzAxv+VY8IYb/W+cgIO 12 | trKfip6SbNKx6hG0jP+IDRweXNU0ZyvKMh5sSp/b8UVmoyEwHzAdBgNVHQ4EFgQU 13 | JEQMvw7eWMDjtnMv78XSs2VnVDkwDAYIKoZIzj0EAwQFAAOBjAAwgYgCQgGvTXL6 14 | ZsnnKCZKHTWjtZZf+gw9Osyh8+a6Lb5VzHu0UybDYolsOUQHfb0gjHdQj9wlct88 15 | EDyqjQEmrgEONtPnGwJCALwoLMw/OF5UVak9hvzYcOrOqE43gbidJ9wGbAOShIDC 16 | klY0quWX6jfgPaqDuU5mkfqTMMx66ALotBk1bK4hpF5l 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/mapper/v7_3/certificate/CertificateEntityToV73IssuancePolicyModelConverter.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.mapper.v7_3.certificate; 2 | 3 | import com.github.nagyesta.lowkeyvault.mapper.common.registry.CertificateConverterRegistry; 4 | import com.github.nagyesta.lowkeyvault.service.certificate.ReadOnlyKeyVaultCertificateEntity; 5 | import lombok.NonNull; 6 | 7 | public class CertificateEntityToV73IssuancePolicyModelConverter extends BaseCertificateEntityToV73PolicyModelConverter { 8 | 9 | private final CertificateConverterRegistry registry; 10 | 11 | public CertificateEntityToV73IssuancePolicyModelConverter(@NonNull final CertificateConverterRegistry registry) { 12 | super(ReadOnlyKeyVaultCertificateEntity::getIssuancePolicy); 13 | this.registry = registry; 14 | } 15 | 16 | @Override 17 | public void afterPropertiesSet() { 18 | registry.registerIssuancePolicyConverter(this); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/EcKeyCreationInput.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.key.impl; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyCurveName; 4 | import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyType; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.Getter; 7 | import lombok.ToString; 8 | import org.springframework.lang.NonNull; 9 | import org.springframework.util.Assert; 10 | 11 | @Getter 12 | @EqualsAndHashCode(callSuper = true) 13 | @ToString(callSuper = true) 14 | public class EcKeyCreationInput 15 | extends KeyCreationInput { 16 | 17 | public EcKeyCreationInput( 18 | @NonNull final KeyType keyType, 19 | final KeyCurveName keyParameter) { 20 | super(keyType, keyParameter); 21 | Assert.isTrue(keyType.isEc(), "KeyType must be EC."); 22 | Assert.notNull(keyParameter, "KeyCurveName must not be null."); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/mapper/common/RecoveryAwareConverter.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.mapper.common; 2 | 3 | import com.github.nagyesta.lowkeyvault.context.ApiVersionAware; 4 | import org.springframework.beans.factory.InitializingBean; 5 | import org.springframework.lang.NonNull; 6 | import org.springframework.lang.Nullable; 7 | 8 | import java.net.URI; 9 | 10 | /** 11 | * Converter interface supporting both active and deleted entities. 12 | * 13 | * @param The source type. 14 | * @param The active target type. 15 | * @param

The deleted target type. 16 | */ 17 | @SuppressWarnings("java:S119") //It is easier to ensure that the types are consistent this way 18 | public interface RecoveryAwareConverter 19 | extends ApiVersionAware, InitializingBean { 20 | 21 | @Nullable 22 | T convert(S source, @NonNull URI vaultUri); 23 | 24 | @NonNull 25 | DT convertDeleted(@NonNull S source, @NonNull URI vaultUri); 26 | } 27 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/common/VersionedEntityMultiMap.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.common; 2 | 3 | import com.github.nagyesta.lowkeyvault.service.EntityId; 4 | import lombok.NonNull; 5 | 6 | import java.util.function.Consumer; 7 | import java.util.function.UnaryOperator; 8 | 9 | @SuppressWarnings("java:S119") //It is easier to ensure that the types are consistent this way 10 | public interface VersionedEntityMultiMap, ME extends RE> 11 | extends ReadOnlyVersionedEntityMultiMap { 12 | 13 | ME getEntity(V entityId); 14 | 15 | void put(V entityId, ME entity); 16 | 17 | boolean isDeleted(); 18 | 19 | void moveTo(K entityId, VersionedEntityMultiMap destination, UnaryOperator applyToAll); 20 | 21 | void purgeExpired(); 22 | 23 | void purgeDeleted(K entityId); 24 | 25 | void forEachEntity(@NonNull Consumer entityConsumer); 26 | } 27 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/certificate/X509CertificateModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.certificate; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.github.nagyesta.lowkeyvault.service.certificate.impl.KeyUsageEnum; 5 | import jakarta.validation.Valid; 6 | import jakarta.validation.constraints.NotBlank; 7 | import jakarta.validation.constraints.NotNull; 8 | import lombok.Data; 9 | 10 | import java.util.Set; 11 | 12 | @Data 13 | public class X509CertificateModel { 14 | 15 | @NotNull 16 | @NotBlank 17 | @JsonProperty("subject") 18 | private String subject; 19 | @JsonProperty("ekus") 20 | private Set extendedKeyUsage; 21 | @JsonProperty("key_usage") 22 | private Set keyUsage; 23 | @JsonProperty("validity_months") 24 | private Integer validityMonths; 25 | @JsonProperty("sans") 26 | @Valid 27 | private SubjectAlternativeNames subjectAlternativeNames; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/RandomBytesResponse.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.key; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.github.nagyesta.lowkeyvault.model.json.util.Base64Deserializer; 6 | import com.github.nagyesta.lowkeyvault.model.json.util.Base64Serializer; 7 | import lombok.Data; 8 | import tools.jackson.databind.annotation.JsonDeserialize; 9 | import tools.jackson.databind.annotation.JsonSerialize; 10 | 11 | @Data 12 | @JsonInclude(JsonInclude.Include.NON_NULL) 13 | public class RandomBytesResponse { 14 | 15 | @JsonProperty("value") 16 | @JsonSerialize(using = Base64Serializer.class) 17 | @JsonDeserialize(using = Base64Deserializer.class) 18 | private byte[] value; 19 | 20 | public RandomBytesResponse() { 21 | } 22 | 23 | public RandomBytesResponse(final byte[] value) { 24 | this(); 25 | this.value = value; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/RsaKeyCreationInput.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.key.impl; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyType; 4 | import lombok.EqualsAndHashCode; 5 | import lombok.Getter; 6 | import lombok.ToString; 7 | import org.springframework.lang.NonNull; 8 | import org.springframework.util.Assert; 9 | 10 | import java.math.BigInteger; 11 | 12 | @Getter 13 | @EqualsAndHashCode(callSuper = true) 14 | @ToString(callSuper = true) 15 | public class RsaKeyCreationInput 16 | extends KeyCreationInput { 17 | 18 | private final BigInteger publicExponent; 19 | 20 | public RsaKeyCreationInput( 21 | @NonNull final KeyType keyType, 22 | final Integer keyParameter, 23 | final BigInteger publicExponent) { 24 | super(keyType, keyParameter); 25 | Assert.isTrue(keyType.isRsa(), "KeyType must be RSA."); 26 | this.publicExponent = publicExponent; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/certificate/KeyVaultCertificateItemModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.certificate; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.github.nagyesta.lowkeyvault.model.json.util.Base64Deserializer; 5 | import com.github.nagyesta.lowkeyvault.model.json.util.Base64Serializer; 6 | import lombok.Data; 7 | import tools.jackson.databind.annotation.JsonDeserialize; 8 | import tools.jackson.databind.annotation.JsonSerialize; 9 | 10 | import java.util.Map; 11 | 12 | @Data 13 | public class KeyVaultCertificateItemModel { 14 | 15 | @JsonProperty("id") 16 | private String certificateId; 17 | @JsonSerialize(using = Base64Serializer.class) 18 | @JsonDeserialize(using = Base64Deserializer.class) 19 | @JsonProperty("x5t") 20 | private byte[] thumbprint; 21 | @JsonProperty("attributes") 22 | private CertificatePropertiesModel attributes; 23 | @JsonProperty("tags") 24 | private Map tags; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/v7_3/certificate/IssuerParameterModelTest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.certificate; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static com.github.nagyesta.lowkeyvault.service.certificate.impl.CertAuthorityType.SELF_SIGNED; 7 | 8 | class IssuerParameterModelTest { 9 | 10 | @Test 11 | void testControllerShouldSetIssuerWhenCalledWithCertificateAuthority() { 12 | //given 13 | 14 | //when 15 | final var actual = new IssuerParameterModel(SELF_SIGNED); 16 | 17 | //then 18 | Assertions.assertEquals(SELF_SIGNED.getValue(), actual.getIssuer()); 19 | } 20 | 21 | @Test 22 | void testControllerShouldNotSetIssuerWhenCalledWithoutCertificateAuthority() { 23 | //given 24 | 25 | //when 26 | final var actual = new IssuerParameterModel(null); 27 | 28 | //then 29 | Assertions.assertNull(actual.getIssuer()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/id/KeyEntityId.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.key.id; 2 | 3 | import com.github.nagyesta.lowkeyvault.service.BaseEntityId; 4 | import com.github.nagyesta.lowkeyvault.service.EntityId; 5 | import lombok.NonNull; 6 | 7 | import java.net.URI; 8 | 9 | public class KeyEntityId 10 | extends BaseEntityId implements EntityId { 11 | 12 | private static final String URI_ROTATIONPOLICY_FORMAT = "%s/%s/%s/rotationpolicy"; 13 | 14 | public KeyEntityId(final URI vault, final String id) { 15 | this(vault, id, null); 16 | } 17 | 18 | public KeyEntityId( 19 | @NonNull final URI vault, 20 | @NonNull final String id, 21 | final String version) { 22 | super(vault, id, version, "key"); 23 | } 24 | 25 | public URI asRotationPolicyUri(@NonNull final URI vaultUri) { 26 | return URI.create(URI_ROTATIONPOLICY_FORMAT 27 | .formatted(vaultUri, entityPathSegment(), id())); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lowkey-vault-client/src/main/java/com/github/nagyesta/lowkeyvault/http/management/EpochSecondsDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.http.management; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.databind.DeserializationContext; 5 | import com.fasterxml.jackson.databind.JsonDeserializer; 6 | 7 | import java.io.IOException; 8 | import java.time.Instant; 9 | import java.time.OffsetDateTime; 10 | import java.time.ZoneOffset; 11 | import java.util.Optional; 12 | 13 | public class EpochSecondsDeserializer 14 | extends JsonDeserializer { 15 | 16 | @Override 17 | public OffsetDateTime deserialize( 18 | final JsonParser parser, 19 | final DeserializationContext context) throws IOException { 20 | return Optional.ofNullable(parser.readValueAs(Long.class)) 21 | .map(Instant::ofEpochSecond) 22 | .map((Instant instant) -> OffsetDateTime.ofInstant(instant, ZoneOffset.UTC)) 23 | .orElse(null); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/EpochSecondsSerializer.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.json.util; 2 | 3 | import tools.jackson.core.JsonGenerator; 4 | import tools.jackson.databind.SerializationContext; 5 | import tools.jackson.databind.ValueSerializer; 6 | 7 | import java.time.OffsetDateTime; 8 | import java.util.Optional; 9 | 10 | public class EpochSecondsSerializer 11 | extends ValueSerializer { 12 | 13 | @Override 14 | public void serialize( 15 | final OffsetDateTime value, 16 | final JsonGenerator generator, 17 | final SerializationContext provider) { 18 | final var optional = Optional.ofNullable(value); 19 | final var optionalEpochSeconds = optional 20 | .map(OffsetDateTime::toEpochSecond); 21 | if (optionalEpochSeconds.isPresent()) { 22 | generator.writeNumber(optionalEpochSeconds.get()); 23 | } else { 24 | generator.writeNull(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/context/ApiVersionAware.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.context; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.common.ApiConstants; 4 | 5 | import java.util.Collections; 6 | import java.util.Set; 7 | import java.util.SortedSet; 8 | import java.util.TreeSet; 9 | 10 | public interface ApiVersionAware { 11 | 12 | /** 13 | * Supported API version collection containing all 7.3+ versions. 14 | */ 15 | SortedSet V7_3_AND_LATER = Collections.unmodifiableSortedSet( 16 | new TreeSet<>(Set.of(ApiConstants.V_7_3, ApiConstants.V_7_4, ApiConstants.V_7_5, ApiConstants.V_7_6))); 17 | /** 18 | * Supported API version collection containing all versions (7.2, 7.3, 7.4, 7.5 and 7.6). 19 | */ 20 | SortedSet ALL_VERSIONS = Collections.unmodifiableSortedSet( 21 | new TreeSet<>(Set.of(ApiConstants.V_7_2, ApiConstants.V_7_3, ApiConstants.V_7_4, ApiConstants.V_7_5, ApiConstants.V_7_6))); 22 | 23 | SortedSet supportedVersions(); 24 | } 25 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/mapper/v7_3/certificate/CertificateEntityToV73PolicyModelConverter.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.mapper.v7_3.certificate; 2 | 3 | import com.github.nagyesta.lowkeyvault.mapper.common.registry.CertificateConverterRegistry; 4 | import com.github.nagyesta.lowkeyvault.service.certificate.ReadOnlyKeyVaultCertificateEntity; 5 | import lombok.NonNull; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | 8 | public class CertificateEntityToV73PolicyModelConverter extends BaseCertificateEntityToV73PolicyModelConverter { 9 | 10 | private final CertificateConverterRegistry registry; 11 | 12 | @Autowired 13 | public CertificateEntityToV73PolicyModelConverter(@NonNull final CertificateConverterRegistry registry) { 14 | super(ReadOnlyKeyVaultCertificateEntity::getOriginalCertificatePolicy); 15 | this.registry = registry; 16 | } 17 | 18 | @Override 19 | public void afterPropertiesSet() { 20 | registry.registerPolicyConverter(this); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/common/BaseBackupModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.common; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.github.nagyesta.lowkeyvault.model.v7_2.BasePropertiesModel; 5 | import com.github.nagyesta.lowkeyvault.model.v7_2.key.BackupListContainer; 6 | import jakarta.validation.Valid; 7 | import jakarta.validation.constraints.NotNull; 8 | import lombok.Data; 9 | 10 | /** 11 | * Base class of backup models. 12 | * 13 | * @param

The type of the properties model. 14 | * @param The type of the backup list items. 15 | * @param The wrapper type of the backup list. 16 | */ 17 | @Data 18 | @JsonInclude(JsonInclude.Include.NON_NULL) 19 | @SuppressWarnings("java:S119") //It is easier to ensure that the types are consistent this way 20 | public class BaseBackupModel

, BL extends BackupListContainer> { 21 | 22 | @Valid 23 | @NotNull 24 | private BL value; 25 | } 26 | -------------------------------------------------------------------------------- /lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256-72.json: -------------------------------------------------------------------------------- 1 | { 2 | "versions": [ 3 | { 4 | "attributes": { 5 | "created": 1649503879, 6 | "enabled": true, 7 | "recoverableDays": 90, 8 | "recoveryLevel": "Recoverable+Purgeable", 9 | "updated": 1649503879 10 | }, 11 | "entityId": "jsonBackupEc-256-72", 12 | "entityVersion": "f16a91376c13478996195a0c2cee39cb", 13 | "keyMaterial": { 14 | "crv": "P-256", 15 | "d": "IvdJQWa59MJflXPF25Cc4UlOREHZVL5_6sD27nyz1J8", 16 | "key_ops": [ 17 | "sign" 18 | ], 19 | "kid": "https://keys-backup-jsonBackupEc-256-72.localhost:8443/keys/jsonBackupEc-256-72/f16a91376c13478996195a0c2cee39cb", 20 | "kty": "EC", 21 | "x": "XBlNkVS33eZqrVgbpeoNerx5XKMixFxFrYl5FJKQ5vQ", 22 | "y": "KKoHw5q3QM1lehkqggvNViaX1KBM2ePvmXE2IQbWiLE" 23 | }, 24 | "managed": false, 25 | "tags": {}, 26 | "vaultBaseUri": "https://keys-backup-jsonBackupEc-256-72.localhost:8443" 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/constants/LifetimeActionType.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.key.constants; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonValue; 5 | 6 | import java.util.Arrays; 7 | 8 | public enum LifetimeActionType { 9 | /** 10 | * Notification triggers. 11 | */ 12 | NOTIFY("notify"), 13 | /** 14 | * Automatic rotation trigger. 15 | */ 16 | ROTATE("rotate"); 17 | 18 | private final String value; 19 | 20 | LifetimeActionType(final String value) { 21 | this.value = value; 22 | } 23 | 24 | @JsonCreator 25 | public static LifetimeActionType forValue(final String name) { 26 | return Arrays.stream(values()) 27 | .filter(actionType -> actionType.getValue().equals(name)) 28 | .findFirst() 29 | .orElse(null); 30 | } 31 | 32 | @JsonValue 33 | public String getValue() { 34 | return value; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-256k-72.json: -------------------------------------------------------------------------------- 1 | { 2 | "versions": [ 3 | { 4 | "attributes": { 5 | "created": 1649503879, 6 | "enabled": true, 7 | "recoverableDays": 90, 8 | "recoveryLevel": "Recoverable+Purgeable", 9 | "updated": 1649503879 10 | }, 11 | "entityId": "jsonBackupEc-256k-72", 12 | "entityVersion": "5425a5872c1f4a9fa96db8f40e6c5d07", 13 | "keyMaterial": { 14 | "crv": "P-256K", 15 | "d": "AONvayGgRSTtrfA6lkCRC-qYccs_pQh35ZSXssCulkrJ", 16 | "key_ops": [ 17 | "sign" 18 | ], 19 | "kid": "https://keys-backup-jsonBackupEc-256k-72.localhost:8443/keys/jsonBackupEc-256k-72/5425a5872c1f4a9fa96db8f40e6c5d07", 20 | "kty": "EC", 21 | "x": "5IeF3ibB0c0QP5J2FOcxqxRi4TAs9aktvys3H41X04c", 22 | "y": "DJivtzkKcvFCNe9ZmzCzflbG_CSQrbuDBauJDneZ6Xc" 23 | }, 24 | "managed": false, 25 | "tags": {}, 26 | "vaultBaseUri": "https://keys-backup-jsonBackupEc-256k-72.localhost:8443" 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /lowkey-vault-client/src/main/java/com/github/nagyesta/lowkeyvault/http/management/EpochSecondsSerializer.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.http.management; 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator; 4 | import com.fasterxml.jackson.databind.JsonSerializer; 5 | import com.fasterxml.jackson.databind.SerializerProvider; 6 | 7 | import java.io.IOException; 8 | import java.time.OffsetDateTime; 9 | import java.util.Optional; 10 | 11 | public class EpochSecondsSerializer 12 | extends JsonSerializer { 13 | 14 | @Override 15 | public void serialize( 16 | final OffsetDateTime value, 17 | final JsonGenerator generator, 18 | final SerializerProvider provider) throws IOException { 19 | final var optionalEpochSeconds = Optional.ofNullable(value) 20 | .map(OffsetDateTime::toEpochSecond); 21 | if (optionalEpochSeconds.isPresent()) { 22 | generator.writeNumber(optionalEpochSeconds.get()); 23 | } else { 24 | generator.writeNull(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyLifetimeActionTypeModelTest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.key; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.v7_3.key.constants.LifetimeActionType; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.Test; 6 | 7 | class KeyLifetimeActionTypeModelTest { 8 | 9 | @SuppressWarnings("ConstantConditions") 10 | @Test 11 | void testConstructorShouldThrowExceptionWhenCalledWithNull() { 12 | //given 13 | 14 | //when 15 | Assertions.assertThrows(IllegalArgumentException.class, () -> new KeyLifetimeActionTypeModel(null)); 16 | 17 | //then + exception 18 | } 19 | 20 | @Test 21 | void testConstructorShouldSetActionTypeWhenCalledWithValidValue() { 22 | //given 23 | final var expected = LifetimeActionType.NOTIFY; 24 | 25 | //when 26 | final var actual = new KeyLifetimeActionTypeModel(expected); 27 | 28 | //then 29 | Assertions.assertEquals(expected, actual.getType()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Something didn't go as expected? Please report a bug to help us improve! 4 | title: Bug report 5 | labels: bug 6 | assignees: "" 7 | --- 8 | 9 | #### Describe the bug 10 | 11 | A clear and concise description of what the bug is. 12 | 13 | #### To reproduce 14 | 15 | Steps to reproduce the behavior: 16 | 17 | 1. Use the app 18 | 2. Do these steps 19 | 20 | #### Expected behavior 21 | 22 | A clear and concise description of what you expected to happen. 23 | 24 | #### Actual behavior 25 | 26 | What happened in your case? e.g. At step 2 there is an error with this message... 27 | 28 | #### The command you used 29 | 30 | If applicable, add screenshots to help explain your problem. 31 | 32 | #### A minimal project that can be used to reproduce the issue 33 | 34 | If applicable. Please do NOT share sensitive information. 35 | 36 | #### Environment 37 | 38 | - OS: [e.g. Windows/Unix] 39 | - Version: [e.g. 1.0.0] 40 | - Java version [e.g. 11.0.2 Open JDK] 41 | 42 | #### Additional context 43 | 44 | Add any other context about the problem here. 45 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/common/backup/CertificateBackupList.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.common.backup; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.github.nagyesta.lowkeyvault.model.v7_2.key.BackupListContainer; 5 | import jakarta.validation.Valid; 6 | import jakarta.validation.constraints.NotNull; 7 | import jakarta.validation.constraints.Size; 8 | import lombok.EqualsAndHashCode; 9 | 10 | import java.util.List; 11 | 12 | @EqualsAndHashCode 13 | public class CertificateBackupList 14 | implements BackupListContainer { 15 | 16 | @Valid 17 | @NotNull 18 | @Size(min = 1) 19 | @JsonProperty("versions") 20 | private List versions = List.of(); 21 | 22 | @Override 23 | public List getVersions() { 24 | return versions; 25 | } 26 | 27 | @Override 28 | public void setVersions(final List versions) { 29 | this.versions = List.copyOf(versions); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/certificate/CertificateLifetimeActionActivity.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.certificate; 2 | 3 | import lombok.Getter; 4 | import lombok.NonNull; 5 | 6 | import java.util.Arrays; 7 | 8 | @Getter 9 | public enum CertificateLifetimeActionActivity { 10 | /** 11 | * Noop, simulates email notification actions. 12 | */ 13 | EMAIL_CONTACTS("EmailContacts"), 14 | /** 15 | * Performs automatic renewal of the certificate. 16 | */ 17 | AUTO_RENEW("AutoRenew"); 18 | 19 | private final String value; 20 | 21 | CertificateLifetimeActionActivity(final String value) { 22 | this.value = value; 23 | } 24 | 25 | public static CertificateLifetimeActionActivity byValue(@NonNull final String value) { 26 | return Arrays.stream(CertificateLifetimeActionActivity.values()) 27 | .filter(v -> v.value.equals(value)) 28 | .findFirst() 29 | .orElseThrow(() -> new IllegalArgumentException("Unknown lifetime action activity: " + value)); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/keys/RandomData.feature: -------------------------------------------------------------------------------- 1 | Feature: Random data 2 | 3 | @RandomData 4 | Scenario Outline: RANDOM_DATA_01 The vault is used for getting random bytes 5 | Given key API version is used 6 | And a key client is created with the vault named default 7 | When the vault is called for bytes of random data 8 | Then the length of the random data is bytes 9 | 10 | Examples: 11 | | api | count | 12 | | 7.3 | 1 | 13 | | 7.3 | 2 | 14 | | 7.3 | 3 | 15 | | 7.3 | 5 | 16 | | 7.3 | 10 | 17 | | 7.3 | 32 | 18 | | 7.3 | 63 | 19 | | 7.3 | 64 | 20 | | 7.3 | 128 | 21 | | 7.3 | 1024 | 22 | | 7.3 | 2048 | 23 | | 7.3 | 15000 | 24 | | 7.3 | 24000 | 25 | | 7.4 | 1 | 26 | | 7.4 | 42 | 27 | | 7.5 | 1 | 28 | | 7.5 | 42 | 29 | | 7.6 | 1 | 30 | | 7.6 | 42 | 31 | -------------------------------------------------------------------------------- /lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-384-72.json: -------------------------------------------------------------------------------- 1 | { 2 | "versions": [ 3 | { 4 | "attributes": { 5 | "created": 1649503879, 6 | "enabled": true, 7 | "recoverableDays": 90, 8 | "recoveryLevel": "Recoverable+Purgeable", 9 | "updated": 1649503879 10 | }, 11 | "entityId": "jsonBackupEc-384-72", 12 | "entityVersion": "7f4c0a2ef5454e07a533e597434984a8", 13 | "keyMaterial": { 14 | "crv": "P-384", 15 | "d": "bd2taaXwxvA_DRUZ1wMT28l8TnaMDz1mn2Z2x_pJT_nkZ11BNS1FFxJvjYHIvoU4", 16 | "key_ops": [ 17 | "sign" 18 | ], 19 | "kid": "https://keys-backup-jsonBackupEc-384-72.localhost:8443/keys/jsonBackupEc-384-72/7f4c0a2ef5454e07a533e597434984a8", 20 | "kty": "EC", 21 | "x": "KzD2vTm-aSjXN_RFlY7P78R6hpfdJcSHTC9WM7QXmf0VJro3cXdFOZk6vrx5WDjE", 22 | "y": "pjLyFfCNzQ7yZUFc6iVS8eSWoRXY84sg3pzcSlkgn5LPYf9Vx7QSgAXfbRtmHGt_" 23 | }, 24 | "managed": false, 25 | "tags": {}, 26 | "vaultBaseUri": "https://keys-backup-jsonBackupEc-384-72.localhost:8443" 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/common/BaseVaultFake.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.common; 2 | 3 | import com.github.nagyesta.lowkeyvault.service.EntityId; 4 | 5 | import java.time.OffsetDateTime; 6 | import java.util.Map; 7 | 8 | /** 9 | * The base interface of the vault fakes. 10 | * 11 | * @param The type of the key (not versioned). 12 | * @param The versioned key type. 13 | * @param The entity type. 14 | */ 15 | public interface BaseVaultFake> 16 | extends TimeAware { 17 | 18 | ReadOnlyVersionedEntityMultiMap getEntities(); 19 | 20 | ReadOnlyVersionedEntityMultiMap getDeletedEntities(); 21 | 22 | void clearTags(V entityId); 23 | 24 | void addTags(V entityId, Map tags); 25 | 26 | void setEnabled(V entityId, boolean enabled); 27 | 28 | void setExpiry(V entityId, OffsetDateTime notBefore, OffsetDateTime expiry); 29 | 30 | void delete(K entityId); 31 | 32 | void recover(K entityId); 33 | 34 | void purge(K entityId); 35 | 36 | } 37 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/cert/invalid-ec.pem: -------------------------------------------------------------------------------- 1 | Bag Attributes 2 | friendlyName: d2590dcf-8ec9-4bef-8c2d-8e51a43b29b6 3 | localKeyID: 54 69 6D 65 20 31 36 36 33 30 31 34 35 33 34 36 38 30 4 | Key Attributes: 5 | -----BEGIN PRIVATE KEY----- 6 | MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCAFlIRPFr7jjJA+9DAf 7 | pWb/gTypdclusJCFTTBK0qc8Nw== 8 | -----END PRIVATE KEY----- 9 | Bag Attributes 10 | friendlyName: d2590dcf-8ec9-4bef-8c2d-8e51a43b29b6 11 | localKeyID: 54 69 6D 65 20 31 36 36 33 30 31 34 35 33 34 36 38 30 12 | subject=CN = ec.localhost 13 | 14 | issuer=CN = ec.localhost 15 | 16 | -----BEGIN CERTIFICATE----- 17 | MIIBRDCB6aADAgECAgR3UZEbMAwGCCqGSM49BAMCBQAwFzEVMBMGA1UEAxMMZWMu 18 | bG9jYWxob3N0MB4XDTIyMDkxMDE5MDA1NVoXDTIzMDkxMDE5MDA1NVowFzEVMBMG 19 | A1UEAxMMZWMubG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9QeN 20 | Y5gGwMQnCSUrFfJ1CQp8ngrTJn9ZzTusUY8Gh5JWennjFdzLIqJ4yhpSzGAl+/jn 21 | Gv3n+fBjt7mUZu2I76MhMB8wHQYDVR0OBBYEFA2YDS/W2/Dv5qJrmtbE7w+HUtL0 22 | MAwGCCqGSM49BAMCBQADSAAwRQIgMQAYrmTDkcxQgS33oHbw+H/7YEO43ZDqSOTr 23 | tn7PQa8CIQCf8JCfvoC0W67JsBRFPDNJEKBuNHVMOWuKjwrXaqynuQ== 24 | -----END CERTIFICATE----- 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea/** 18 | !.idea/icon.png 19 | !.idea/codeStyles 20 | !.idea/codeStyles/** 21 | !.idea/inspectionProfiles 22 | !.idea/inspectionProfiles/** 23 | *.iws 24 | *.iml 25 | *.ipr 26 | out/ 27 | 28 | ### NetBeans ### 29 | /nbproject/private/ 30 | /nbbuild/ 31 | /dist/ 32 | /nbdist/ 33 | /.nb-gradle/ 34 | 35 | ### VS Code ### 36 | .vscode/ 37 | 38 | ## For subprojects 39 | */.gradle 40 | */build/ 41 | !*/gradle/wrapper/gradle-wrapper.jar 42 | !*/**/src/main/** 43 | !*/**/src/test/** 44 | 45 | ### STS ### 46 | */.apt_generated 47 | */.classpath 48 | */.factorypath 49 | */.project 50 | */.settings 51 | */.springBeans 52 | */.sts4-cache 53 | 54 | ### IntelliJ IDEA ### 55 | */.idea 56 | */*.iws 57 | */*.iml 58 | */*.ipr 59 | */out/ 60 | 61 | ### NetBeans ### 62 | /*/nbproject/private/ 63 | /*/nbbuild/ 64 | /*/dist/ 65 | /*/nbdist/ 66 | /*/.nb-gradle/ 67 | 68 | ### VS Code ### 69 | */.vscode/ 70 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/certificate/id/CertificateEntityId.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.certificate.id; 2 | 3 | import com.github.nagyesta.lowkeyvault.service.BaseEntityId; 4 | import com.github.nagyesta.lowkeyvault.service.EntityId; 5 | import lombok.NonNull; 6 | 7 | import java.net.URI; 8 | 9 | public class CertificateEntityId 10 | extends BaseEntityId implements EntityId { 11 | 12 | public CertificateEntityId( 13 | final URI vault, 14 | final String id) { 15 | this(vault, id, null); 16 | } 17 | 18 | public CertificateEntityId( 19 | @NonNull final URI vault, 20 | @NonNull final String id, 21 | final String version) { 22 | super(vault, id, version, "certificate"); 23 | } 24 | 25 | public URI asPolicyUri(@NonNull final URI vaultUri) { 26 | return URI.create(asUri(vaultUri).toString() + "/policy"); 27 | } 28 | 29 | public URI asPendingOperationUri(@NonNull final URI vaultUri) { 30 | return URI.create(asUriNoVersion(vaultUri).toString() + "/pending"); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/mapper/v7_2/key/KeyEntityToV72KeyVersionItemModelConverter.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.mapper.v7_2.key; 2 | 3 | import com.github.nagyesta.lowkeyvault.mapper.common.registry.KeyConverterRegistry; 4 | import com.github.nagyesta.lowkeyvault.service.key.ReadOnlyKeyVaultKeyEntity; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.lang.NonNull; 7 | 8 | import java.net.URI; 9 | 10 | public class KeyEntityToV72KeyVersionItemModelConverter extends KeyEntityToV72KeyItemModelConverter { 11 | 12 | @Autowired 13 | public KeyEntityToV72KeyVersionItemModelConverter(@NonNull final KeyConverterRegistry registry) { 14 | super(registry); 15 | } 16 | 17 | @Override 18 | protected void register(final KeyConverterRegistry registry) { 19 | registry.registerVersionedItemConverter(this); 20 | } 21 | 22 | @Override 23 | protected String convertKeyId( 24 | final ReadOnlyKeyVaultKeyEntity source, 25 | final URI vaultUri) { 26 | return source.getId().asUri(vaultUri).toString(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-present Istvan Zoltan Nagy (a.k.a Esta Nagy) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/mapper/common/BasePropertiesModelConverter.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.mapper.common; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.v7_2.BasePropertiesModel; 4 | import com.github.nagyesta.lowkeyvault.service.EntityId; 5 | import com.github.nagyesta.lowkeyvault.service.common.BaseVaultEntity; 6 | 7 | public abstract class BasePropertiesModelConverter, M extends BasePropertiesModel> 9 | implements AliasAwareConverter { 10 | 11 | protected M mapCommonFields( 12 | final E entity, 13 | final M attributes) { 14 | attributes.setCreatedOn(entity.getCreated()); 15 | attributes.setUpdatedOn(entity.getUpdated()); 16 | attributes.setEnabled(entity.isEnabled()); 17 | entity.getExpiry().ifPresent(attributes::setExpiresOn); 18 | entity.getNotBefore().ifPresent(attributes::setNotBefore); 19 | attributes.setRecoveryLevel(entity.getRecoveryLevel()); 20 | attributes.setRecoverableDays(entity.getRecoverableDays()); 21 | return attributes; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyLifetimeActionTypeModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.key; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.github.nagyesta.lowkeyvault.model.v7_3.key.constants.LifetimeActionType; 7 | import com.github.nagyesta.lowkeyvault.model.v7_3.key.validator.Restore; 8 | import com.github.nagyesta.lowkeyvault.model.v7_3.key.validator.Update; 9 | import jakarta.validation.constraints.NotNull; 10 | import lombok.Data; 11 | import lombok.NonNull; 12 | 13 | @Data 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | public class KeyLifetimeActionTypeModel { 16 | 17 | @NotNull(groups = {Restore.class, Update.class}) 18 | @JsonProperty("type") 19 | private LifetimeActionType type; 20 | 21 | @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) 22 | public KeyLifetimeActionTypeModel() { 23 | } 24 | 25 | public KeyLifetimeActionTypeModel(@NonNull final LifetimeActionType type) { 26 | this(); 27 | this.type = type; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/management/VaultBackupModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.management; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.github.nagyesta.lowkeyvault.model.common.backup.CertificateBackupList; 6 | import com.github.nagyesta.lowkeyvault.model.common.backup.KeyBackupList; 7 | import com.github.nagyesta.lowkeyvault.model.common.backup.SecretBackupList; 8 | import jakarta.validation.Valid; 9 | import jakarta.validation.constraints.NotNull; 10 | import lombok.Data; 11 | 12 | import java.util.Map; 13 | 14 | @Data 15 | @JsonInclude(JsonInclude.Include.NON_NULL) 16 | public class VaultBackupModel { 17 | 18 | @Valid 19 | @NotNull 20 | @JsonProperty("attributes") 21 | private VaultModel attributes; 22 | @Valid 23 | @JsonProperty("keys") 24 | private Map keys; 25 | @Valid 26 | @JsonProperty("secrets") 27 | private Map secrets; 28 | @Valid 29 | @JsonProperty("certificates") 30 | private Map certificates; 31 | } 32 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/certificate/CertificateKeyModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.certificate; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyCurveName; 6 | import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyType; 7 | import jakarta.validation.constraints.Max; 8 | import jakarta.validation.constraints.Min; 9 | import jakarta.validation.constraints.NotNull; 10 | import lombok.Data; 11 | 12 | @Data 13 | public class CertificateKeyModel { 14 | 15 | @JsonProperty("exportable") 16 | private boolean exportable; 17 | @JsonProperty("crv") 18 | @JsonInclude(JsonInclude.Include.NON_NULL) 19 | private KeyCurveName keyCurveName; 20 | @NotNull 21 | @JsonProperty("kty") 22 | private KeyType keyType; 23 | @Min(1024) 24 | @Max(4096) 25 | @JsonInclude(JsonInclude.Include.NON_NULL) 26 | @JsonProperty("key_size") 27 | private Integer keySize = null; 28 | @JsonProperty("reuse_key") 29 | private boolean reuseKey; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/BasePropertiesUpdateModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.github.nagyesta.lowkeyvault.model.json.util.EpochSecondsDeserializer; 6 | import com.github.nagyesta.lowkeyvault.model.json.util.EpochSecondsSerializer; 7 | import lombok.Data; 8 | import tools.jackson.databind.annotation.JsonDeserialize; 9 | import tools.jackson.databind.annotation.JsonSerialize; 10 | 11 | import java.time.OffsetDateTime; 12 | 13 | @Data 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | public class BasePropertiesUpdateModel { 16 | 17 | @JsonProperty("enabled") 18 | private Boolean enabled; 19 | @JsonProperty("exp") 20 | @JsonSerialize(using = EpochSecondsSerializer.class) 21 | @JsonDeserialize(using = EpochSecondsDeserializer.class) 22 | private OffsetDateTime expiresOn; 23 | @JsonProperty("nbf") 24 | @JsonSerialize(using = EpochSecondsSerializer.class) 25 | @JsonDeserialize(using = EpochSecondsDeserializer.class) 26 | private OffsetDateTime notBefore; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/validator/ExpiryPeriodValidator.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.key.validator; 2 | 3 | import com.github.nagyesta.lowkeyvault.service.key.constants.LifetimeActionTriggerType; 4 | import com.github.nagyesta.lowkeyvault.service.key.util.PeriodUtil; 5 | import jakarta.validation.ConstraintValidator; 6 | import jakarta.validation.ConstraintValidatorContext; 7 | 8 | import java.time.Period; 9 | import java.util.Optional; 10 | 11 | public class ExpiryPeriodValidator 12 | implements ConstraintValidator { 13 | 14 | @Override 15 | public void initialize(final ExpiryPeriod constraintAnnotation) { 16 | ConstraintValidator.super.initialize(constraintAnnotation); 17 | } 18 | 19 | @Override 20 | public boolean isValid( 21 | final Period value, 22 | final ConstraintValidatorContext context) { 23 | return Optional.ofNullable(value) 24 | .map(PeriodUtil::asDays) 25 | .map(totalDays -> totalDays >= LifetimeActionTriggerType.MINIMUM_EXPIRY_PERIOD_IN_DAYS) 26 | .orElse(true); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lowkey-vault-docker/src/test/resources/json/backups/jsonBackupEc-521-72.json: -------------------------------------------------------------------------------- 1 | { 2 | "versions": [ 3 | { 4 | "attributes": { 5 | "created": 1649503879, 6 | "enabled": true, 7 | "recoverableDays": 90, 8 | "recoveryLevel": "Recoverable+Purgeable", 9 | "updated": 1649503879 10 | }, 11 | "entityId": "jsonBackupEc-521-72", 12 | "entityVersion": "9301e31d18a540e08c68ad5230e60e21", 13 | "keyMaterial": { 14 | "crv": "P-521", 15 | "d": "AWNmGqYqot0Zq_6uqnqv2lkA40ke1uTJhehp692dS_r2C7oPmJ0qcPgc31jOOFzc-v69chbMawkR-wyKXFf2O4mh", 16 | "key_ops": [ 17 | "sign" 18 | ], 19 | "kid": "https://keys-backup-jsonBackupEc-521-72.localhost:8443/keys/jsonBackupEc-521-72/9301e31d18a540e08c68ad5230e60e21", 20 | "kty": "EC", 21 | "x": "AbDYVOxj0e-tUTgqybst45IXon0axFo_sXlHyJQgYvFDxMNrxa5d4rV7X5H487zh3p_aR_dnVEnbWz9od42mlldE", 22 | "y": "ANk3zxFnwHSgsJr2AZvCHe06Zv7LOQdifHIVaZqx72NOf87DQQoEEMXNp3lTMYANkAqlerz80kjGpoBAe1ZyEtsH" 23 | }, 24 | "managed": false, 25 | "tags": {}, 26 | "vaultBaseUri": "https://keys-backup-jsonBackupEc-521-72.localhost:8443" 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /.github/workflows/gradle-oss-index-scan.yml: -------------------------------------------------------------------------------- 1 | name: "OSS-Index-Scan" 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | # * is a special character in YAML, so we have to quote this string 7 | - cron: "0 7 * * 2,4,6" 8 | 9 | permissions: read-all 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 19 | - name: Validate Gradle wrapper 20 | uses: gradle/actions/wrapper-validation@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0 21 | - name: Set up JDK 25 22 | uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 23 | with: 24 | distribution: temurin 25 | java-version: 25 26 | - name: Setup Gradle 27 | uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0 28 | with: 29 | cache-disabled: true 30 | - name: Check dependencies with Gradle 31 | run: ./gradlew ossIndexAudit -PossIndexUsername=${{ secrets.OSS_INDEX_USER }} -PossIndexPassword=${{ secrets.OSS_INDEX_PASSWORD }} --info 32 | 33 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/request/KeySignParameters.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.key.request; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.SignatureAlgorithm; 7 | import jakarta.validation.constraints.NotBlank; 8 | import jakarta.validation.constraints.NotNull; 9 | import lombok.Data; 10 | 11 | import java.util.Base64; 12 | import java.util.Optional; 13 | 14 | @Data 15 | @JsonInclude(JsonInclude.Include.NON_NULL) 16 | public class KeySignParameters { 17 | 18 | private static final Base64.Decoder DECODER = Base64.getUrlDecoder(); 19 | 20 | @NotNull 21 | @JsonProperty("alg") 22 | private SignatureAlgorithm algorithm; 23 | 24 | @NotNull 25 | @NotBlank 26 | @JsonProperty("value") 27 | private String value; 28 | 29 | @JsonIgnore 30 | public byte[] getValueAsBase64DecodedBytes() { 31 | return Optional.ofNullable(value) 32 | .map(DECODER::decode) 33 | .orElse(null); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/mapper/v7_2/secret/SecretEntityToV72SecretVersionItemModelConverter.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.mapper.v7_2.secret; 2 | 3 | import com.github.nagyesta.lowkeyvault.mapper.common.registry.SecretConverterRegistry; 4 | import com.github.nagyesta.lowkeyvault.service.secret.ReadOnlyKeyVaultSecretEntity; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.lang.NonNull; 7 | 8 | import java.net.URI; 9 | 10 | public class SecretEntityToV72SecretVersionItemModelConverter 11 | extends SecretEntityToV72SecretItemModelConverter { 12 | 13 | @Autowired 14 | public SecretEntityToV72SecretVersionItemModelConverter(@NonNull final SecretConverterRegistry registry) { 15 | super(registry); 16 | } 17 | 18 | @Override 19 | protected void register(final SecretConverterRegistry registry) { 20 | registry.registerVersionedItemConverter(this); 21 | } 22 | 23 | @Override 24 | protected String convertSecretId( 25 | final ReadOnlyKeyVaultSecretEntity source, 26 | final URI vaultUri) { 27 | return source.getId().asUri(vaultUri).toString(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/certificate/CertificateLifetimeActionModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.certificate; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.github.nagyesta.lowkeyvault.model.json.util.CertificateLifetimeActionDeserializer; 5 | import com.github.nagyesta.lowkeyvault.model.json.util.CertificateLifetimeActionSerializer; 6 | import com.github.nagyesta.lowkeyvault.service.certificate.CertificateLifetimeActionActivity; 7 | import jakarta.validation.Valid; 8 | import jakarta.validation.constraints.NotNull; 9 | import lombok.Data; 10 | import tools.jackson.databind.annotation.JsonDeserialize; 11 | import tools.jackson.databind.annotation.JsonSerialize; 12 | 13 | @Data 14 | public class CertificateLifetimeActionModel { 15 | 16 | @Valid 17 | @NotNull 18 | @JsonProperty("trigger") 19 | private CertificateLifetimeActionTriggerModel trigger; 20 | @Valid 21 | @NotNull 22 | @JsonProperty("action") 23 | @JsonSerialize(using = CertificateLifetimeActionSerializer.class) 24 | @JsonDeserialize(using = CertificateLifetimeActionDeserializer.class) 25 | private CertificateLifetimeActionActivity action; 26 | } 27 | -------------------------------------------------------------------------------- /lowkey-vault-docker/src/test/java/com/github/nagyesta/lowkeyvault/sequential/RunSequentialCucumberTest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.sequential; 2 | 3 | import io.cucumber.picocontainer.PicoFactory; 4 | import io.cucumber.testng.AbstractTestNGCucumberTests; 5 | import io.cucumber.testng.CucumberOptions; 6 | import org.testng.annotations.DataProvider; 7 | import org.testng.annotations.Test; 8 | 9 | @CucumberOptions( 10 | glue = {"com.github.nagyesta.lowkeyvault.sequential", 11 | "com.github.nagyesta.lowkeyvault.hook", 12 | "com.github.nagyesta.lowkeyvault.steps", 13 | "com.github.nagyesta.lowkeyvault.context"}, 14 | features = {"classpath:/com/github/nagyesta/lowkeyvault/management"}, 15 | plugin = {"com.github.nagyesta.abortmission.booster.cucumber.AbortMissionPlugin", 16 | "html:build/reports/cucumber/cucumber-sequential-report.html"}, 17 | objectFactory = PicoFactory.class) 18 | @Test(dependsOnGroups = "parallel") 19 | public class RunSequentialCucumberTest extends AbstractTestNGCucumberTests { 20 | 21 | @DataProvider 22 | @Override 23 | public Object[][] scenarios() { 24 | return super.scenarios(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/certificate/CertificateImportRequest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.certificate; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.github.nagyesta.lowkeyvault.model.json.util.Base64CertDeserializer; 5 | import com.github.nagyesta.lowkeyvault.model.json.util.Base64CertSerializer; 6 | import jakarta.validation.Valid; 7 | import jakarta.validation.constraints.NotNull; 8 | import lombok.Data; 9 | import tools.jackson.databind.annotation.JsonDeserialize; 10 | import tools.jackson.databind.annotation.JsonSerialize; 11 | 12 | import java.util.Map; 13 | 14 | @Data 15 | public class CertificateImportRequest { 16 | 17 | @JsonProperty("pwd") 18 | private String password; 19 | @NotNull 20 | @JsonSerialize(using = Base64CertSerializer.class) 21 | @JsonDeserialize(using = Base64CertDeserializer.class) 22 | @JsonProperty("value") 23 | private byte[] certificate; 24 | @Valid 25 | @JsonProperty("attributes") 26 | private CertificatePropertiesModel attributes; 27 | @JsonProperty("policy") 28 | private CertificatePolicyModel policy; 29 | @JsonProperty("tags") 30 | private Map tags; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/certificate/impl/CertAuthorityType.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.certificate.impl; 2 | 3 | import com.github.nagyesta.lowkeyvault.service.certificate.CertificateLifetimeActionActivity; 4 | import lombok.Getter; 5 | 6 | import java.util.Arrays; 7 | 8 | @Getter 9 | public enum CertAuthorityType { 10 | 11 | /** 12 | * Self-signed certificate. 13 | */ 14 | SELF_SIGNED("Self", CertificateLifetimeActionActivity.AUTO_RENEW), 15 | /** 16 | * Unknown, imported certificate. 17 | */ 18 | UNKNOWN("Unknown", CertificateLifetimeActionActivity.EMAIL_CONTACTS); 19 | 20 | private final String value; 21 | private final CertificateLifetimeActionActivity defaultAction; 22 | 23 | CertAuthorityType( 24 | final String value, 25 | final CertificateLifetimeActionActivity action) { 26 | this.value = value; 27 | this.defaultAction = action; 28 | } 29 | 30 | public static CertAuthorityType byValue(final String value) { 31 | return Arrays.stream(values()) 32 | .filter(c -> c.getValue().equals(value)) 33 | .findFirst() 34 | .orElse(UNKNOWN); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/common/backup/KeyBackupModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.common.backup; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.json.util.Base64ZipKeyDeserializer; 4 | import com.github.nagyesta.lowkeyvault.model.json.util.Base64ZipKeySerializer; 5 | import com.github.nagyesta.lowkeyvault.model.v7_2.common.BaseBackupModel; 6 | import com.github.nagyesta.lowkeyvault.model.v7_2.key.KeyPropertiesModel; 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | import lombok.ToString; 10 | import tools.jackson.databind.annotation.JsonDeserialize; 11 | import tools.jackson.databind.annotation.JsonSerialize; 12 | 13 | @Data 14 | @EqualsAndHashCode(callSuper = true) 15 | @ToString(callSuper = true) 16 | public class KeyBackupModel 17 | extends BaseBackupModel { 18 | 19 | @JsonSerialize(using = Base64ZipKeySerializer.class) 20 | @Override 21 | public KeyBackupList getValue() { 22 | return super.getValue(); 23 | } 24 | 25 | @JsonDeserialize(using = Base64ZipKeyDeserializer.class) 26 | @Override 27 | public void setValue(final KeyBackupList value) { 28 | super.setValue(value); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/DefaultKeyRotationPolicy.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.key.impl; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.v7_3.key.constants.LifetimeActionType; 4 | import com.github.nagyesta.lowkeyvault.service.key.KeyLifetimeAction; 5 | import com.github.nagyesta.lowkeyvault.service.key.constants.LifetimeActionTriggerType; 6 | import com.github.nagyesta.lowkeyvault.service.key.id.KeyEntityId; 7 | 8 | import java.time.Period; 9 | import java.util.Map; 10 | 11 | public class DefaultKeyRotationPolicy 12 | extends KeyRotationPolicy { 13 | 14 | private static final KeyLifetimeActionTrigger TRIGGER_30_DAYS_BEFORE_EXPIRY = 15 | new KeyLifetimeActionTrigger(Period.ofDays(30), LifetimeActionTriggerType.TIME_BEFORE_EXPIRY); 16 | private static final KeyLifetimeAction NOTIFY_30_DAYS_BEFORE_EXPIRY 17 | = new KeyLifetimeAction(LifetimeActionType.NOTIFY, TRIGGER_30_DAYS_BEFORE_EXPIRY); 18 | private static final Period PERIOD_1_YEAR = Period.ofYears(1); 19 | 20 | public DefaultKeyRotationPolicy(final KeyEntityId keyEntityId) { 21 | super(keyEntityId, PERIOD_1_YEAR, Map.of(LifetimeActionType.NOTIFY, NOTIFY_30_DAYS_BEFORE_EXPIRY)); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/template/backup/BackupTemplateProcessor.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.template.backup; 2 | 3 | import com.github.jknack.handlebars.Handlebars; 4 | import com.github.nagyesta.lowkeyvault.template.HandlebarsTemplateProcessor; 5 | import lombok.NonNull; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Component; 8 | 9 | import java.io.IOException; 10 | 11 | @Component 12 | public class BackupTemplateProcessor 13 | implements HandlebarsTemplateProcessor { 14 | 15 | private final TimeHelperSource timeHelperSource; 16 | 17 | @Autowired 18 | public BackupTemplateProcessor(final TimeHelperSource timeHelperSource) { 19 | this.timeHelperSource = timeHelperSource; 20 | } 21 | 22 | @Override 23 | public String processTemplate( 24 | @NonNull final String templateAsString, 25 | @NonNull final BackupContext context) throws IOException { 26 | final var handlebars = new Handlebars(); 27 | handlebars.registerHelpers(timeHelperSource); 28 | final var template = handlebars.compileInline(templateAsString); 29 | return template.apply(context); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/certificate/KeyVaultPendingCertificateModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.certificate; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.github.nagyesta.lowkeyvault.model.json.util.Base64CertDeserializer; 5 | import com.github.nagyesta.lowkeyvault.model.json.util.Base64CertSerializer; 6 | import lombok.Data; 7 | import tools.jackson.databind.annotation.JsonDeserialize; 8 | import tools.jackson.databind.annotation.JsonSerialize; 9 | 10 | @Data 11 | public class KeyVaultPendingCertificateModel { 12 | 13 | @JsonProperty("cancellation_requested") 14 | private boolean cancellationRequested; 15 | @JsonProperty("csr") 16 | @JsonDeserialize(using = Base64CertDeserializer.class) 17 | @JsonSerialize(using = Base64CertSerializer.class) 18 | private byte[] csr; 19 | @JsonProperty("id") 20 | private String id; 21 | @JsonProperty("issuer") 22 | private IssuerParameterModel issuer; 23 | @JsonProperty("request_id") 24 | private String requestId; 25 | @JsonProperty("status") 26 | private String status; 27 | @JsonProperty("status_details") 28 | private String statusDetails; 29 | @JsonProperty("target") 30 | private String target; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /lowkey-vault-client/src/test/java/com/github/nagyesta/lowkeyvault/http/management/RecoveryLevelTest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.http.management; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.params.ParameterizedTest; 5 | import org.junit.jupiter.params.provider.Arguments; 6 | import org.junit.jupiter.params.provider.MethodSource; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | import java.util.stream.Collectors; 11 | import java.util.stream.Stream; 12 | 13 | class RecoveryLevelTest { 14 | 15 | public static Stream validProvider() { 16 | final var list = Arrays.stream(RecoveryLevel.values()) 17 | .map(r -> Arguments.of(r.getValue(), r)) 18 | .collect(Collectors.toCollection(ArrayList::new)); 19 | list.add(Arguments.of(null, RecoveryLevel.PURGEABLE)); 20 | return list.stream(); 21 | } 22 | 23 | @ParameterizedTest 24 | @MethodSource("validProvider") 25 | void testForValueShouldReturnExpectedValueWhenCalled(final String input, final RecoveryLevel expected) { 26 | //given 27 | 28 | //when 29 | final var actual = RecoveryLevel.forValue(input); 30 | 31 | //then 32 | Assertions.assertEquals(expected, actual); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/mapper/v7_3/certificate/CertificateEntityToV73CertificateVersionItemModelConverter.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.mapper.v7_3.certificate; 2 | 3 | import com.github.nagyesta.lowkeyvault.mapper.common.registry.CertificateConverterRegistry; 4 | import com.github.nagyesta.lowkeyvault.service.certificate.ReadOnlyKeyVaultCertificateEntity; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.lang.NonNull; 7 | 8 | import java.net.URI; 9 | 10 | public class CertificateEntityToV73CertificateVersionItemModelConverter 11 | extends CertificateEntityToV73CertificateItemModelConverter { 12 | 13 | @Autowired 14 | public CertificateEntityToV73CertificateVersionItemModelConverter(@NonNull final CertificateConverterRegistry registry) { 15 | super(registry); 16 | } 17 | 18 | @Override 19 | protected String convertCertificateId( 20 | final ReadOnlyKeyVaultCertificateEntity source, 21 | final URI vaultUri) { 22 | return source.getId().asUri(vaultUri).toString(); 23 | } 24 | 25 | @Override 26 | protected void register(final CertificateConverterRegistry registry) { 27 | registry.registerVersionedItemConverter(this); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/common/backup/SecretBackupModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.common.backup; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.json.util.Base64ZipSecretDeserializer; 4 | import com.github.nagyesta.lowkeyvault.model.json.util.Base64ZipSecretSerializer; 5 | import com.github.nagyesta.lowkeyvault.model.v7_2.common.BaseBackupModel; 6 | import com.github.nagyesta.lowkeyvault.model.v7_2.secret.SecretPropertiesModel; 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | import lombok.ToString; 10 | import tools.jackson.databind.annotation.JsonDeserialize; 11 | import tools.jackson.databind.annotation.JsonSerialize; 12 | 13 | @Data 14 | @EqualsAndHashCode(callSuper = true) 15 | @ToString(callSuper = true) 16 | public class SecretBackupModel 17 | extends BaseBackupModel { 18 | 19 | @JsonSerialize(using = Base64ZipSecretSerializer.class) 20 | @Override 21 | public SecretBackupList getValue() { 22 | return super.getValue(); 23 | } 24 | 25 | @JsonDeserialize(using = Base64ZipSecretDeserializer.class) 26 | @Override 27 | public void setValue(final SecretBackupList value) { 28 | super.setValue(value); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/constants/EncryptionAlgorithmTest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.key.constants; 2 | 3 | import com.github.nagyesta.lowkeyvault.TestConstants; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.params.ParameterizedTest; 6 | import org.junit.jupiter.params.provider.Arguments; 7 | import org.junit.jupiter.params.provider.MethodSource; 8 | 9 | import java.util.Arrays; 10 | import java.util.stream.Stream; 11 | 12 | class EncryptionAlgorithmTest { 13 | 14 | public static Stream valueProvider() { 15 | final var builder = Stream.builder() 16 | .add(Arguments.of(null, null)) 17 | .add(Arguments.of(TestConstants.EMPTY, null)); 18 | Arrays.stream(EncryptionAlgorithm.values()).forEach(a -> builder.add(Arguments.of(a.getValue(), a))); 19 | return builder.build(); 20 | } 21 | 22 | @ParameterizedTest 23 | @MethodSource("valueProvider") 24 | void forValue(final String inout, final EncryptionAlgorithm expected) { 25 | //given 26 | 27 | //when 28 | final var actual = EncryptionAlgorithm.forValue(inout); 29 | 30 | //then 31 | Assertions.assertEquals(expected, actual); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/common/ReadOnlyVersionedEntityMultiMap.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.common; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.v7_2.common.constants.RecoveryLevel; 4 | import com.github.nagyesta.lowkeyvault.service.EntityId; 5 | 6 | import java.util.Deque; 7 | import java.util.List; 8 | import java.util.Optional; 9 | import java.util.function.Predicate; 10 | 11 | @SuppressWarnings("java:S119") //It is easier to ensure that the types are consistent this way 12 | public interface ReadOnlyVersionedEntityMultiMap> { 13 | 14 | List listLatestEntities(); 15 | 16 | List listLatestNonManagedEntities(); 17 | 18 | Deque getVersions(K entityId); 19 | 20 | boolean containsName(String name); 21 | 22 | boolean containsEntityMatching(String name, Predicate predicate); 23 | 24 | boolean containsEntity(K entityId); 25 | 26 | void assertContainsEntity(V entityId); 27 | 28 | V getLatestVersionOfEntity(K entityId); 29 | 30 | R getEntity(V entityId, Class type); 31 | 32 | RE getReadOnlyEntity(V entityId); 33 | 34 | RecoveryLevel getRecoveryLevel(); 35 | 36 | Optional getRecoverableDays(); 37 | } 38 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/mapper/v7_2/key/AesJsonWebKeyImportRequestConverter.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.mapper.v7_2.key; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.JsonWebKeyImportRequest; 4 | import com.github.nagyesta.lowkeyvault.service.exception.CryptoException; 5 | import org.springframework.lang.NonNull; 6 | 7 | import javax.crypto.SecretKey; 8 | import javax.crypto.spec.SecretKeySpec; 9 | 10 | /** 11 | * Converts import requests to AES key pairs. 12 | */ 13 | public class AesJsonWebKeyImportRequestConverter 14 | extends BaseJsonWebKeyImportRequestConverter { 15 | 16 | private static final int AES_BYTES_TO_KEY_SIZE_BITS_MULTIPLIER = 8; 17 | 18 | @NonNull 19 | @Override 20 | public SecretKey convert(@NonNull final JsonWebKeyImportRequest source) { 21 | try { 22 | return new SecretKeySpec(source.getK(), source.getKeyType().getAlgorithmName()); 23 | } catch (final Exception e) { 24 | throw new CryptoException(e.getMessage(), e); 25 | } 26 | } 27 | 28 | @Override 29 | public Integer getKeyParameter(@NonNull final JsonWebKeyImportRequest source) { 30 | return source.getK().length * AES_BYTES_TO_KEY_SIZE_BITS_MULTIPLIER; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /lowkey-vault-client/src/main/java/com/github/nagyesta/lowkeyvault/http/management/VaultModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.http.management; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 5 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | import java.net.URI; 11 | import java.time.OffsetDateTime; 12 | import java.util.Set; 13 | 14 | @Data 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | public class VaultModel { 18 | 19 | @JsonProperty("baseUri") 20 | private URI baseUri; 21 | @JsonProperty("aliases") 22 | private Set aliases; 23 | @JsonProperty("recoveryLevel") 24 | private RecoveryLevel recoveryLevel; 25 | @JsonProperty("recoverableDays") 26 | private Integer recoverableDays; 27 | @JsonProperty("created") 28 | @JsonSerialize(using = EpochSecondsSerializer.class) 29 | @JsonDeserialize(using = EpochSecondsDeserializer.class) 30 | private OffsetDateTime createdOn; 31 | @JsonProperty("deleted") 32 | @JsonSerialize(using = EpochSecondsSerializer.class) 33 | @JsonDeserialize(using = EpochSecondsDeserializer.class) 34 | private OffsetDateTime deletedOn; 35 | } 36 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/template/backup/VaultImporterProperties.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.template.backup; 2 | 3 | import lombok.Data; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.stereotype.Component; 6 | 7 | import java.io.File; 8 | 9 | @Data 10 | @Component 11 | public class VaultImporterProperties { 12 | 13 | private final File importFile; 14 | private final String importTemplateHost; 15 | private final int importTemplatePort; 16 | 17 | public VaultImporterProperties( 18 | @Value("${LOWKEY_IMPORT_LOCATION}") final File importFile, 19 | @Value("${LOWKEY_IMPORT_TEMPLATE_HOST:localhost}") final String importTemplateHost, 20 | @Value("${LOWKEY_IMPORT_TEMPLATE_PORT:${server.port}}") final int importTemplatePort) { 21 | this.importFile = importFile; 22 | this.importTemplateHost = importTemplateHost; 23 | this.importTemplatePort = importTemplatePort; 24 | } 25 | 26 | public BackupContext context() { 27 | return new BackupContext(importTemplateHost, importTemplatePort); 28 | } 29 | 30 | public boolean importFileExists() { 31 | return importFile != null && importFile.exists() && importFile.isFile() && importFile.canRead(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/key/impl/KeyLifetimeActionTrigger.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.key.impl; 2 | 3 | import com.github.nagyesta.lowkeyvault.service.key.LifetimeActionTrigger; 4 | import com.github.nagyesta.lowkeyvault.service.key.constants.LifetimeActionTriggerType; 5 | import com.github.nagyesta.lowkeyvault.service.key.util.PeriodUtil; 6 | import lombok.NonNull; 7 | 8 | import java.time.OffsetDateTime; 9 | import java.time.Period; 10 | 11 | public record KeyLifetimeActionTrigger( 12 | @NonNull Period timePeriod, 13 | @NonNull LifetimeActionTriggerType triggerType) implements LifetimeActionTrigger { 14 | 15 | @Override 16 | public boolean shouldTrigger( 17 | final OffsetDateTime created, 18 | final OffsetDateTime expiry) { 19 | return triggerType.shouldTrigger(created, expiry, timePeriod); 20 | } 21 | 22 | @Override 23 | public long rotateAfterDays(@NonNull final Period expiryPeriod) { 24 | final long days; 25 | if (triggerType == LifetimeActionTriggerType.TIME_AFTER_CREATE) { 26 | days = PeriodUtil.asDays(timePeriod); 27 | } else { 28 | days = PeriodUtil.asDays(expiryPeriod) - PeriodUtil.asDays(timePeriod); 29 | } 30 | return days; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/constants/KeyCurveNameTest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.key.constants; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.params.ParameterizedTest; 5 | import org.junit.jupiter.params.provider.Arguments; 6 | import org.junit.jupiter.params.provider.MethodSource; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | import java.util.List; 11 | import java.util.Optional; 12 | import java.util.stream.Stream; 13 | 14 | class KeyCurveNameTest { 15 | 16 | public static Stream valueProvider() { 17 | final List list = new ArrayList<>(); 18 | list.add(null); 19 | list.addAll(Arrays.asList(KeyCurveName.values())); 20 | return list.stream() 21 | .map(value -> Arguments.of(Optional.ofNullable(value).map(KeyCurveName::getValue).orElse(null), value)); 22 | } 23 | 24 | @ParameterizedTest 25 | @MethodSource("valueProvider") 26 | void testForValueShouldReturnEnumWhenValueStringMatches(final String input, final KeyCurveName expected) { 27 | //given 28 | 29 | //when 30 | final var actual = KeyCurveName.forValue(input); 31 | 32 | //then 33 | Assertions.assertEquals(expected, actual); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/constants/KeyOperationTest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.key.constants; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.params.ParameterizedTest; 5 | import org.junit.jupiter.params.provider.Arguments; 6 | import org.junit.jupiter.params.provider.MethodSource; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | import java.util.List; 11 | import java.util.Optional; 12 | import java.util.stream.Stream; 13 | 14 | class KeyOperationTest { 15 | 16 | public static Stream valueProvider() { 17 | final List list = new ArrayList<>(); 18 | list.add(null); 19 | list.addAll(Arrays.asList(KeyOperation.values())); 20 | return list.stream() 21 | .map(value -> Arguments.of(Optional.ofNullable(value).map(KeyOperation::getValue).orElse(null), value)); 22 | } 23 | 24 | @ParameterizedTest 25 | @MethodSource("valueProvider") 26 | void testForValueShouldReturnEnumWhenValueStringMatches(final String input, final KeyOperation expected) { 27 | //given 28 | 29 | //when 30 | final var actual = KeyOperation.forValue(input); 31 | 32 | //then 33 | Assertions.assertEquals(expected, actual); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/common/backup/KeyBackupList.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.common.backup; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.github.nagyesta.lowkeyvault.model.v7_2.key.BackupListContainer; 6 | import com.github.nagyesta.lowkeyvault.model.v7_3.key.KeyRotationPolicyModel; 7 | import jakarta.validation.Valid; 8 | import jakarta.validation.constraints.NotNull; 9 | import jakarta.validation.constraints.Size; 10 | import lombok.EqualsAndHashCode; 11 | import lombok.Getter; 12 | import lombok.Setter; 13 | import lombok.ToString; 14 | 15 | import java.util.List; 16 | 17 | @Getter 18 | @EqualsAndHashCode 19 | @ToString 20 | public class KeyBackupList 21 | implements BackupListContainer { 22 | 23 | @Valid 24 | @NotNull 25 | @Size(min = 1) 26 | @JsonProperty("versions") 27 | private List versions = List.of(); 28 | 29 | @Setter 30 | @Valid 31 | @JsonProperty("rotationPolicy") 32 | @JsonInclude(JsonInclude.Include.NON_NULL) 33 | private KeyRotationPolicyModel keyRotationPolicy; 34 | 35 | public void setVersions(final List versions) { 36 | this.versions = List.copyOf(versions); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/common/BaseBackupListItem.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.common; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.github.nagyesta.lowkeyvault.model.v7_2.BasePropertiesModel; 6 | import jakarta.validation.Valid; 7 | import jakarta.validation.constraints.NotBlank; 8 | import jakarta.validation.constraints.NotNull; 9 | import lombok.Data; 10 | 11 | import java.net.URI; 12 | import java.util.Map; 13 | 14 | /** 15 | * Base list item of backup models. 16 | * 17 | * @param

The type of the properties model. 18 | */ 19 | @Data 20 | @JsonInclude(JsonInclude.Include.NON_NULL) 21 | public class BaseBackupListItem

{ 22 | 23 | @NotNull 24 | @JsonProperty("vaultBaseUri") 25 | private URI vaultBaseUri; 26 | @NotNull 27 | @NotBlank 28 | @JsonProperty("entityId") 29 | private String id; 30 | @NotNull 31 | @NotBlank 32 | @JsonProperty("entityVersion") 33 | private String version; 34 | @Valid 35 | @NotNull 36 | @JsonProperty("attributes") 37 | private P attributes; 38 | @JsonProperty("tags") 39 | private Map tags; 40 | @JsonProperty("managed") 41 | private boolean managed; 42 | } 43 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/common/backup/CertificateBackupModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.common.backup; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.json.util.Base64ZipCertificateDeserializer; 4 | import com.github.nagyesta.lowkeyvault.model.json.util.Base64ZipCertificateSerializer; 5 | import com.github.nagyesta.lowkeyvault.model.v7_2.common.BaseBackupModel; 6 | import com.github.nagyesta.lowkeyvault.model.v7_3.certificate.CertificatePropertiesModel; 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | import lombok.ToString; 10 | import tools.jackson.databind.annotation.JsonDeserialize; 11 | import tools.jackson.databind.annotation.JsonSerialize; 12 | 13 | @Data 14 | @EqualsAndHashCode(callSuper = true) 15 | @ToString(callSuper = true) 16 | public class CertificateBackupModel 17 | extends BaseBackupModel { 18 | 19 | @JsonSerialize(using = Base64ZipCertificateSerializer.class) 20 | @Override 21 | public CertificateBackupList getValue() { 22 | return super.getValue(); 23 | } 24 | 25 | @JsonDeserialize(using = Base64ZipCertificateDeserializer.class) 26 | @Override 27 | public void setValue(final CertificateBackupList value) { 28 | super.setValue(value); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/vault/VaultFake.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.vault; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.v7_2.common.constants.RecoveryLevel; 4 | import com.github.nagyesta.lowkeyvault.service.certificate.CertificateVaultFake; 5 | import com.github.nagyesta.lowkeyvault.service.key.KeyVaultFake; 6 | import com.github.nagyesta.lowkeyvault.service.secret.SecretVaultFake; 7 | 8 | import java.net.URI; 9 | import java.time.OffsetDateTime; 10 | import java.util.Set; 11 | import java.util.function.UnaryOperator; 12 | 13 | public interface VaultFake { 14 | 15 | boolean matches(URI vaultUri, UnaryOperator uriMapper); 16 | 17 | URI baseUri(); 18 | 19 | Set aliases(); 20 | 21 | void setAliases(Set aliases); 22 | KeyVaultFake keyVaultFake(); 23 | 24 | SecretVaultFake secretVaultFake(); 25 | 26 | CertificateVaultFake certificateVaultFake(); 27 | 28 | RecoveryLevel getRecoveryLevel(); 29 | 30 | Integer getRecoverableDays(); 31 | 32 | OffsetDateTime getCreatedOn(); 33 | 34 | OffsetDateTime getDeletedOn(); 35 | 36 | boolean isDeleted(); 37 | 38 | boolean isActive(); 39 | 40 | boolean isExpired(); 41 | 42 | void delete(); 43 | 44 | void recover(); 45 | 46 | void timeShift(int offsetSeconds, boolean regenerateCertificates); 47 | } 48 | -------------------------------------------------------------------------------- /lowkey-vault-client/src/main/java/com/github/nagyesta/lowkeyvault/http/management/LowkeyVaultManagementClient.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.http.management; 2 | 3 | import lombok.NonNull; 4 | 5 | import java.io.IOException; 6 | import java.net.URI; 7 | import java.util.List; 8 | import java.util.function.Supplier; 9 | 10 | public interface LowkeyVaultManagementClient { 11 | 12 | void verifyConnectivity( 13 | int retries, 14 | int waitMillis, Supplier exceptionProvider) throws T, InterruptedException; 15 | 16 | VaultModel createVault( 17 | @NonNull URI baseUri, 18 | @NonNull RecoveryLevel recoveryLevel, 19 | @NonNull Integer recoverableDays); 20 | 21 | List listVaults(); 22 | 23 | List listDeletedVaults(); 24 | 25 | boolean delete(@NonNull URI baseUri); 26 | 27 | VaultModel recover(@NonNull URI baseUri); 28 | 29 | VaultModel addAlias(@NonNull URI baseUri, @NonNull URI alias); 30 | 31 | VaultModel removeAlias(@NonNull URI baseUri, @NonNull URI alias); 32 | 33 | boolean purge(@NonNull URI baseUri); 34 | 35 | void timeShift(@NonNull TimeShiftContext context); 36 | 37 | String exportActive(); 38 | 39 | String unpackBackup(byte[] backup) throws IOException; 40 | 41 | byte[] compressBackup(String backup) throws IOException; 42 | } 43 | -------------------------------------------------------------------------------- /lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/secrets/ListSecrets.feature: -------------------------------------------------------------------------------- 1 | Feature: Secret list 2 | 3 | @Secret @SecretCreate @SecretList @CreateVault 4 | Scenario Outline: SECRET_LIST_01 Secrets are created with the secret client then all are listed 5 | Given secret API version is used 6 | And a vault is created with name secrets-list- 7 | And a secret client is created with the vault named secrets-list- 8 | And secrets with - prefix are created valued abc123 9 | When the secret properties are listed 10 | Then the listed secrets are matching the ones created 11 | And the list of secrets should contain 0 managed items 12 | 13 | Examples: 14 | | api | index | count | secretName | 15 | | 7.2 | 01 | 1 | listSecret | 16 | | 7.3 | 02 | 1 | listSecret | 17 | | 7.3 | 03 | 2 | list-secret-name | 18 | | 7.3 | 04 | 3 | listSecret | 19 | | 7.3 | 05 | 5 | list-secret-name | 20 | | 7.3 | 06 | 25 | listSecret | 21 | | 7.3 | 07 | 42 | list-secret-name | 22 | | 7.4 | 08 | 5 | list-secret-name | 23 | | 7.5 | 09 | 5 | list-secret-name | 24 | | 7.6 | 10 | 5 | list-secret-name | 25 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/mapper/common/VaultFakeToVaultModelConverter.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.mapper.common; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.management.VaultModel; 4 | import com.github.nagyesta.lowkeyvault.service.vault.VaultFake; 5 | import org.springframework.core.convert.converter.Converter; 6 | import org.springframework.lang.NonNull; 7 | import org.springframework.lang.Nullable; 8 | import org.springframework.stereotype.Component; 9 | 10 | import java.util.Optional; 11 | 12 | @Component 13 | public class VaultFakeToVaultModelConverter 14 | implements Converter { 15 | 16 | @Override 17 | public VaultModel convert(@Nullable final VaultFake source) { 18 | return Optional.ofNullable(source) 19 | .map(this::convertNonNull) 20 | .orElse(null); 21 | } 22 | 23 | @NonNull 24 | public VaultModel convertNonNull(@NonNull final VaultFake fake) { 25 | final var model = new VaultModel(); 26 | model.setBaseUri(fake.baseUri()); 27 | model.setAliases(fake.aliases()); 28 | model.setRecoveryLevel(fake.getRecoveryLevel()); 29 | model.setRecoverableDays(fake.getRecoverableDays()); 30 | model.setCreatedOn(fake.getCreatedOn()); 31 | model.setDeletedOn(fake.getDeletedOn()); 32 | return model; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/certificate/CertificatePolicyModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.certificate; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import jakarta.validation.Valid; 6 | import jakarta.validation.constraints.NotNull; 7 | import jakarta.validation.constraints.Size; 8 | import lombok.Data; 9 | 10 | import java.util.List; 11 | 12 | @Data 13 | public class CertificatePolicyModel { 14 | 15 | @JsonProperty("id") 16 | private String id; 17 | @Valid 18 | @NotNull 19 | @JsonProperty("key_props") 20 | private CertificateKeyModel keyProperties; 21 | @Valid 22 | @NotNull 23 | @JsonProperty("secret_props") 24 | private CertificateSecretModel secretProperties; 25 | @Valid 26 | @NotNull 27 | @JsonProperty("x509_props") 28 | private X509CertificateModel x509Properties; 29 | @Valid 30 | @NotNull 31 | @JsonProperty("issuer") 32 | private IssuerParameterModel issuer; 33 | @Valid 34 | @JsonProperty("attributes") 35 | private CertificatePropertiesModel attributes; 36 | @Valid 37 | @Size(max = 1) //only one can be set on the UI 38 | @JsonProperty("lifetime_actions") 39 | @JsonInclude(JsonInclude.Include.NON_NULL) 40 | private List lifetimeActions; 41 | } 42 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/service/certificate/impl/CertificateAlgorithm.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.service.certificate.impl; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyType; 4 | import lombok.Getter; 5 | import lombok.NonNull; 6 | 7 | import java.util.Arrays; 8 | import java.util.Set; 9 | 10 | public enum CertificateAlgorithm { 11 | 12 | /** 13 | * Certificate signed using RSA algorithm. 14 | */ 15 | RSA("SHA256withRSA", Set.of(KeyType.RSA, KeyType.RSA_HSM)), 16 | /** 17 | * Certificate signed using EC algorithm. 18 | */ 19 | EC("SHA256withECDSA", Set.of(KeyType.EC, KeyType.EC_HSM)); 20 | 21 | @Getter 22 | private final String algorithm; 23 | private final Set keyTypes; 24 | 25 | CertificateAlgorithm( 26 | final String algorithm, 27 | final Set keyTypes) { 28 | this.algorithm = algorithm; 29 | this.keyTypes = keyTypes; 30 | } 31 | 32 | public static CertificateAlgorithm forKeyType(@NonNull final KeyType keyType) { 33 | final var value = Arrays.stream(CertificateAlgorithm.values()) 34 | .filter(v -> v.keyTypes.contains(keyType)) 35 | .findFirst(); 36 | return value.orElseThrow(() -> new IllegalArgumentException("Unable to find certificate algorithm for key type: " + keyType)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/DeletedKeyVaultKeyModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.key; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.github.nagyesta.lowkeyvault.model.common.DeletedModel; 6 | import com.github.nagyesta.lowkeyvault.model.json.util.EpochSecondsDeserializer; 7 | import com.github.nagyesta.lowkeyvault.model.json.util.EpochSecondsSerializer; 8 | import lombok.Data; 9 | import lombok.EqualsAndHashCode; 10 | import tools.jackson.databind.annotation.JsonDeserialize; 11 | import tools.jackson.databind.annotation.JsonSerialize; 12 | 13 | import java.time.OffsetDateTime; 14 | 15 | @Data 16 | @EqualsAndHashCode(callSuper = true) 17 | @JsonInclude(JsonInclude.Include.NON_NULL) 18 | public class DeletedKeyVaultKeyModel 19 | extends KeyVaultKeyModel implements DeletedModel { 20 | 21 | @JsonProperty("recoveryId") 22 | private String recoveryId; 23 | @JsonProperty("deletedDate") 24 | @JsonSerialize(using = EpochSecondsSerializer.class) 25 | @JsonDeserialize(using = EpochSecondsDeserializer.class) 26 | private OffsetDateTime deletedDate; 27 | @JsonProperty("scheduledPurgeDate") 28 | @JsonSerialize(using = EpochSecondsSerializer.class) 29 | @JsonDeserialize(using = EpochSecondsDeserializer.class) 30 | private OffsetDateTime scheduledPurgeDate; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/constants/LifetimeActionTypeTest.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.key.constants; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.params.ParameterizedTest; 5 | import org.junit.jupiter.params.provider.Arguments; 6 | import org.junit.jupiter.params.provider.MethodSource; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | import java.util.List; 11 | import java.util.Optional; 12 | import java.util.stream.Stream; 13 | 14 | class LifetimeActionTypeTest { 15 | 16 | public static Stream valueProvider() { 17 | final List list = new ArrayList<>(); 18 | list.add(null); 19 | list.addAll(Arrays.asList(LifetimeActionType.values())); 20 | return list.stream() 21 | .map(value -> Arguments.of( 22 | Optional.ofNullable(value).map(LifetimeActionType::getValue).orElse("unknown"), 23 | value)); 24 | } 25 | 26 | @ParameterizedTest 27 | @MethodSource("valueProvider") 28 | void testForValueShouldReturnEnumWhenValueStringMatches(final String input, final LifetimeActionType expected) { 29 | //given 30 | 31 | //when 32 | final var actual = LifetimeActionType.forValue(input); 33 | 34 | //then 35 | Assertions.assertEquals(expected, actual); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lowkey-vault-client/src/main/java/com/github/nagyesta/lowkeyvault/http/management/impl/UriUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.http.management.impl; 2 | 3 | import com.github.nagyesta.lowkeyvault.http.management.LowkeyVaultException; 4 | import lombok.NonNull; 5 | import org.apache.http.client.utils.URIBuilder; 6 | 7 | import java.net.URI; 8 | import java.net.URISyntaxException; 9 | import java.util.Collections; 10 | import java.util.Map; 11 | import java.util.Objects; 12 | 13 | public final class UriUtil { 14 | 15 | private UriUtil() { 16 | throw new IllegalCallerException("Utility cannot be instantiated."); 17 | } 18 | 19 | public static URI uriBuilderForPath( 20 | final String baseUrl, 21 | final String path) { 22 | return uriBuilderForPath(baseUrl, path, Map.of()); 23 | } 24 | 25 | public static URI uriBuilderForPath( 26 | @NonNull final String baseUrl, 27 | @NonNull final String path, 28 | final Map parameters) { 29 | try { 30 | final var builder = new URIBuilder(baseUrl).setPath(path); 31 | Objects.requireNonNullElse(parameters, Collections.emptyMap()).forEach(builder::addParameter); 32 | return builder.build(); 33 | } catch (final URISyntaxException e) { 34 | throw new LowkeyVaultException("Unable to parse baseUrl.", e); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/CertificateLifetimeActionDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.json.util; 2 | 3 | import com.github.nagyesta.lowkeyvault.service.certificate.CertificateLifetimeActionActivity; 4 | import org.springframework.util.Assert; 5 | import tools.jackson.core.JsonParser; 6 | import tools.jackson.databind.DeserializationContext; 7 | import tools.jackson.databind.ValueDeserializer; 8 | 9 | public class CertificateLifetimeActionDeserializer 10 | extends ValueDeserializer { 11 | 12 | private static final String INNER_NODE_NAME = "action_type"; 13 | 14 | @Override 15 | public CertificateLifetimeActionActivity deserialize( 16 | final JsonParser parser, 17 | final DeserializationContext context) { 18 | final var node = parser.readValueAsTree(); 19 | Assert.isTrue(node.isObject(), "The \"action\" node must represent an object."); 20 | final var actionType = node.path(INNER_NODE_NAME); 21 | Assert.isTrue(actionType.isValueNode(), 22 | "The \"action\" node must have an \"" + INNER_NODE_NAME + "\" child containing the value."); 23 | try (var textField = actionType.traverse(context)) { 24 | final var value = textField.nextStringValue(); 25 | return CertificateLifetimeActionActivity.byValue(value); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/key/DeletedKeyVaultKeyItemModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.key; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.github.nagyesta.lowkeyvault.model.common.DeletedModel; 6 | import com.github.nagyesta.lowkeyvault.model.json.util.EpochSecondsDeserializer; 7 | import com.github.nagyesta.lowkeyvault.model.json.util.EpochSecondsSerializer; 8 | import lombok.Data; 9 | import lombok.EqualsAndHashCode; 10 | import tools.jackson.databind.annotation.JsonDeserialize; 11 | import tools.jackson.databind.annotation.JsonSerialize; 12 | 13 | import java.time.OffsetDateTime; 14 | 15 | @Data 16 | @EqualsAndHashCode(callSuper = true) 17 | @JsonInclude(JsonInclude.Include.NON_NULL) 18 | public class DeletedKeyVaultKeyItemModel 19 | extends KeyVaultKeyItemModel implements DeletedModel { 20 | 21 | @JsonProperty("recoveryId") 22 | private String recoveryId; 23 | @JsonProperty("deletedDate") 24 | @JsonSerialize(using = EpochSecondsSerializer.class) 25 | @JsonDeserialize(using = EpochSecondsDeserializer.class) 26 | private OffsetDateTime deletedDate; 27 | @JsonProperty("scheduledPurgeDate") 28 | @JsonSerialize(using = EpochSecondsSerializer.class) 29 | @JsonDeserialize(using = EpochSecondsDeserializer.class) 30 | private OffsetDateTime scheduledPurgeDate; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/secret/DeletedKeyVaultSecretModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.secret; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.github.nagyesta.lowkeyvault.model.common.DeletedModel; 6 | import com.github.nagyesta.lowkeyvault.model.json.util.EpochSecondsDeserializer; 7 | import com.github.nagyesta.lowkeyvault.model.json.util.EpochSecondsSerializer; 8 | import lombok.Data; 9 | import lombok.EqualsAndHashCode; 10 | import tools.jackson.databind.annotation.JsonDeserialize; 11 | import tools.jackson.databind.annotation.JsonSerialize; 12 | 13 | import java.time.OffsetDateTime; 14 | 15 | @Data 16 | @EqualsAndHashCode(callSuper = true) 17 | @JsonInclude(JsonInclude.Include.NON_NULL) 18 | public class DeletedKeyVaultSecretModel 19 | extends KeyVaultSecretModel implements DeletedModel { 20 | 21 | @JsonProperty("recoveryId") 22 | private String recoveryId; 23 | @JsonProperty("deletedDate") 24 | @JsonSerialize(using = EpochSecondsSerializer.class) 25 | @JsonDeserialize(using = EpochSecondsDeserializer.class) 26 | private OffsetDateTime deletedDate; 27 | @JsonProperty("scheduledPurgeDate") 28 | @JsonSerialize(using = EpochSecondsSerializer.class) 29 | @JsonDeserialize(using = EpochSecondsDeserializer.class) 30 | private OffsetDateTime scheduledPurgeDate; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/key/import/rsa-import-missing-d.json: -------------------------------------------------------------------------------- 1 | { 2 | "key": { 3 | "kty": "RSA", 4 | "n": "nKAwarTrOpzd1hhH4cQNdVTgRF-b0ubPD8ZNVf0UXjb62QuAk3Dn68ESThcF7SoDYRx2QVcfoMC9WCcuQUQDieJF-lvJTSer1TwH72NBovwKlHvrXqEI0a6_uVYY5n-soGt7qFZNbwQLdWWA6PrbqTLIkv6r01dcuhTiQQAn6OWEa0JbFvWfF1kILQIaSBBBaaQ4R7hZs7-VQTHGD7J1xGteof4gw2VTiwNdcE8p5UG5b6S9KQwAeET4yB4KFPwQ3TDdzxJQ89mwYVi_sgAIggN54hTq4oEKYJHBOMtFGIN0_HQ60ZSUnpOi87xNC-8VFqnv4rfTQ7nkK6XMvjMVfw", 5 | "e": "AQAB", 6 | "dp": "ZGnmWx-Nca71z9a9vvT4g02iv3S-3kSgmhl8JST09YQwK8tfiK7nXnNMtXJi2K4dLKKnLicGtCzB6W3mXdLcP2SUOWDOeStoBt8HEBT4MrI1psCKqnBum78WkHju90rBFj99amkP6UeQy5EASAzgmKQu2nUaUnRV0lYP8LHMCkE", 7 | "dq": "dtpke0foFs04hPS6XYLA5lc7-1MAHfZKN4CkMAofwDqPmRQzCxpDJUk0gMWGJEdU_Lqfbg22Py44cci0dczH36NW3UU5BL86T2_SPPDOuyX7kDscrIJCdowxQCGJHGRBEozM_uTL46wu6UnUIv7m7cuGgodJyZBcdwpo6ziFink", 8 | "qi": "Y9KD5GaHkAYmAqpOfAQUMr71QuAAaBb0APzMuUvoEYw39PD3_vJeh9HZ15QmJ8zCX10-nlzUB-bWwvK-rGcJXbK4pArilr5MiaYv7e8h5eW2zs2_itDJ6Oebi-wVbMhg7DvUTBbkCvPhhIedE4UlDQmMYP7RhzVVs7SfmkGs_DQ", 9 | "p": "v1jeCPnuJQM2PW2690Q9KJk0Ulok8VFGjkcHUHVi3orKdy7y_TCIWM6ZGvgFzI6abinzYbTEPKV4wFdMAwvOWmawXj5YrsoeB44_HXJ0ak_5_iP6XXR8MLGXbd0ZqsxvAZyzMj9vyle7EN2cBod6aenI2QZoRDucPvjPwZsZotk", 10 | "q": "0Yv-Dj6qnvx_LL70lUnKA6MgHE_bUC4drl5ZNDDsUdUUYfxIK4G1rGU45kHGtp-Qg-Uyf9s52ywLylhcVE3jfbjOgEozlSwKyhqfXkLpMLWHqOKj9fcfYd4PWKPOgpzWsqjA6fJbBUMYo0CU2G9cWCtVodO7sBJVSIZunWrAlBc" 11 | }, 12 | "tags": { 13 | "purpose": "unit test" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/test/resources/key/import/rsa-import-missing-n.json: -------------------------------------------------------------------------------- 1 | { 2 | "key": { 3 | "kty": "RSA", 4 | "e": "AQAB", 5 | "d": "GeT1_D5LAZa7qlC7WZ0DKJnOth8kcPrN0urTEFtWCbmHQWkAad_px_VUpGp0BWDDzENbXbQcu4QCCdf4crve5eXt8dVI86OSah-RpEdBq8OFsETIhg2Tmq8MbYTJexoynRcIC62xAaCmkFMmu931gQSvWnYWTEuOPgmD2oE_F-bP9TFlGRc69a6MSbtcSRyFTsd5KsUr40QS4zf2W4kZCOWejyLuxk88SXgUqcJx86Ulc1Ol1KkTBLadvReAZCyCMwKBlNRGw46BU_iK0vK7rTD9fmEd639Gjti6eLpnyQYpnVe8uGgwVU1fHBkAKyapWoEG6VMhMntcrvgukKLIsQ", 6 | "dp": "ZGnmWx-Nca71z9a9vvT4g02iv3S-3kSgmhl8JST09YQwK8tfiK7nXnNMtXJi2K4dLKKnLicGtCzB6W3mXdLcP2SUOWDOeStoBt8HEBT4MrI1psCKqnBum78WkHju90rBFj99amkP6UeQy5EASAzgmKQu2nUaUnRV0lYP8LHMCkE", 7 | "dq": "dtpke0foFs04hPS6XYLA5lc7-1MAHfZKN4CkMAofwDqPmRQzCxpDJUk0gMWGJEdU_Lqfbg22Py44cci0dczH36NW3UU5BL86T2_SPPDOuyX7kDscrIJCdowxQCGJHGRBEozM_uTL46wu6UnUIv7m7cuGgodJyZBcdwpo6ziFink", 8 | "qi": "Y9KD5GaHkAYmAqpOfAQUMr71QuAAaBb0APzMuUvoEYw39PD3_vJeh9HZ15QmJ8zCX10-nlzUB-bWwvK-rGcJXbK4pArilr5MiaYv7e8h5eW2zs2_itDJ6Oebi-wVbMhg7DvUTBbkCvPhhIedE4UlDQmMYP7RhzVVs7SfmkGs_DQ", 9 | "p": "v1jeCPnuJQM2PW2690Q9KJk0Ulok8VFGjkcHUHVi3orKdy7y_TCIWM6ZGvgFzI6abinzYbTEPKV4wFdMAwvOWmawXj5YrsoeB44_HXJ0ak_5_iP6XXR8MLGXbd0ZqsxvAZyzMj9vyle7EN2cBod6aenI2QZoRDucPvjPwZsZotk", 10 | "q": "0Yv-Dj6qnvx_LL70lUnKA6MgHE_bUC4drl5ZNDDsUdUUYfxIK4G1rGU45kHGtp-Qg-Uyf9s52ywLylhcVE3jfbjOgEozlSwKyhqfXkLpMLWHqOKj9fcfYd4PWKPOgpzWsqjA6fJbBUMYo0CU2G9cWCtVodO7sBJVSIZunWrAlBc" 11 | }, 12 | "tags": { 13 | "purpose": "unit test" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_2/secret/DeletedKeyVaultSecretItemModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_2.secret; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.github.nagyesta.lowkeyvault.model.common.DeletedModel; 6 | import com.github.nagyesta.lowkeyvault.model.json.util.EpochSecondsDeserializer; 7 | import com.github.nagyesta.lowkeyvault.model.json.util.EpochSecondsSerializer; 8 | import lombok.Data; 9 | import lombok.EqualsAndHashCode; 10 | import tools.jackson.databind.annotation.JsonDeserialize; 11 | import tools.jackson.databind.annotation.JsonSerialize; 12 | 13 | import java.time.OffsetDateTime; 14 | 15 | @Data 16 | @EqualsAndHashCode(callSuper = true) 17 | @JsonInclude(JsonInclude.Include.NON_NULL) 18 | public class DeletedKeyVaultSecretItemModel 19 | extends KeyVaultSecretItemModel implements DeletedModel { 20 | 21 | @JsonProperty("recoveryId") 22 | private String recoveryId; 23 | @JsonProperty("deletedDate") 24 | @JsonSerialize(using = EpochSecondsSerializer.class) 25 | @JsonDeserialize(using = EpochSecondsDeserializer.class) 26 | private OffsetDateTime deletedDate; 27 | @JsonProperty("scheduledPurgeDate") 28 | @JsonSerialize(using = EpochSecondsSerializer.class) 29 | @JsonDeserialize(using = EpochSecondsDeserializer.class) 30 | private OffsetDateTime scheduledPurgeDate; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/key/KeyRotationPolicyModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.key; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.fasterxml.jackson.annotation.JsonPropertyOrder; 6 | import com.github.nagyesta.lowkeyvault.model.v7_3.key.validator.Restore; 7 | import com.github.nagyesta.lowkeyvault.model.v7_3.key.validator.Update; 8 | import com.github.nagyesta.lowkeyvault.service.key.id.KeyEntityId; 9 | import jakarta.validation.Valid; 10 | import jakarta.validation.constraints.NotNull; 11 | import jakarta.validation.constraints.Size; 12 | import lombok.Data; 13 | 14 | import java.net.URI; 15 | import java.util.List; 16 | 17 | @Data 18 | @JsonPropertyOrder({"attributes", "id", "lifetimeActions"}) 19 | public class KeyRotationPolicyModel { 20 | 21 | @NotNull(groups = {Restore.class}) 22 | @JsonProperty("id") 23 | private URI id; 24 | @Valid 25 | @NotNull(groups = {Restore.class, Update.class}) 26 | @JsonProperty("attributes") 27 | private KeyRotationPolicyAttributes attributes; 28 | @Valid 29 | @NotNull(groups = {Restore.class, Update.class}) 30 | @Size(min = 1, max = 2, groups = {Restore.class, Update.class}) 31 | @JsonProperty("lifetimeActions") 32 | private List lifetimeActions; 33 | @JsonIgnore 34 | private KeyEntityId keyEntityId; 35 | 36 | } 37 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/json/util/Base64Serializer.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.json.util; 2 | 3 | import tools.jackson.core.JsonGenerator; 4 | import tools.jackson.databind.SerializationContext; 5 | import tools.jackson.databind.ValueSerializer; 6 | 7 | import java.util.Base64; 8 | import java.util.Optional; 9 | 10 | public class Base64Serializer extends ValueSerializer { 11 | 12 | private static final Base64.Encoder DEFAULT_ENCODER = Base64.getUrlEncoder().withoutPadding(); 13 | private final Base64.Encoder encoder; 14 | 15 | public Base64Serializer() { 16 | this(DEFAULT_ENCODER); 17 | } 18 | 19 | protected Base64Serializer(final Base64.Encoder encoder) { 20 | this.encoder = encoder; 21 | } 22 | 23 | @Override 24 | public void serialize( 25 | final byte[] value, 26 | final JsonGenerator generator, 27 | final SerializationContext provider) { 28 | final var text = base64Encode(value); 29 | if (text != null) { 30 | generator.writeString(text); 31 | } else { 32 | generator.writeNull(); 33 | } 34 | } 35 | 36 | protected String base64Encode(final byte[] value) { 37 | final var optional = Optional.ofNullable(value); 38 | return optional.filter(v -> v.length > 0) 39 | .map(encoder::encodeToString) 40 | .orElse(null); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lowkey-vault-docker/src/test/resources/com/github/nagyesta/lowkeyvault/secrets/ListDeletedSecrets.feature: -------------------------------------------------------------------------------- 1 | Feature: Secret list deleted 2 | 3 | @Secret @SecretCreate @SecretListDeleted @CreateVault 4 | Scenario Outline: SECRET_LIST_DELETED_01 Secrets are created and deleted with the secret client then all are listed as deleted secrets 5 | Given secret API version is used 6 | And a vault is created with name secrets-del- 7 | And a secret client is created with the vault named secrets-del- 8 | And secrets with - prefix are created valued abc123 9 | And secrets with - prefix are deleted 10 | When the deleted secret properties are listed 11 | Then the listed deleted secrets are matching the ones deleted before 12 | 13 | Examples: 14 | | api | index | count | secretName | 15 | | 7.2 | 01 | 1 | listSecret | 16 | | 7.3 | 02 | 1 | listSecret | 17 | | 7.3 | 03 | 2 | list-secret-name | 18 | | 7.3 | 04 | 3 | listSecret | 19 | | 7.3 | 05 | 5 | list-secret-name | 20 | | 7.3 | 06 | 25 | listSecret | 21 | | 7.3 | 07 | 42 | list-secret-name | 22 | | 7.4 | 08 | 5 | list-secret-name | 23 | | 7.5 | 09 | 5 | list-secret-name | 24 | | 7.6 | 10 | 5 | list-secret-name | 25 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/mapper/v7_3/key/RsaPrivateKeyToJsonWebKeyImportRequestConverter.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.mapper.v7_3.key; 2 | 3 | import com.github.nagyesta.lowkeyvault.model.v7_2.key.constants.KeyType; 4 | import com.github.nagyesta.lowkeyvault.model.v7_2.key.request.JsonWebKeyImportRequest; 5 | import lombok.NonNull; 6 | import org.springframework.core.convert.converter.Converter; 7 | 8 | import java.security.interfaces.RSAPrivateCrtKey; 9 | 10 | public class RsaPrivateKeyToJsonWebKeyImportRequestConverter 11 | implements Converter { 12 | 13 | @Override 14 | public JsonWebKeyImportRequest convert(final @NonNull RSAPrivateCrtKey source) { 15 | final var importRequest = new JsonWebKeyImportRequest(); 16 | importRequest.setKeyType(KeyType.RSA); 17 | importRequest.setN(source.getModulus().toByteArray()); 18 | importRequest.setE(source.getPublicExponent().toByteArray()); 19 | importRequest.setD(source.getPrivateExponent().toByteArray()); 20 | importRequest.setP(source.getPrimeP().toByteArray()); 21 | importRequest.setQ(source.getPrimeQ().toByteArray()); 22 | importRequest.setDq(source.getPrimeExponentQ().toByteArray()); 23 | importRequest.setDp(source.getPrimeExponentP().toByteArray()); 24 | importRequest.setQi(source.getCrtCoefficient().toByteArray()); 25 | return importRequest; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lowkey-vault-app/src/main/java/com/github/nagyesta/lowkeyvault/model/v7_3/certificate/DeletedKeyVaultCertificateModel.java: -------------------------------------------------------------------------------- 1 | package com.github.nagyesta.lowkeyvault.model.v7_3.certificate; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.github.nagyesta.lowkeyvault.model.common.DeletedModel; 6 | import com.github.nagyesta.lowkeyvault.model.json.util.EpochSecondsDeserializer; 7 | import com.github.nagyesta.lowkeyvault.model.json.util.EpochSecondsSerializer; 8 | import lombok.Data; 9 | import lombok.EqualsAndHashCode; 10 | import tools.jackson.databind.annotation.JsonDeserialize; 11 | import tools.jackson.databind.annotation.JsonSerialize; 12 | 13 | import java.time.OffsetDateTime; 14 | 15 | @Data 16 | @EqualsAndHashCode(callSuper = true) 17 | @JsonInclude(JsonInclude.Include.NON_NULL) 18 | public class DeletedKeyVaultCertificateModel 19 | extends KeyVaultCertificateModel implements DeletedModel { 20 | 21 | @JsonProperty("recoveryId") 22 | private String recoveryId; 23 | @JsonProperty("deletedDate") 24 | @JsonSerialize(using = EpochSecondsSerializer.class) 25 | @JsonDeserialize(using = EpochSecondsDeserializer.class) 26 | private OffsetDateTime deletedDate; 27 | @JsonProperty("scheduledPurgeDate") 28 | @JsonSerialize(using = EpochSecondsSerializer.class) 29 | @JsonDeserialize(using = EpochSecondsDeserializer.class) 30 | private OffsetDateTime scheduledPurgeDate; 31 | 32 | } 33 | --------------------------------------------------------------------------------