0

With the entered code here, I am trying to build a project on springboot. I am using AES encryption in my project. I am keeping the password(key) of AES in the application properties. The variable and method in the AES class are static. I use static for using the method without creating an instance of the class. But I can't inject the value into the static variable in the AES class. I injected using a workaround method, which is injecting the value to the instance variable and then passing it to the static variable. I also wrote setter method to inject, but these methods throwing warning in the SonarQube. I am using SonarQube to validate my code. I don't know what to do. I just supressed the warning. Is there any way to inject value without triggering the warning in SonarQube.

package com.example.demo.utils;

import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.*;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Base64;

@Component
public class AESUtils {

    @Getter
    private static String password;

    @SuppressWarnings("squid:S2696")
    @Value("${aes.secret.key}")
    public void setPassword(String injectedPassword) { // Spring calls this
        AESUtils.password = injectedPassword;
    }

    // AES parameters
    private static final int GCM_IV_LENGTH = 12; // 12-byte IV recommended
    private static final int GCM_TAG_LENGTH = 128; // 128-bit authentication tag
    private static final int PBKDF2_ITERATIONS = 51159; // For key derivation
    private static final int PBKDF2_KEY_LENGTH = 256;

    private static final SecureRandom secureRandom = new SecureRandom();

    // ================================================
    // Encrypt a plaintext string using AES-GCM
    // Generates a random IV for each encryption
    // Returns Base64-encoded string: IV + ciphertext
    // ================================================
    public static String encrypt(String plainText) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeyException {
        // Generate random 12-byte IV
        byte[] iv = new byte[GCM_IV_LENGTH];
        secureRandom.nextBytes(iv);

        // Derive AES key from password
        SecretKey key = deriveKey(password, iv); // using IV as salt

        // Initialize cipher
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
        cipher.init(Cipher.ENCRYPT_MODE, key, spec);

        // Encrypt plaintext
        byte[] cipherText = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));

        // Prepend IV to ciphertext
        byte[] ivAndCipher = new byte[iv.length + cipherText.length];
        System.arraycopy(iv, 0, ivAndCipher, 0, iv.length);
        System.arraycopy(cipherText, 0, ivAndCipher, iv.length, cipherText.length);

        // Base64 encode for easy storage/transmission
        return  Base64.getUrlEncoder().withoutPadding().encodeToString(ivAndCipher);

    }

    // ================================================
    // Decrypt a Base64-encoded ciphertext string
    // Extracts IV from first 12 bytes
    // Returns original plaintext
    // ================================================
    public static String decrypt(String base64CipherText) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
        byte[] ivAndCipher = Base64.getUrlDecoder().decode(base64CipherText);

        // Extract IV
        byte[] iv = new byte[GCM_IV_LENGTH];
        System.arraycopy(ivAndCipher, 0, iv, 0, GCM_IV_LENGTH);

        // Extract ciphertext
        byte[] cipherText = new byte[ivAndCipher.length - GCM_IV_LENGTH];
        System.arraycopy(ivAndCipher, GCM_IV_LENGTH, cipherText, 0, cipherText.length);

        // Derive AES key from password
        SecretKey key = deriveKey(password, iv); // same IV used as salt

        // Initialize cipher
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
        cipher.init(Cipher.DECRYPT_MODE, key, spec);

        // Decrypt
        byte[] plainBytes = cipher.doFinal(cipherText);

        return  new String(plainBytes, StandardCharsets.UTF_8);

    }

    // ================================================
    // Derive AES key from password using PBKDF2 with HMAC-SHA256
    // Salt ensures unique key per encryption
    // ================================================
    private static SecretKey deriveKey(String password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
        PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, PBKDF2_ITERATIONS, PBKDF2_KEY_LENGTH);
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        byte[] keyBytes = factory.generateSecret(spec).getEncoded();
        return new SecretKeySpec(keyBytes, "AES");
    }

}
2
  • What is AESUtils, is it your class? If yes, add its code pls to your question Commented Nov 20 at 7:28
  • Pls have a look at the answer and if it is suitable for you, don't forget to accept it by clicking the check mark Commented Nov 20 at 15:25

1 Answer 1

0

The correct and clean solution would be not to use static methods or static fields for AES encryption, but make AESUtils a Spring bean, inject the secret key normally and autowire AESUtils wherever needed.

You could also rename the class to AESCryptoService so that it no longer uses the Utils suffix, which helps avoid the confusion of having a utility-style name on a class that doesn’t contain static methods:

@Component
public class AESCryptoService {

    @Value("${aes.secret.key}")
    private String password;


    public String encrypt(String plainText) {
         // implementation
    }

    public String decrypt(String base64CipherText) {
        // implementation
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.