어떤 것을 하려다가 문제가 발생했는가?
KB 최종 프로젝트에서 티켓 검증 로직에 RSA 암호화를 추가하기로 했다. 프론트엔드(Vue)에서 공개키로 암호화한 티켓 정보를 백엔드(Spring)에서 개인키로 복호화하려고 시도했다.
발생한 환경, 프로그램
프론트엔드에서 Vue의 crypto.subtle API를 사용하여 RSA-OAEP(SHA-256)로 암호화하였고, 백엔드에서는 Java의 Cipher 클래스를 사용하여 복호화를 시도했다.
발생한 문제(에러)
Java에서 BadPaddingException: Decryption error가 발생하여 복호화가 실패했다. 이는 암호화와 복호화 시 사용된 OAEP 패딩에서 MGF1 해시 알고리즘의 불일치로 인해 발생한 오류였다.
추정되는 원인
Vue의 crypto.subtle API와 Java의 Cipher에서 암호화 및 복호화 설정이 다를 수 있다는 점을 의심했다.
실제 원인
Vue의 crypto.subtle API는 기본적으로 SHA-256을 MGF1 해시 알고리즘으로 사용했지만, Java에서 MGF1의 기본 해시 알고리즘은 SHA-1로 설정되어 있어, BadPaddingException이 발생했다.
최종 해결을 위한 시행착오(optional)
문제 해결을 위해 Node.js와 Java로 비슷한 환경을 재현하여 MGF1 해시 알고리즘의 불일치 문제를 확인했다. Node.js에서는 node-forge를 사용해 MGF1 해시 알고리즘을 SHA-256으로 맞추었고, Java에서는 OAEPParameterSpec을 사용해 MGF1 해시 알고리즘을 명시적으로 SHA-256으로 설정했다.
해결방법
Java의 Cipher 클래스를 이용한 복호화 코드에서 OAEPParameterSpec을 사용해 MGF1 해시 알고리즘을 SHA-256으로 명시적으로 설정함으로써 프론트엔드와 백엔드 간 암호화 설정을 일치시켰다. 결과적으로 BadPaddingException 없이 정상적으로 복호화가 이루어졌다.
import javax.crypto.Cipher;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.MGF1ParameterSpec;
import javax.crypto.spec.PSource;
@Override
public String decrypt(String encryptedData, PrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
OAEPParameterSpec oaepParams = new OAEPParameterSpec(
"SHA-256",
"MGF1",
new MGF1ParameterSpec("SHA-256"),
PSource.PSpecified.DEFAULT
);
cipher.init(Cipher.DECRYPT_MODE, privateKey, oaepParams);
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedData));
String decrypted = new String(decryptedBytes, StandardCharsets.UTF_8);
return decrypted;
}