From eef1459f0b56bb53b8595ec7cf03121d559b7a1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominique=20Eifl=C3=A4nder?= Date: Thu, 14 Oct 2021 12:42:32 +0200 Subject: [PATCH] RED-2252: Use compliant encryption algorithm AES/GCM/NoPadding --- .../service/EncryptionDecryptionService.java | 98 ++++++++++++++----- 1 file changed, 72 insertions(+), 26 deletions(-) diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/EncryptionDecryptionService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/EncryptionDecryptionService.java index be071f77f..88913d646 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/EncryptionDecryptionService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/EncryptionDecryptionService.java @@ -1,61 +1,107 @@ package com.iqser.red.service.persistence.management.v1.processor.service; import lombok.SneakyThrows; + import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; + +import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; +import java.security.SecureRandom; +import java.security.spec.KeySpec; import java.util.Arrays; import java.util.Base64; @Service public class EncryptionDecryptionService { - @Value("${configuration-service.crypto.key:redaction}") + @Value("${persistence-service.crypto.key:redaction}") private String key; - private SecretKeySpec secretKey; + + private SecretKey secretKey; + private byte[] iv; + @SneakyThrows @PostConstruct protected void postConstruct() { - byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8); - var sha = MessageDigest.getInstance("SHA-1"); - keyBytes = sha.digest(keyBytes); - keyBytes = Arrays.copyOf(keyBytes, 16); - secretKey = new SecretKeySpec(keyBytes, "AES"); + + SecureRandom secureRandom = new SecureRandom(); + iv = new byte[12]; + secureRandom.nextBytes(iv); + secretKey = generateSecretKey(key, iv); } + @SneakyThrows public String encrypt(String strToEncrypt) { - Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); - cipher.init(Cipher.ENCRYPT_MODE, secretKey); - return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes(StandardCharsets.UTF_8))); + + return Base64.getEncoder().encodeToString(encrypt(strToEncrypt.getBytes())); } - @SneakyThrows - public byte[] encrypt(byte[] bytes) { - Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); - cipher.init(Cipher.ENCRYPT_MODE, secretKey); - return cipher.doFinal(bytes); - } - - @SneakyThrows - public byte[] decrypt(byte[] bytes) { - Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); - cipher.init(Cipher.ENCRYPT_MODE, secretKey); - return cipher.doFinal(bytes); - } @SneakyThrows public String decrypt(String strToDecrypt) { - Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING"); - cipher.init(Cipher.DECRYPT_MODE, secretKey); - return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)), StandardCharsets.UTF_8); + + byte[] bytes = Base64.getDecoder().decode(strToDecrypt); + return new String(decrypt(bytes), StandardCharsets.UTF_8); } + @SneakyThrows + public byte[] encrypt(byte[] data) { + + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv); + cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec); + byte[] encryptedData = cipher.doFinal(data); + ByteBuffer byteBuffer = ByteBuffer.allocate(4 + iv.length + encryptedData.length); + byteBuffer.putInt(iv.length); + byteBuffer.put(iv); + byteBuffer.put(encryptedData); + return byteBuffer.array(); + } + + + @SneakyThrows + public byte[] decrypt(byte[] encryptedData) { + + ByteBuffer byteBuffer = ByteBuffer.wrap(encryptedData); + int noonceSize = byteBuffer.getInt(); + if (noonceSize < 12 || noonceSize >= 16) { + throw new IllegalArgumentException("Nonce size is incorrect. Make sure that the incoming data is an AES encrypted file."); + } + byte[] iv = new byte[noonceSize]; + byteBuffer.get(iv); + + SecretKey secretKey = generateSecretKey(key, iv); + + byte[] cipherBytes = new byte[byteBuffer.remaining()]; + byteBuffer.get(cipherBytes); + + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv); + cipher.init(Cipher.DECRYPT_MODE, secretKey, parameterSpec); + return cipher.doFinal(cipherBytes); + } + + + @SneakyThrows + public SecretKey generateSecretKey(String password, byte[] iv) { + + KeySpec spec = new PBEKeySpec(password.toCharArray(), iv, 65536, 128); // AES-128 + SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); + byte[] key = secretKeyFactory.generateSecret(spec).getEncoded(); + return new SecretKeySpec(key, "AES"); + } + }