猿问

java.security.SignatureException:签名编码无效。

使用 Signature.verify 验证签名时,我收到“签名的无效编码”异常。使用 Azure 服务验证相同签名时,将验证签名。


我有一个散列数据 (SHA-256)、一个公钥和一个我要验证的签名。使用 com.microsoft.azure.keyvault.KeyVaultClient.sign 方法接收签名,签名算法为“ES256”。


这有效(使用 ES256 算法):


    com.microsoft.azure.keyvault.KeyVaultClient keyVaultClient;

    String keyPairIdentifier;


    boolean verify(byte[] hashData, byte[] signature, JsonWebKeySignatureAlgorithm signingAlgorithm) {

        com.microsoft.azure.keyvault.models.KeyVerifyResult result = keyVaultClient.verify(keyPairIdentifier, signingAlgorithm, hashData, signature);

        return result.value().booleanValue();

    }

这失败了(证书持有存储在 Azure keyvault 中的相同公钥):


    Signature ecdsaSign = Signature.getInstance("SHA256withECDSA");

    ecdsaSign.initVerify(certificate.getPublicKey());

    ecdsaSign.update(hashData);

    ecdsaSign.verify(signature)

预期结果 - true(签名已验证)


实际结果:


java.security.SignatureException: Could not verify signature

    at sun.security.ec.ECDSASignature.engineVerify(ECDSASignature.java:325)

    at java.security.Signature$Delegate.engineVerify(Signature.java:1222)

    at java.security.Signature.verify(Signature.java:655)

    at TestKV.KeyVault.VerifyDPSignature.verifySignatureUsingCertificate(VerifyDPSignature.java:143)

    at TestKV.KeyVault.VerifyDPSignature.main(VerifyDPSignature.java:104)

Caused by: java.security.SignatureException: Invalid encoding for signature

    at sun.security.ec.ECDSASignature.decodeSignature(ECDSASignature.java:400)

    at sun.security.ec.ECDSASignature.engineVerify(ECDSASignature.java:322)

    ... 4 more 

Caused by: java.io.IOException: Sequence tag error

    at sun.security.util.DerInputStream.getSequence(DerInputStream.java:330)

    at sun.security.ec.ECDSASignature.decodeSignature (ECDSASignature.java:376)


千巷猫影
浏览 1013回答 3
3回答

湖上湖

Azure 的 JWS只是连接固定大小的 r 和 s 的 I2OSP,但 Java JCE 与大多数但不是所有标准一样,使用 ASN.1 DER 编码,例如 rfc3279(注意:现在有用于 ECDSA 的 OID 和其他哈希值)。要将 JWS/plain 转换为 DER,请参阅我的(交叉)https://security.stackexchange.com/questions/174095/convert-ecdsa-signature-from-plain-to-der-format以获得 C 方法,但 Java 使它更容易,因为BigInteger为你做了一半的工作:// byte[64] plain contains the JWS-style r,s (de-base64-ed if necessary)byte[] r = new BigInteger(1,Arrays.copyOfRange(plain,0,32)).toByteArray();byte[] s = new BigInteger(1,Arrays.copyOfRange(plain,32,64)).toByteArray();byte[] der = new byte[6+r.length+s.length]; der[0] = 0x30; der[1] = der.length-2; int o = 2;der[o++] = 2; der[o++] = (byte)r.length; System.arraycopy (r,0, der,o, r.length); o+=r.length;der[o++] = 2; der[o++] = (byte)s.length; System.arraycopy (s,0, der,o, s.length); //o+=s.length;更正并添加: Java 9 向上也使用诸如 之类的算法名称直接处理此问题SHA256withECDSAinP1363format,并且在所有版本的 Java 上,如果您添加 BouncyCastle,它会使用诸如SHA256withPLAIN-ECDSA或 之类的名称来处理此问题SHA256withCVC-ECDSA。看java.security.Signature签名方法和Java ECDSAwithSHA256签名长度不一致如何指定签名长度。

千万里不及你

dave_thompson_085 - 谢谢!你附上的代码有一些错误,签名部分的标签应该是0x02,而不是0x30,而且你复制第一部分后没有增加o。这是更改后的代码:    byte[] r = new BigInteger(1,Arrays.copyOfRange(signature,0,32)).toByteArray();    byte[] s = new BigInteger(1,Arrays.copyOfRange(signature,32,64)).toByteArray();    byte[] der = new byte[6+r.length+s.length];    der[0] = 0x30; // Tag of signature object    der[1] = (byte)(der.length-2); // Length of signature object    int o = 2;    der[o++] = 0x02; // Tag of ASN1 Integer    der[o++] = (byte)r.length; // Length of first signature part    System.arraycopy (r,0, der,o, r.length);    o += r.length;    der[o++] = 0x02; // Tag of ASN1 Integer    der[o++] = (byte)s.length; // Length of second signature part    System.arraycopy (s,0, der,o, s.length);格式更改后,我没有得到“序列标记错误”异常。但是验证还是失败了。

精慕HU

在我的例子中,我只需要先将签名的原始字节解码为 Base64。byte[] signatureBytes = Base64.getDecoder().decode(signature.getBytes());    byte[] r = new BigInteger(1,Arrays.copyOfRange(signatureBytes,0,32)).toByteArray();    byte[] s = new BigInteger(1,Arrays.copyOfRange(signatureBytes,32,64)).toByteArray();
随时随地看视频慕课网APP

相关分类

Java
我要回答