Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions MiniKms/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package ftn.security.minikms.entity;

import ftn.security.minikms.enumeration.KeyOperation;
import ftn.security.minikms.enumeration.KeyType;
import jakarta.persistence.*;
import jakarta.persistence.Id;
import lombok.Data;

import java.time.Instant;
import java.util.List;
import java.util.UUID;

@Data
@Entity
public class WrappedKeyEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false, updatable = false)
private UUID logicalKey;

private String alias;
private Integer version;

@Enumerated(EnumType.STRING)
private KeyType keyType;

@Lob
private byte[] wrappedKey;

@ElementCollection(fetch = FetchType.EAGER)
@Enumerated(EnumType.STRING)
private List<KeyOperation> allowedOperations;

private Instant createdAt;

public static WrappedKeyEntity of(UUID logicalKey, String alias, Integer version, KeyType keyType, byte[] wrappedKey, List<KeyOperation> allowedOperations) {
var entity = new WrappedKeyEntity();
entity.logicalKey = logicalKey;
entity.alias = alias;
entity.version = version;
entity.keyType = keyType;
entity.wrappedKey = wrappedKey;
entity.allowedOperations = allowedOperations;
entity.createdAt = Instant.now();
return entity;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package ftn.security.minikms.enumeration;

public enum KeyOperation {
ENCRYPT, SIGN
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package ftn.security.minikms.enumeration;

public enum KeyType {
SYMMETRIC, ASYMMETRIC, HMAC
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package ftn.security.minikms.repository;

import ftn.security.minikms.entity.WrappedKeyEntity;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;
import java.util.UUID;

public interface WrappedKeyRepository extends JpaRepository<WrappedKeyEntity, Long> {
Optional<WrappedKeyEntity> findFirstByLogicalKeyOrderByVersionDesc(UUID logicalKey);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package ftn.security.minikms.service;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.GeneralSecurityException;
import java.util.Base64;

@Service
public class RootKeyManager {
private static final java.security.SecureRandom RNG = new java.security.SecureRandom();
private final SecretKey rootKey;

public RootKeyManager(@Value("${ROOT_KEY}") String base64Key) {
if (base64Key == null || base64Key.isEmpty()) {
throw new IllegalArgumentException("ROOT_KEY environment variable is not set or empty");
}

byte[] raw = Base64.getDecoder().decode(base64Key);
this.rootKey = new SecretKeySpec(raw, "AES");
}

public byte[] wrap(byte[] plaintextKey, byte[] aad) throws GeneralSecurityException {
var iv = new byte[12];
RNG.nextBytes(iv);

var c = Cipher.getInstance("AES/GCM/NoPadding");
c.init(Cipher.ENCRYPT_MODE, rootKey, new GCMParameterSpec(128, iv));
if (aad != null && aad.length > 0) c.updateAAD(aad);
var ct = c.doFinal(plaintextKey);

var out = new byte[iv.length + ct.length];
System.arraycopy(iv, 0, out, 0, iv.length);
System.arraycopy(ct, 0, out, iv.length, ct.length);
return out;
}

public byte[] unwrap(byte[] blob, byte[] aad) throws GeneralSecurityException {
if (blob.length < 12 + 16) throw new GeneralSecurityException("Blob too short");
var iv = java.util.Arrays.copyOfRange(blob, 0, 12);
var ct = java.util.Arrays.copyOfRange(blob, 12, blob.length);

var c = Cipher.getInstance("AES/GCM/NoPadding");
c.init(Cipher.DECRYPT_MODE, rootKey, new GCMParameterSpec(128, iv));
if (aad != null && aad.length > 0) c.updateAAD(aad);
return c.doFinal(ct);
}
}
2 changes: 2 additions & 0 deletions MiniKms/src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
spring.application.name=MiniKms

ROOT_KEY=your-base64-encoded-key-here
Loading