当前位置: 首页 > 知识库问答 >
问题:

当r或s为负时如何验证ECDSA签名

孔阎宝
2023-03-14

我正在写一段代码来检查签名

如果r和s是正的,我下面的代码可以工作,否则验证失败。

我的代码是

PublicKey publicKey = cert.getPublicKey();
Signature ecdsaVerify = Signature.getInstance("SHA256withECDSA", new BouncyCastleProvider());
ecdsaVerify.initVerify(publicKey);
ecdsaVerify.update(message.getBytes());
valid = ecdsaVerify.verify(sigDer);

我的签名是从原始的r和s开始,以der格式创建的。请记住,如果r或s是负数,要在der格式中添加“00 ”,我还使用了BouncyCastle ANS1类

ASN1EncodableVector v = new ASN1EncodableVector();
v.add(new ASN1Integer(r));
v.add(new ASN1Integer(s));          
DERSequence der = new DERSequence(v);

但是我仍然能够验证r和s是否为正,如果其中一个为负,我就不能验证(初始字节大于0x80)

我错过了什么?

-----编辑-----

我已经创建了一个更好的代码来解释我的问题

static boolean verifySignature(String messagePlain, String certificatePEM, String rHex, String sHex)
{
    boolean valid = false;
    Security.addProvider(new BouncyCastleProvider());
        
    try 
    {       
        BigInteger r = new BigInteger(rHex, 16);
        BigInteger s = new BigInteger(sHex, 16);

        ASN1EncodableVector v = new ASN1EncodableVector();
        v.add(new ASN1Integer(r));
        v.add(new ASN1Integer(s));          
        DERSequence der = new DERSequence(v);
        byte[] signature = der.getEncoded();

        CertificateFactory cf = CertificateFactory.getInstance("X.509", new BouncyCastleProvider());
        InputStream certStream = new ByteArrayInputStream(certificatePEM.getBytes());
        X509Certificate x509cert  = (X509Certificate)cf.generateCertificate(certStream);
            
        PublicKey publicKey = x509cert.getPublicKey();
        Signature ecdsaVerify = Signature.getInstance("SHA256withECDSA", new BouncyCastleProvider());
        ecdsaVerify.initVerify(publicKey);
        ecdsaVerify.update(messagePlain.getBytes());
        valid = ecdsaVerify.verify(signature);

    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (CertificateException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (SignatureException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return valid;
}

我的主要

public static void main( String[] args )
{       
    String messNOT = "dc03d9c5676cc8a76b59214b86214b865d01022cdd518622c721967c7c23abbc133c133c133c133c133c133c2c651a2419a64d4a598d19cd202326752cdefe3204031f00000506675b20383373";
    String rNOT = "663e5dd196f22f03e3aba8f6652d1ca92d207088911987f44048a4b85ec36e91";
    String sNOT = "84c4ca1678dce85b19b2ddb378f4a1df878cec21ce6fbab9292576281602041d";
    String certNOT ="-----BEGIN CERTIFICATE-----\r\n"
            + "MIICzDCCAlOgAwIBAgICANMwCgYIKoZIzj0EAwMwdjELMAkGA1UEBhMCQ0gxDjAM\r\n"
            + "BgNVBAoMBUFkbWluMREwDwYDVQQLDAhTZXJ2aWNlczEiMCAGA1UECwwZQ2VydGlm\r\n"
            + "aWNhdGlvbiBBdXRob3JpdGllczEgMB4GA1UEAwwXYWJuYS1jc2NhLXN3aXR6ZXJs\r\n"
            + "YW5kLTIwHhcNMjIwMjA5MTQxNDM2WhcNMjgwMjE0MTQxNDM2WjAaMQswCQYDVQQG\r\n"
            + "EwJDSDELMAkGA1UEAxMCVlMwggEzMIHsBgcqhkjOPQIBMIHgAgEBMCwGByqGSM49\r\n"
            + "AQECIQCp+1fboe6pvD5mCpCdg41ybjv2I9UmICggE0gdH25TdzBEBCB9Wgl1/Cww\r\n"
            + "V+72dTBBev/n+4BVwSbcXGzpSktE8zC12QQgJtxcbOlKS0TzMLXZu9d8v5WEFilc\r\n"
            + "9+HOa8zcGP+MB7YEQQSL0q65y35XyyxLSC/8gbevud4n4eO9I8I6RFO9ms4yYlR+\r\n"
            + "+DXD2sT9l/hGGhRhHcnCd0UTLe2OVFwdVMcvBGmXAiEAqftX26Huqbw+ZgqQnYON\r\n"
            + "cYw5eqO1Yab3kB4OgpdIVqcCAQEDQgAEEw6HdjvJQhXyiitwaWZ4DmypigCjdJen\r\n"
            + "oRjW7Tz3n1Mlqf2dwwgYcwHB7N6zvLpQ2aZmILOicbyvSKHCZHaztqNRME8wHwYD\r\n"
            + "VR0jBBgwFoAU5/zrAS4O3jj5DvGKXM5XDxMEyXMwFQYHZ4EIAQEGAgQKMAgCAQAx\r\n"
            + "AxMBVjAVBgNVHSUBAf8ECzAJBgdngQgBAQsBMAoGCCqGSM49BAMDA2cAMGQCMEG4\r\n"
            + "zJn2/N85NLsDM58+jB0oyFTB152gPyAcdhq04gLpXynW2qbSNfE6Fgw34Qm+vAIw\r\n"
            + "AZSAdepOu4r6T+hj1p8Q3HFlSvlByjpr/b2VNiMXup6v3O7BnGqbyRFgMlc3c/BB\r\n"
            + "-----END CERTIFICATE-----";
        
    String messYES = "dc03d9c5d9b71fe6fe336de4e53214665d01022cdb5bd2b3c549cd1da93c5bd458135c6f57fc133c133c133c9e2e4d0d28043132b09e1ae53f5c52853f78fe370403900000050659e9269f2cb8";
    String rYES = "473c2e821b49bffc32e49cb7ed5f0645a32ad76c8258ebd4ec9aca56";
    String sYES = "77c7e16920143444cf68d3891436b1a0189e08d06b104fdb247cd3ef";
    String certYES ="-----BEGIN CERTIFICATE-----\r\n"
            + "MIID7jCCAdagAwIBAgIBAjANBgkqhkiG9w0BAQsFADAuMQswCQYDVQQGEwJVVDEP\r\n"
            + "MA0GA1UECgwGVVRPUElBMQ4wDAYDVQQDDAVDU0NBMTAeFw0yMTEwMjAxMjEwMTha\r\n"
            + "Fw0yNDEwMTkxMjEwMThaMBoxCzAJBgNVBAYTAlVUMQswCQYDVQQDDAJBMTCCARMw\r\n"
            + "gdQGByqGSM49AgEwgcgCAQEwKAYHKoZIzj0BAQIdANfBNKomQ2aGKhgwJXXR14ew\r\n"
            + "nwdXl9qJ9X7IwP8wPAQcaKXmLKnObBwpmAOmwVMLUU4YKtiwBCpZytKfQwQcJYD2\r\n"
            + "PM/kQTiHBxOxqSNp4z4hNdJm27NyOGxACwQ5BA2QKa0sflz0NAgjsqh9xoyeTOMX\r\n"
            + "TB5u/e4SwH1Yqlb3csBybyTGuJ5OzawkNUuemcqj9tN2FALNAh0A18E0qiZDZoYq\r\n"
            + "GDAlddD7mNEWvEtt3ryjpaeTnwIBAQM6AATBWidMSa55454DGh7j+IkWfswMr33v\r\n"
            + "jwQiNONEo+LRC3+yL5dDcfdCfDQqrMdAC8PfR/8cMVRMJ6M6MDgwHwYDVR0jBBgw\r\n"
            + "FoAUCoYRgjvodzM4CDLB5bSdQMEXyBIwFQYDVR0lAQH/BAswCQYHZ4EIAQEOAjAN\r\n"
            + "BgkqhkiG9w0BAQsFAAOCAgEAe+CFC4CEPjGx9vDi+zWKCho6QtwGI/MVwWqmgoRS\r\n"
            + "UgBgMrtAq/tYeWz8tD6kZzLW8M5PxGLRUyd4oBt2WzdNqHGJ72w4NDeUm33DH40g\r\n"
            + "bivStdcHIeYxxNCq1WFiN1oAfqpi+XdzfSgRSSuYY+8sZoGxg2A7ydMIGN4CuWGD\r\n"
            + "Op1T/BViespPkZWFo9P8wdetaV3mqjLu7oiDISkfxQhGRFMpOJrw/blxhl5ZgRSh\r\n"
            + "Zrvpz8nzMZitxPompJLCL0rHCofLwPRKvFF4W5DQt0n3MWVrXjHtLFC4haO05Ugw\r\n"
            + "VQy+KgWwE+AhdkUJGHrSUtEajiscgULU6l/UIuOpmF68VqHvx6EEM7UpLbXXQkuM\r\n"
            + "lE+9YWKRj0bssVKtoShw3m+TDsrk1xfGzk6lLPPpcObvdNkRiPufoMCD0Ci8h05S\r\n"
            + "oQCWLFLJcOrn8yGVSwaHgbcsuVX15rCneeXIhGbRlnnqjD/Z+vWGh3RleOSvdIDM\r\n"
            + "hnYDoRm7gZs0b5hIZ6M9kBqDTf61rraV7+0LzFuWMIq7+ree2eCIeBv26f+LRFXy\r\n"
            + "UDGWqEfiZR2ytyY54VUc/ptMVwkz1EethHu2JqZxAp1qkWUci++AZN43Typ/NJ9r\r\n"
            + "qZjM5nSSQzQDVvfE8seLe/78mmwmM9pD+NMGxWMpdA0MBwJN0Nzhlcp/rnWKtp7u\r\n"
            + "3jo=\r\n"
            + "-----END CERTIFICATE-----";
                    
    System.out.println("valid: " + verifySignature(messNOT, certNOT, rNOT, sNOT));
    System.out.println("valid: " + verifySignature(messYES, certYES, rYES, sYES));
}

输出

valid: false
valid: true

使用openssl可以验证两个签名,为什么它在java中不工作?

共有1个答案

孟自强
2023-03-14

问题的原因不是r和/或s以字节开头

如果不是UTF-8编码而是十六进制解码,则有问题的消息MessNot的验证成功。相反,对于工作消息MessYES,如果MessYES是UTF-8编码,则验证成功。

由于两个消息在发布的代码中都是UTF-8编码,这解释了为什么< code>messNOT的验证失败,而< code>messYES的验证成功。

为了成功验证这两条消息,最简单的方法是将消息作为verifySignature()中的byte[]

static boolean verifySignature(byte[] messagePlain, String certificatePEM, String rHex, String sHex) {
    ...
    ecdsaVerify.update(messagePlain);
    ...
}

并事先执行转换(如上所述):

import org.bouncycastle.util.encoders.Hex;
...
System.out.println("valid: " + verifySignature(Hex.decode(messNOT), certNOT, rNOT, sNOT)); // valid: true
System.out.println("valid: " + verifySignature(messYES.getBytes(StandardCharsets.UTF_8), certYES, rYES, sYES)); // valid: true

这与通过OpenSSL进行的验证是一致的,这是应该的。

对了,BouncyCastle还支持< code > sha 256 with plain-ECD sa 的IEEE P1363(即r|s)格式,所以不需要转换成ASN.1/DER。

 类似资料:
  • 我尝试使用java客户端对内容进行签名,然后在服务器(nodejs)上进行验证。我的客户端签名函数使用ECDSA并返回一个< code>byte[]。我可以访问服务器上包含< code>publicKey的< code>x和< code>y坐标值。 是否可以找到构成签名的和?如何将从上面获得的<code>字节〔〕对或<code>DER编码的签名 编辑: 谢谢戴夫的评论,我正在使用这个SO答案中指示

  • 我使用Bouncy Castle/Java生成签名,因为它给出了两个带有R,S值的大整数。现在的要求是使用OpenSSL命令来验证。 例如:我要签名的消息是,运行上面的实用程序将返回R和S值,分隔符为'',在base64中编码为 现在在openssl端: 它无法验证,我被困住了--在openssl端不断得到“验证失败”的错误。

  • 我在开发安全系统时遇到了以下问题: 我们收到一些数据,我们必须通过签名进行验证。签名算法是ecdsa-with-SHA256,openssl_verify()似乎没有这个选项。已经尝试过搜索像phpseclib这样的独立PHP库了——也不走运,ecdsa-with-SHA1似乎是他们提供的最佳选择。 对于这个问题,什么是合适的解决方案?也许我错过了一些实现这种功能的库?

  • 我需要使用256位的私钥为ECDSA的256位散列签名,就像比特币一样。由于缺少python中的ECDSA文档,我感到绝望。 我在网上找到了很多代码,但是没有什么比或类似的,我发现的所有东西都是我不懂的大量数学代码,但他们使用ecdsa库(我不知道为什么他们不在一个库中添加一个用于签名的签名函数,而是在使用库时需要一页代码?)。 这是目前为止我找到的最好的代码: 但我就是不能相信这样的代码,因为我

  • 我应该如何更改A方法?任何想法都是好的。提前谢谢。

  • 最近,我一直在尝试构建一个Java库来解释和验证NZ Covid传递。在签名验证之前(这个过程中稍微重要的一部分),我已经让代码变得更好或更坏。完整的代码可以在这里找到,但是仍然很粗糙。 验证器本身在这里可用,有一个配套测试。新冠通行证的技术规范在这里。至少有一个相关部分。 在与另一个开发人员合作后,我想我已经确定了解释提供的公钥。下面的代码(删除了调试输出)。公钥详细信息来自这里 该错误可能存在