A Spring Boot library that provides encryption and decryption functionality using AWS KMS data keys with AES-256-GCM encryption.
This project was initially created using the following prompt:
Spring Boot Kotlin 라이브러리를 만들어줘. 이 라이브러리는 암호화 복호화 기능을 제공한다.
AWS KMS를 사용하여 data key를 생성하고, 이 data key를 이용하여 AES-256-GCM 방식으로 암호화 복호화를 수행한다.
사용자는 이 라이브러리를 dependency로 추가하여 사용할 수 있어야 한다.
Spring Boot의 auto-configuration을 사용하여 별도의 설정 없이도 기본적인 기능을 사용할 수 있도록 한다.
설정 파일(application.yml)에서 KMS 설정과 암호화 옵션을 지정할 수 있어야 한다.
키 로테이션을 지원하여 여러 버전의 키를 관리할 수 있어야 한다.
- Data Key Caching Implementation:
KmsDataKeyService를 통해 얻어온 data key는 이후 저장해서 계속 사용하고 싶다.
This resulted in adding a comprehensive caching mechanism with TTL and size limits for data keys.
- Package Name Change:
root package를 com.example.crypto 대신 com.musinsa.crypto.kms 로 변경해줘
Changed the root package from com.example.crypto
to com.musinsa.crypto.kms
.
- Cache Simplification:
data key는 instance가 떠있는 동안 계속 사용될 것이기 때문에 ttl이 필요없습니다.
cache는 항상사용할 것이기 때문에 cache.enabled 설정도 필요 없습니다
max-size 설정도 필요 없는 것 같습니다. 즉 최종적으로 cache 설정은 불필요해보입니다
Simplified caching to permanently store data keys for the instance lifetime without TTL, size limits, or configuration options.
- AES-256-GCM encryption/decryption
- AWS KMS integration for data key management
- Permanent data key caching for performance (cached for instance lifetime)
- Spring Boot auto-configuration
- Support for key rotation (multiple key versions)
Add the following dependency to your build.gradle.kts
:
dependencies {
implementation("com.musinsa:spring-kms-crypto-library:1.0.0")
}
Configure the library in your application.yml
:
crypto-kms:
enabled: true
aws-region: us-east-1
current-key-id: v1
encrypted-data-keys:
v1: "Base64EncodedEncryptedDataKey1..."
v2: "Base64EncodedEncryptedDataKey2..."
⚠️ Important: All configured data keys are decrypted at application startup. If any encrypted data key is missing or invalid, or if KMS is unavailable, the application will fail to start. This is a fail-fast approach to ensure data security and prevent runtime encryption/decryption failures.Note: If a key that wasn't configured at startup is requested later (e.g., old keys for decryption), the library will attempt to decrypt it from KMS on-demand, which will increase latency and log a warning.
@RestController
@RequestMapping("/api/crypto")
class CryptoController(
private val cryptoService: CryptoService
) {
@PostMapping("/encrypt")
fun encrypt(@RequestBody request: CryptoRequest): CryptoResponse {
return cryptoService.encrypt(request)
}
@PostMapping("/decrypt")
fun decrypt(@RequestBody request: DecryptRequest): Map<String, String> {
val plaintext = cryptoService.decrypt(request)
return mapOf("plaintext" to plaintext)
}
}
To generate encrypted data keys for configuration:
val kmsClient = KmsClient.builder()
.region(Region.US_EAST_1)
.build()
val generateDataKeyRequest = GenerateDataKeyRequest.builder()
.keyId("your-kms-master-key-id")
.keySpec(DataKeySpec.AES_256)
.build()
val response = kmsClient.generateDataKey(generateDataKeyRequest)
val encryptedDataKey = Base64.getEncoder().encodeToString(
response.ciphertextBlob().asByteArray()
)
interface CryptoService {
fun encrypt(request: CryptoRequest): CryptoResponse
fun decrypt(request: DecryptRequest): String
}
data class CryptoRequest(
val plaintext: String
)
data class CryptoResponse(
val keyId: String, // Version identifier
val encryptedText: String, // Base64 encoded
val iv: String // Base64 encoded IV
)
data class DecryptRequest(
val keyId: String,
val encryptedText: String,
val iv: String
)
- Data Key Storage: Encrypted data keys are stored in configuration and decrypted at startup using AWS KMS
- Key Rotation: Support multiple key versions for gradual key rotation
- Caching: Plaintext data keys are cached in memory for the lifetime of the application instance
- Encryption: Uses AES-256-GCM for authenticated encryption
- Startup Validation: All configured data keys must be valid and accessible via KMS at startup, or the application will fail to initialize
Your application needs the following KMS permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"kms:Decrypt"
],
"Resource": "arn:aws:kms:region:account:key/*"
}
]
}
Run tests with:
./gradlew test
Apache License 2.0