java.security.SignatureException:签名的编码无效。Azure验证的签名

java.security.SignatureException:签名的编码无效。Azure验证的签名,java,azure,signature,Java,Azure,Signature,使用signature.verify验证签名时,我收到“签名编码无效”异常。 使用Azure服务验证同一签名时,将验证该签名 我有一个哈希数据(SHA-256)、一个公钥和一个签名,我正试图验证它。 使用com.microsoft.azure.keyvault.KeyVaultClient.sign方法接收签名,签名算法为“ES256” 这是可行的(使用ES256算法): com.microsoft.azure.keyvault.KeyVaultClient-KeyVaultClient; 字符

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

我有一个哈希数据(SHA-256)、一个公钥和一个签名,我正试图验证它。 使用com.microsoft.azure.keyvault.KeyVaultClient.sign方法接收签名,签名算法为“ES256”

这是可行的(使用ES256算法):

com.microsoft.azure.keyvault.KeyVaultClient-KeyVaultClient;
字符串键对标识符;
布尔验证(字节[]哈希数据、字节[]签名、JsonWebKeySignatureAlgorithm签名算法){
com.microsoft.azure.keyvault.models.KeyVerifyResult结果=keyVaultClient.verify(keyPairIdentifier、签名算法、hashData、签名);
返回结果.value().booleanValue();
}
此操作失败(证书持有存储在Azure keyvault中的相同公钥):

Signature ecdsaSign=Signature.getInstance(“SHA256withECDSA”);
ecdsaSign.initVerify(certificate.getPublicKey());
ecdsaSign.update(hashData);
ecdsaSign.verify(签名)
预期结果-真(签名已验证)

实际结果:

java.security.SignatureException:无法验证签名
位于sun.security.ec.ECDSASignature.engineVerify(ECDSASignature.java:325)
位于java.security.Signature$Delegate.engineVerify(Signature.java:1222)
位于java.security.Signature.verify(Signature.java:655)
在TestKV.KeyVault.VerifyDPSignature.verifySignatureUsingCertificate(VerifyDPSignature.java:143)
位于TestKV.KeyVault.VerifyDPSignature.main(VerifyDPSignature.java:104)
原因:java.security.SignatureException:签名的编码无效
位于sun.security.ec.ECDSASignature.decodeSignature(ECDSASignature.java:400)
位于sun.security.ec.ECDSASignature.engineVerify(ECDSASignature.java:322)
... 4更多
原因:java.io.IOException:序列标记错误
位于sun.security.util.DerInputStream.getSequence(DerInputStream.java:330)
位于sun.security.ec.ECDSASignature.decodeSignature(ECDSASignature.java:376)
Azure不支持JWS,但Java JCE与大多数但并非所有标准一样,使用ASN.1 DER编码(注意:现在有用于ECDSA和其他哈希的OID)

要将JWS/plain转换为DER,请参阅my(cross)中的C方法,但Java使之更容易,因为
biginger
为您完成了一半的工作:

// 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;

2020-05更正并添加:Java 9 up也直接使用算法名称处理此问题,如
SHA256 with ECDSainp1363 format
,并且在所有版本的Java上,如果添加BouncyCastle,它会使用
SHA256 with plain ECDSA
SHA256 with CVC ECDSA
等名称。请参见和。

dave_thompson_085-谢谢! 您附加的代码中有一些错误,签名部分的标记应该是0x02,而不是0x30,并且在复制第一部分后没有增加o。 这是更改后的代码:

byte[]r=新的BigInteger(1,Arrays.copyOfRange(signature,0,32)).toByteArray();
byte[]s=新的BigInteger(1,Arrays.copyOfRange(signature,32,64)).toByteArray();
字节[]der=新字节[6+r.length+s.length];
der[0]=0x30;//签名对象的标记
der[1]=(字节)(der.length-2);//签名对象的长度
INTO=2;
der[o++]=0x02;//ASN1整数的标记
der[o++]=(字节)r.length;//第一个签名部分的长度
System.arraycopy(r,0,der,o,r.长度);
o+=r.长度;
der[o++]=0x02;//ASN1整数的标记
der[o++]=(字节)s.length;//第二签名部分的长度
System.arraycopy(s,0,der,o,s.长度);
在格式更改之后,我没有得到“序列标记错误”异常。但核查仍然失败


谢谢

它用于验证原始数据而不是散列数据:ecdsaSign.update(data);