Search code examples
javaencryptionrsasignaturesha256

Why is the signature verification not working despite using the same parameters?


I have trouble making this simple test for verifying a signature work. I have the Public and Private Key from the same Keystore, I do the exact steps for signing and verifying, but yet the verifying is always false.

Should I suspect some encoding problem?

@Test
        public void authShouldBeOk() throws Exception {
                // 
                String initialMessage = pCrypto.getKey();
                Signature sign = Signature.getInstance("SHA256withRSA");
                String paramPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArWTAz/TDWyrbXGKva+8ankn/USEmUSj1UM3JQkYnX1ilWxd0kCb+PodWcrp/l4rbBef5xyFIIEZmkWNFuIUVKUvPX35Fg4DVvhbZekMRIjh4lRBi3wyXV41OqTNr35eYBiFbHYJwLeUn5eeQqB0h4S7NmPCV1t6Ih5TWOO8vLJLC0wEwEzapopXkISj9FNXHwQHHzQGvSnqxQhWBLYwFJ9ECGNDXzlALHzshM1EGthKe5zddQoRm63bxGuBsxa75qJdmOcL/xLMnad3/FPntNcC573VM83aI0xh2lzQqhG4ZzjNvVUH+NiYwDmZgE9DVk9vP3XwR/1anBz11vTwksQIDAQAB";
                
                Cipher cipher = Cipher.getInstance("RSA");

                byte[] privBytes = pCrypto.getPriv().getEncoded();

                cipher.init(Cipher.ENCRYPT_MODE, pCrypto.getPriv());
                byte[] encrypted = cipher.doFinal(initialMessage.getBytes());
                String encryptedMessage = Base64.getEncoder().encodeToString(encrypted);

                sign.initSign(pCrypto.getPriv());
                sign.update(encrypted);

                byte[] signatureBytes = sign.sign();

                String signatureMessage = Base64.getEncoder().encodeToString(signatureBytes);

                PublicKey certP = pCrypto.getPublic();
                sign.initVerify(certP);
                sign.update(signatureBytes);
                boolean b = sign.verify(signatureBytes);

                // should be true
                assert(b)

                // when
                authController.getAuth(encryptedMessage, signatureMessage, paramPublicKey);
        }

Solution

  • No, you don't perform the same steps when signing and verifying: When signing with sign.update(encrypted) the content of encrypted is signed. Therefore, when verifying, sign.update(encrypted) has to be used, but instead sign.update(signatureBytes) is applied, which is why verification fails. If you fix this, verification is successful.

    Apart from that, it is unclear why you are using the private key to encrypt the message. Encryptions are performed with the public key. Java technically allows encryption with the private key, but this is actually for low level signing, not encryption for confidentiality (which would also be pointless, since anyone could decrypt with the public key).


    Note that the padding should be specified when instantiating the Cipher object, e.g. RSA/ECB/PKCS1PADDING, otherwise provider dependent default values are used.
    In addition, the encoding/decoding should specify the charset used, e.g. initialMessage.getBytes(StandardCharsets.UTF_8), otherwise platform dependent default values are applied.
    Both can lead to incompatibilities between encryption and decryption.