Skip to content

Commit 33eb5c5

Browse files
authored
Merge pull request #5 from ftn-projects/feature/encryption
Feature/encryption
2 parents 033f820 + 12ff9c4 commit 33eb5c5

9 files changed

Lines changed: 296 additions & 12 deletions

File tree

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package ftn.security.minikms.controller;
2+
3+
import ftn.security.minikms.dto.CryptoDTO;
4+
import ftn.security.minikms.service.KeyComputeService;
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.http.HttpStatus;
7+
import org.springframework.http.ResponseEntity;
8+
import org.springframework.web.bind.annotation.PostMapping;
9+
import org.springframework.web.bind.annotation.RequestBody;
10+
import org.springframework.web.bind.annotation.RequestMapping;
11+
import org.springframework.web.bind.annotation.RestController;
12+
13+
import javax.crypto.BadPaddingException;
14+
import javax.crypto.IllegalBlockSizeException;
15+
import javax.crypto.NoSuchPaddingException;
16+
import java.security.*;
17+
import java.security.spec.InvalidKeySpecException;
18+
19+
@RestController
20+
@RequestMapping(value = "/api/v1/crypto")
21+
public class KeyComputeController {
22+
private final KeyComputeService service;
23+
@Autowired
24+
public KeyComputeController(KeyComputeService service) {
25+
this.service = service;
26+
}
27+
28+
@PostMapping("/encrypt/symmetric")
29+
public ResponseEntity<?> encryptSymmetric(@RequestBody CryptoDTO dto) throws InvalidAlgorithmParameterException,
30+
NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException,
31+
BadPaddingException, InvalidKeyException {
32+
try {
33+
String encrypted = service.encryptAes(dto.getMessage(), dto.getKeyId(),
34+
dto.getUsername(), dto.getVersion());
35+
return ResponseEntity.status(HttpStatus.CREATED).body(encrypted);
36+
} catch (InvalidParameterException e) {
37+
return ResponseEntity.badRequest().body(e.getMessage());
38+
}
39+
}
40+
@PostMapping("/decrypt/symmetric")
41+
public ResponseEntity<?> decryptSymmetric(@RequestBody CryptoDTO dto) throws InvalidAlgorithmParameterException,
42+
NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException,
43+
BadPaddingException, InvalidKeyException {
44+
try {
45+
String decrypted = service.decryptAes(dto.getMessage(), dto.getKeyId(),
46+
dto.getUsername(), dto.getVersion());
47+
return ResponseEntity.status(HttpStatus.CREATED).body(decrypted);
48+
} catch (InvalidParameterException e) {
49+
return ResponseEntity.badRequest().body(e.getMessage());
50+
}
51+
}
52+
@PostMapping("/encrypt/asymmetric")
53+
public ResponseEntity<?> encryptAsymmetric(@RequestBody CryptoDTO dto) throws
54+
NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException,
55+
BadPaddingException, InvalidKeyException, InvalidKeySpecException {
56+
try {
57+
String encrypted = service.encryptRsa(dto.getMessage(), dto.getKeyId(),
58+
dto.getUsername(), dto.getVersion());
59+
return ResponseEntity.status(HttpStatus.CREATED).body(encrypted);
60+
} catch (InvalidParameterException e) {
61+
return ResponseEntity.badRequest().body(e.getMessage());
62+
}
63+
}
64+
@PostMapping("/decrypt/asymmetric")
65+
public ResponseEntity<?> decryptAsymmetric(@RequestBody CryptoDTO dto) throws
66+
NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException,
67+
BadPaddingException, InvalidKeyException, InvalidKeySpecException {
68+
try {
69+
String decrypted = service.decryptRsa(dto.getMessage(), dto.getKeyId(),
70+
dto.getUsername(), dto.getVersion());
71+
return ResponseEntity.status(HttpStatus.CREATED).body(decrypted);
72+
} catch (InvalidParameterException e) {
73+
return ResponseEntity.badRequest().body(e.getMessage());
74+
}
75+
}
76+
@PostMapping("/compute/hmac")
77+
public ResponseEntity<?> computeHmac(@RequestBody CryptoDTO dto) throws Exception {
78+
try {
79+
String computed = service.computeHmac(dto.getMessage(), dto.getKeyId(),
80+
dto.getUsername(), dto.getVersion());
81+
return ResponseEntity.status(HttpStatus.CREATED).body(computed);
82+
} catch (InvalidParameterException e) {
83+
return ResponseEntity.badRequest().body(e.getMessage());
84+
}
85+
}
86+
@PostMapping("/verify/hmac")
87+
public ResponseEntity<?> verifyHmac(@RequestBody CryptoDTO dto) throws Exception {
88+
try {
89+
Boolean verified = service.verifyHmac(dto.getMessage(), dto.getHmacBase64(), dto.getKeyId(),
90+
dto.getUsername(), dto.getVersion());
91+
return ResponseEntity.status(HttpStatus.CREATED).body(verified);
92+
} catch (InvalidParameterException e) {
93+
return ResponseEntity.badRequest().body(e.getMessage());
94+
}
95+
}
96+
}

