logo

Database

Need

Use of authenticated encryption with strong keys and current hash functions

Context

• Usage of Kotlin 1.9+ on the JVM for building application services

• Usage of javax.crypto for symmetric encryption

• Usage of java.security for message digests

Description

1. Non compliant code

import java.security.MessageDigest
import javax.crypto.Cipher
import javax.crypto.spec.SecretKeySpec

fun encrypt(key: ByteArray, plaintext: ByteArray): ByteArray {
    // DES has a 56-bit key, and ECB mode leaks patterns in the data
    val cipher = Cipher.getInstance("DES/ECB/PKCS5Padding")
    cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key, "DES"))...

The code below relies on broken cryptographic algorithms. `encrypt` calls `Cipher.getInstance("DES/ECB/PKCS5Padding")`. DES has a 56-bit key that dedicated hardware and cloud instances can search exhaustively in hours, and ECB mode encrypts identical blocks into identical ciphertext, so patterns in the data remain visible. The code also accepts any key size, and short keys produced with `KeyGenerator.init(64)` or RSA keys of 1024 bits with `KeyPairGenerator.initialize(1024)` are equally weak. `fingerprint` hashes an API key with SHA-1, which has practical collision attacks and should not be used for security decisions. Several related mistakes produce the same weakness: AES in CBC, CTR, CFB or OFB mode without integrity protection, initialization vectors created from constant strings with `IvParameterSpec` or `GCMParameterSpec`, encryption keys written in the source, and `SSLContext` instances configured with old protocols, trust managers that accept every certificate or host name verifiers that always return `true`.

2. Steps

• Replace DES, Triple DES, RC4 and AES in ECB mode with `AES/GCM/NoPadding` and a 256-bit key.

• Do not use AES in CBC, CTR, CFB or OFB mode without a separate integrity check; prefer an authenticated mode such as GCM.

• Generate a new random IV with `SecureRandom` for every encryption, and never build `IvParameterSpec` or `GCMParameterSpec` from constant values.

• Use key sizes of at least 128 bits for AES and 2048 bits for RSA, and load keys from a key management service or the Android Keystore instead of the source code.

• Replace MD5 and SHA-1 with SHA-256 or stronger in `MessageDigest`, DigestUtils and Guava hashing calls.

• Configure `SSLContext` with `TLSv1.3` or `TLSv1.2`, and never install trust managers or host name verifiers that accept everything.

3. Secure code example

import java.security.MessageDigest
import java.security.SecureRandom
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.SecretKey
import javax.crypto.spec.GCMParameterSpec

private const val IV_BYTES = 12...

The corrected code uses AES-256 in Galois/Counter Mode. `newKey` generates the key with `KeyGenerator` initialized to 256 bits, and `encrypt` uses the `AES/GCM/NoPadding` transformation. GCM is an authenticated mode: decryption fails with an `AEADBadTagException` if the ciphertext was modified, which rules out the bit-flipping and padding oracle attacks that affect unauthenticated modes. The `NoPadding` suffix is correct here, because GCM is a stream mode that does not need padding. Each call draws a new 12-byte initialization vector from a shared `SecureRandom` and prepends it to the ciphertext, since reusing an IV with the same key breaks GCM. `fingerprint` uses SHA-256. Keys must come from a key management service, the Android Keystore or a secret store, never from constants in the code. For key pairs, RSA keys must have at least 2048 bits, and elliptic curves such as `secp256r1` are preferred.