Description
In Java and Kotlin, an algorithm-only call such as Cipher.getInstance("AES") leaves mode and padding selection to the JCA provider. Oracle JDK 25 documents ECB and PKCS5Padding defaults for several symmetric ciphers in SunJCE and SunPKCS11; in SunJCE, AES is equivalent to AES/ECB/PKCS5Padding. ECB maps identical plaintext blocks to identical ciphertext blocks, exposing repeated patterns, and provides no integrity or authentication. Other providers may have different defaults, making both security properties and compatibility uncertain.
Potential impact
- Repeated plaintext patterns can reveal information about the data's structure or contents.
- A mode without authentication may fail to detect ciphertext modification.
- Changing providers or deployment environments can cause decryption failures or unexpected security changes.
Remediation
- Specify the algorithm, mode and padding explicitly.
- Prefer a reviewed AEAD mode such as
AES/GCM/NoPaddingwhen confidentiality and integrity are required. - Use a unique 96-bit nonce for every GCM encryption under the same key. Never reuse it. For random nonces, generate 12 bytes with
SecureRandomand retain or transmit the nonce with the ciphertext for decryption. The nonce is not secret. - If existing ECB ciphertext must remain readable, plan its migration separately from new encryption.
Examples
Before
import javax.crypto.Cipher;
Cipher cipher = Cipher.getInstance("AES");
After
key is an AES SecretKey generated and managed securely elsewhere. This excerpt initializes the cipher; ciphertext storage and decryption error handling are omitted.
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
byte[] nonce = new byte[12];
new SecureRandom().nextBytes(nonce);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(128, nonce));
The first call can select an unsuitable provider default such as ECB. The second explicitly selects authenticated GCM and a 96-bit nonce. Each subsequent encryption under that key needs a new nonce, which the decrypting side must receive with the ciphertext.
The same principles apply to Kotlin because it uses the same javax.crypto.Cipher API.