MiniKms/src/main/java/ftn/security/minikms/controller/KeyManagementController.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import ftn.security.minikms.dto.KeyDTO;
44
import ftn.security.minikms.dto.KeyMapper;
5-
import ftn.security.minikms.service.KeyService;
5+
import ftn.security.minikms.service.KeyManagementService;
66
import org.mapstruct.factory.Mappers;
77
import org.springframework.beans.factory.annotation.Autowired;
88
import org.springframework.http.HttpStatus;
@@ -17,11 +17,11 @@
1717
@RestController
1818
@RequestMapping(value = "/api/v1/keys")
1919
public class KeyManagementController {
20-
private final KeyService keyService;
20+
private final KeyManagementService keyService;
2121
private final KeyMapper mapper;
2222

2323
@Autowired
24-
public KeyManagementController(KeyService keyService) {
24+
public KeyManagementController(KeyManagementService keyService) {
2525
this.keyService = keyService;
2626
this.mapper = Mappers.getMapper(KeyMapper.class);
2727
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package ftn.security.minikms.dto;
2+
3+
import lombok.Data;
4+
import lombok.NoArgsConstructor;
5+
6+
import java.util.UUID;
7+
8+
@Data
9+
@NoArgsConstructor
10+
public class CryptoDTO {
11+
private String message;
12+
private UUID keyId;
13+
private String username;
14+
private Integer version;
15+
private String hmacBase64;
16+
}

MiniKms/src/main/java/ftn/security/minikms/entity/KeyMetadata.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,10 @@ public void updatePrimaryVersion(Integer version) {
6262
rotatedAt = Instant.now();
6363
}
6464
}
65+
public WrappedKey getVersion(int version) {
66+
return versions.stream()
67+
.filter(wk -> wk.getVersion() == version)
68+
.findFirst()
69+
.orElse(null);
70+
}
6571
}

MiniKms/src/main/java/ftn/security/minikms/service/AESService.java

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,64 @@
22

33
import ftn.security.minikms.entity.KeyMaterial;
44

5-
import javax.crypto.KeyGenerator;
5+
import javax.crypto.*;
6+
import javax.crypto.spec.GCMParameterSpec;
7+
import javax.crypto.spec.SecretKeySpec;
8+
import java.nio.ByteBuffer;
9+
import java.nio.charset.StandardCharsets;
10+
import java.security.InvalidAlgorithmParameterException;
11+
import java.security.InvalidKeyException;
612
import java.security.NoSuchAlgorithmException;
713
import java.security.SecureRandom;
14+
import java.util.Base64;
815

9-
class AESService implements ICryptoService {
16+
public class AESService implements ICryptoService {
1017
public KeyMaterial generateKey() throws NoSuchAlgorithmException {
1118
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
1219
keyGenerator.init(256, SecureRandom.getInstanceStrong());
1320
var key = keyGenerator.generateKey();
1421
return KeyMaterial.of(key);
1522
}
23+
public String encrypt(String input, KeyMaterial key) throws NoSuchAlgorithmException,
24+
NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException,
25+
InvalidAlgorithmParameterException, InvalidKeyException {
26+
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
27+
28+
byte[] iv = new byte[12];
29+
SecureRandom random = new SecureRandom();
30+
random.nextBytes(iv);
31+
GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv);
32+
33+
SecretKey secretKey = new SecretKeySpec(key.getKey(),"AES");
34+
cipher.init(Cipher.ENCRYPT_MODE, secretKey, gcmSpec);
35+
36+
byte[] cipherText = cipher.doFinal(input.getBytes(StandardCharsets.UTF_8));
37+
38+
ByteBuffer byteBuffer = ByteBuffer.allocate(iv.length + cipherText.length);
39+
byteBuffer.put(iv);
40+
byteBuffer.put(cipherText);
41+
42+
return Base64.getEncoder().encodeToString(byteBuffer.array());
43+
}
44+
public String decrypt(String encrypted, KeyMaterial key)
45+
throws NoSuchPaddingException, NoSuchAlgorithmException,
46+
InvalidAlgorithmParameterException, InvalidKeyException,
47+
BadPaddingException, IllegalBlockSizeException {
48+
byte[] decoded = Base64.getDecoder().decode(encrypted);
49+
ByteBuffer byteBuffer = ByteBuffer.wrap(decoded);
50+
51+
byte[] iv = new byte[12];
52+
byteBuffer.get(iv);
53+
54+
byte[] cipherText = new byte[byteBuffer.remaining()];
55+
byteBuffer.get(cipherText);
56+
57+
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
58+
SecretKey secretKey = new SecretKeySpec(key.getKey(),"AES");
59+
GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv);
60+
61+
cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmSpec);
62+
byte[] plainText = cipher.doFinal(cipherText);
63+
return new String(plainText, StandardCharsets.UTF_8);
64+
}
1665
}

MiniKms/src/main/java/ftn/security/minikms/service/HMACService.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,30 @@
33
import ftn.security.minikms.entity.KeyMaterial;
44

55
import javax.crypto.KeyGenerator;
6+
import javax.crypto.Mac;
7+
import javax.crypto.spec.SecretKeySpec;
8+
import java.nio.charset.StandardCharsets;
9+
import java.security.MessageDigest;
610
import java.security.NoSuchAlgorithmException;
711
import java.security.SecureRandom;
12+
import java.util.Base64;
813

9-
class HMACService implements ICryptoService {
14+
public class HMACService implements ICryptoService {
1015
public KeyMaterial generateKey() throws NoSuchAlgorithmException {
1116
KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacSHA512");
1217
keyGenerator.init(256, SecureRandom.getInstanceStrong());
1318
var key = keyGenerator.generateKey();
1419
return KeyMaterial.of(key);
1520
}
21+
public String computeHmac(String message, KeyMaterial key) throws Exception {
22+
Mac mac = Mac.getInstance("HmacSHA512");
23+
mac.init(new SecretKeySpec(key.getKey(),"HmacSHA512"));
24+
byte[] hmacBytes = mac.doFinal(message.getBytes(StandardCharsets.UTF_8));
25+
return Base64.getEncoder().encodeToString(hmacBytes);
26+
}
27+
public boolean verifyHmac(String message, String hmacBase64, KeyMaterial key) throws Exception {
28+
String computedHmac = computeHmac(message, key);
29+
return MessageDigest.isEqual(computedHmac.getBytes(StandardCharsets.UTF_8),
30+
hmacBase64.getBytes(StandardCharsets.UTF_8));
31+
}
1632
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package ftn.security.minikms.service;
2+
3+
import ftn.security.minikms.entity.KeyMaterial;
4+
import ftn.security.minikms.repository.KeyMetadataRepository;
5+
import org.springframework.stereotype.Service;
6+
7+
import javax.crypto.BadPaddingException;
8+
import javax.crypto.IllegalBlockSizeException;
9+
import javax.crypto.NoSuchPaddingException;
10+
import java.security.InvalidAlgorithmParameterException;
11+
import java.security.InvalidKeyException;
12+
import java.security.InvalidParameterException;
13+
import java.security.NoSuchAlgorithmException;
14+
import java.security.spec.InvalidKeySpecException;
15+
import java.util.UUID;
16+
17+
@Service
18+
public class KeyComputeService {
19+
private final KeyMetadataRepository metadataRepository;
20+
private final AESService aesService;
21+
private final RSAService rsaService;
22+
private final HMACService hmacService;
23+
private static final String NOT_AUTHORIZED_MSG = "You do not own a key with given id";
24+
25+
public KeyComputeService(KeyMetadataRepository metadataRepository) {
26+
this.metadataRepository = metadataRepository;
27+
this.aesService = new AESService();
28+
this.rsaService = new RSAService();
29+
this.hmacService = new HMACService();
30+
}
31+
public String encryptAes(String message, UUID keyId, String username, Integer version)
32+
throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException,
33+
NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
34+
return aesService.encrypt(message, getKey(keyId, username, version));
35+
}
36+
public String decryptAes(String message, UUID keyId, String username, Integer version)
37+
throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException,
38+
NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
39+
return aesService.decrypt(message, getKey(keyId, username, version));
40+
}
41+
public String encryptRsa(String message, UUID keyId, String username, Integer version)
42+
throws IllegalBlockSizeException, NoSuchPaddingException, BadPaddingException,
43+
NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException {
44+
return rsaService.encrypt(message, getKey(keyId, username, version));
45+
}
46+
public String decryptRsa(String message, UUID keyId, String username, Integer version)
47+
throws IllegalBlockSizeException, NoSuchPaddingException, BadPaddingException,
48+
NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException {
49+
return rsaService.decrypt(message, getKey(keyId, username, version));
50+
}
51+
public String computeHmac(String message, UUID keyId, String username, Integer version) throws Exception {
52+
return hmacService.computeHmac(message, getKey(keyId, username, version));
53+
}
54+
public boolean verifyHmac(String message, String hmacBase64, UUID keyId, String username, Integer version)
55+
throws Exception {
56+
return hmacService.verifyHmac(message,hmacBase64, getKey(keyId, username, version));
57+
}
58+
private KeyMaterial getKey(UUID keyId, String username, Integer version){
59+
var metadata = metadataRepository.findByIdAndUserUsername(keyId, username)
60+
.orElseThrow(() -> new InvalidParameterException(NOT_AUTHORIZED_MSG));
61+
var wrappedKey = version != null? metadata.getVersion(version) : metadata.getVersion(metadata.getPrimaryVersion());
62+
return wrappedKey.getWrappedMaterial();
63+
}
64+
}

