aes-128-encryption-module-for-isms-java-oracle.md
devcondasecurityaes-128-encryption-module-for-isms-java-oracle.md

AES-128 Encryption Module for ISMS (Java + Oracle)

Written by

in

A few ISMS crypto-control requirements show up constantly in application work. The excerpts below are from an internal company policy.

Criteria for choosing encryption technology and programs

For confidentiality, use a proven symmetric-key encryption algorithm with a key length of at least 128 bits.

Encryption targets: unique identifiers, credit card numbers, account numbers, biometric data, and similar information.

Recommended security strength

Security strengthSymmetric-key algorithmGuidance
Below 80-bitDESNot recommended
80-bit2TDEANot recommended
112-bit3TDEANot recommended
128-bitSEED, HIGHT, ARIA-128, AES-128Recommended
192-bitARIA-192, AES-192Recommended
256-bitARIA-256, AES-256Recommended

Encryption key management

  • If an encryption key is stored on external media, it must be stored on a secure medium that unauthorized users cannot access.
  • When keeping an encryption key in document form, store it encrypted.
  • An encryption key must not be stored on the operating system as a plaintext file or embedded in program source.
  • Program source that contains an encryption key must be removed from the operating system and kept only as a load module.
  • Encryption keys stored on a server must themselves be stored in encrypted form.

Implementation

As covered in the earlier post on Oracle DBMS_CRYPTO, the package function had the encryption key passed in plaintext as a function parameter, so it needed to be fixed. Existing encrypted data was already encrypted with AES-128, so I also chose AES-128 on the Java side.

Design

AS-IS

  1. Call an Oracle package function that stores the encryption key in plaintext

TO-BE

  1. Create a module that encrypts and decrypts the encryption key
  2. Encrypt the operational encryption key with that module and store it in a txt file
  3. Store the txt file on the WAS server
  4. On the WAS, use AES128.jar to decrypt the key from the txt file, then call the Oracle package function

Module source

package com.erp.common;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AES128 {
    // IV
    private String ips;
    // Key class
    private Key keySpec;
    // Constructor
    public AES128() {
        // 16-byte key used to encrypt/decrypt the encryption key
        String key = "test1234ttest1234";
        try {
            // byte[] to hold the encryption key
            byte[] keyBytes = new byte[16];
            byte[] b = key.getBytes("UTF-8");
            System.arraycopy(b, 0, keyBytes, 0, keyBytes.length);
            // Declare the secret key class
            SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
            // The production Oracle package function does not use IV, so use null 16 bytes
            this.ips = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
            // Store the AES method and key value in the Key class
            this.keySpec = keySpec;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // Overloaded constructor
    public AES128(String key) {
        try {
            byte[] keyBytes = new byte[16];
            byte[] b = key.getBytes("UTF-8");
            System.arraycopy(b, 0, keyBytes, 0, keyBytes.length);
            SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
            this.ips = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; // null 16 bytes
            this.keySpec = keySpec;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // Encrypt
    public String encrypt(String plainText) {
        // Class that provides encryption/decryption
        Cipher cipher;
        try {
            // Declare cipher mode, chaining mode, and padding, then get an instance
            cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            // Initialize with the encryption key and IV
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(ips.getBytes()));
            // Run encryption
            byte[] encrypted = cipher.doFinal(plainText.getBytes("UTF-8"));
            // Oracle DBMS_CRYPTO returns a HEX string, so convert byte[] to HEX
            String encryptStr = new String(byteArrayToHex(encrypted).toUpperCase());
            // Return the encrypted string
            return encryptStr;
        } catch (Exception e) {
            return null;
        }
    }
    // Decrypt
    public String decrypt(String encryptStr) {
        try {
            // Declare cipher mode, chaining mode, and padding, then get an instance
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            // Initialize with the encryption key and IV
            cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(ips.getBytes("UTF-8")));
            // Convert HEX String to byte[]
            byte[] byteStr = hexToByteArray(encryptStr);
            // Run decryption
            String decryptStr = new String(cipher.doFinal(byteStr), "UTF-8");
            return decryptStr;
        } catch (Exception e) {
            return null;
        }
    }
    // byte[] -> hex String
    private String byteArrayToHex(byte[] encrypted) {
        if (encrypted == null || encrypted.length == 0) {
            return null;
        }
        StringBuffer sb = new StringBuffer();
        for (byte b : encrypted) {
            // Format as 2-digit hex, zero-padded (2 hex digits = 1 byte)
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }
    // hex String -> byte[]
    private byte[] hexToByteArray(String hex) {
        if (hex == null || hex.length() == 0) {
            return null;
        }
        // 2 hex digits = 1 byte
        byte[] byteArray = new byte[hex.length() / 2];
        for (int i = 0; i < byteArray.length; i++) {
            // Convert two characters at a time as hex
            byteArray[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16);
        }
        return byteArray;
    }
}

Module caller

Decrypt the encryption key and keep it available from a static method in a Util class.

import java.io.File;
import java.util.Scanner;

public class Util {
    public static String getEncKey() {
        AES128 aes128 = new AES128();
        String enc_key = null;
        try {
            // enc_key.txt content:
            // 25570DDCCE2517C85728B94A051AF276A3D07E616EE120540EE4EFD7C88ED357
            File f1 = new File("project_path" + "/enc_key.txt");
            Scanner sc = new Scanner(f1);
            while (sc.hasNextLine()) {
                String data = sc.nextLine();
                enc_key = aes128.decrypt(data);
            }
            sc.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            return enc_key;
        }
    }
}

Usage in the service layer

Store the value in a DataMap paramData object in the service.

// Declare as a service class field
private String enc_key = Util.getEncKey();

// Assign it to the query parameter map inside a service method
public String getHpNo(DataMap paramData) {
    paramData.put("enc_key", enc_key);
}

Query usage

SELECT CRYPTO.DECRYPT(HP_NO, ${enc_key})
FROM DUAL

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *