RSA encryption without OAEP

RSA encryption without OAEP

Description

RSA encryption needs secure padding. NoPadding is deterministic and vulnerable to ciphertext manipulation. PKCS#1 v1.5 padding (PKCS1Padding) can expose a padding oracle, such as a Bleichenbacher attack, when decryption errors or processing times distinguish valid from invalid padding. An attacker can use those response differences to decrypt ciphertext incrementally.

Potential impact

  • Plaintext disclosure: a padding oracle can expose encrypted secrets such as tokens, session keys, and personal data.
  • Credential or token theft: disclosed login tokens or API keys can lead to account compromise.
  • Integrity loss: raw RSA or vulnerable padding can permit meaningful ciphertext manipulation.
  • Reduced cryptographic protection: allowing legacy padding instead of OAEP can weaken the system and combine with other attacks.

Remediation

  • Use OAEP padding: Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding").
  • Specify the OAEP parameters explicitly where possible: OAEPParameterSpec with SHA-256, MGF1 with SHA-256, and PSource.PSpecified.DEFAULT.
  • Replace "RSA", "RSA/ECB/PKCS1Padding", and "RSA/ECB/NoPadding" in RSA encryption code with OAEP. Verify matching parameters and provider support on both the encryption and decryption sides.
  • Handle decryption failures uniformly so that error details do not create an oracle.
  • Use hybrid encryption in applications: encrypt data with AES-GCM and wrap a random session key with RSA-OAEP.
  • Use RSA keys of at least 2048 bits and maintain the JCA/JCE provider.

Examples

Before

java
import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;

public class InsecureRSAExample {
    // 취약: PKCS#1 v1.5 패딩 사용
    public static byte[] encryptPassword(String password, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); // BAD
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return cipher.doFinal(password.getBytes(StandardCharsets.UTF_8));
    }
}

After

java
import javax.crypto.Cipher;
import javax.crypto.spec.OAEPParameterSpec;
import java.security.spec.MGF1ParameterSpec;
import javax.crypto.spec.PSource;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;

public class SecureRSAExample {
    // 안전: OAEP(SHA-256, MGF1) 사용
    public static byte[] encryptPassword(String password, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
        OAEPParameterSpec oaep = new OAEPParameterSpec(
                "SHA-256",
                "MGF1",
                MGF1ParameterSpec.SHA256,
                PSource.PSpecified.DEFAULT
        );
        cipher.init(Cipher.ENCRYPT_MODE, publicKey, oaep);
        return cipher.doFinal(password.getBytes(StandardCharsets.UTF_8));
    }
}

Explanation:

  • Before: PKCS#1 v1.5 encryption can be exploited through a padding oracle if the decrypting system distinguishes padding errors. NoPadding is also deterministic and malleable, so it is not a suitable alternative.
  • After: The example explicitly uses SHA-256 for both the OAEP hash and the MGF1 hash. It demonstrates encryption only; uniform handling of decryption failures must be implemented separately.

References