MiniKms/src/main/java/ftn/security/minikms/service/KeyService.java renamed to MiniKms/src/main/java/ftn/security/minikms/service/KeyManagementService.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@
1717
import java.util.UUID;
1818

1919
@Service
20-
public class KeyService {
20+
public class KeyManagementService {
2121
private final KeyMetadataRepository metadataRepository;
2222
private final WrappedKeyRepository keyRepository;
2323
private final UserRepository userRepository;
2424
private final RootKeyManager rootKeyManager;
2525
private final Map<KeyType, ICryptoService> cryptoServices;
2626
private static final String NOT_AUTHORIZED_MSG = "You do not own a key with given id";
2727

28-
public KeyService(
28+
public KeyManagementService(
2929
KeyMetadataRepository metadataRepository,
3030
WrappedKeyRepository keyRepository,
3131
UserRepository userRepository,

MiniKms/src/main/java/ftn/security/minikms/service/RSAService.java

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,51 @@
22

33
import ftn.security.minikms.entity.KeyMaterial;
44

5-
import java.security.KeyPairGenerator;
6-
import java.security.NoSuchAlgorithmException;
5+
import javax.crypto.BadPaddingException;
6+
import javax.crypto.Cipher;
7+
import javax.crypto.IllegalBlockSizeException;
8+
import javax.crypto.NoSuchPaddingException;
9+
import java.nio.charset.StandardCharsets;
10+
import java.security.*;
11+
import java.security.spec.InvalidKeySpecException;
12+
import java.security.spec.PKCS8EncodedKeySpec;
13+
import java.security.spec.X509EncodedKeySpec;
14+
import java.util.Base64;
715

8-
class RSAService implements ICryptoService {
16+
public class RSAService implements ICryptoService {
917
public KeyMaterial generateKey() throws NoSuchAlgorithmException {
1018
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
11-
generator.initialize(2048);
19+
generator.initialize(3072);
1220
var pair = generator.generateKeyPair();
1321
return KeyMaterial.of(pair);
1422
}
23+
public String encrypt(String input, KeyMaterial key) throws InvalidKeyException,
24+
IllegalBlockSizeException, BadPaddingException,
25+
NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException {
26+
Cipher encryptCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
27+
28+
KeyFactory factory = KeyFactory.getInstance("RSA");
29+
PublicKey publicKey = factory.generatePublic(new X509EncodedKeySpec(key.getPublicKey()));
30+
31+
encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);
32+
33+
byte[] secretMessageBytes = input.getBytes(StandardCharsets.UTF_8);
34+
byte[] encryptedMessageBytes = encryptCipher.doFinal(secretMessageBytes);
35+
return Base64.getEncoder().encodeToString(encryptedMessageBytes);
36+
}
37+
38+
public String decrypt(String encrypted, KeyMaterial key) throws NoSuchPaddingException, NoSuchAlgorithmException,
39+
InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidKeySpecException {
40+
Cipher decryptCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
41+
42+
KeyFactory factory = KeyFactory.getInstance("RSA");
43+
PrivateKey privateKey = factory.generatePrivate(new PKCS8EncodedKeySpec(key.getKey()));
44+
45+
decryptCipher.init(Cipher.DECRYPT_MODE, privateKey);
46+
47+
byte[] encryptedBytes = Base64.getDecoder().decode(encrypted);
48+
byte[] decryptedMessageBytes = decryptCipher.doFinal(encryptedBytes);
49+
50+
return new String(decryptedMessageBytes, StandardCharsets.UTF_8);
51+
}
1552
}

0 commit comments

Comments
 (0)