【问题标题】:Fixed length 64 Bytes EC P-256 Signature with JCE带 JCE 的固定长度 64 字节 EC P-256 签名
【发布时间】:2015-12-03 10:37:36
【问题描述】:

我需要一个带有 NIST P-256 曲线的固定长度 64 字节 ECDSA 签名。

实现必须使用JCE。

以下代码示例可以生成签名并进行验证。

Provider provSign = new SunEC();
Provider provVerify = new SunEC();


    // generate EC key
    KeyPairGenerator kg = KeyPairGenerator.getInstance("EC", provSign);
    ECGenParameterSpec ecParam = new ECGenParameterSpec("secp256r1");
    kg.initialize(ecParam);      
    KeyPair keyPair = kg.generateKeyPair(); 
    PrivateKey privateKey = keyPair.getPrivate();      
    PublicKey publicKey = keyPair.getPublic();

    try
    {
      // export public key                  
      KeyFactory kf = KeyFactory.getInstance("EC", provSign);
      ECPublicKeySpec publicKeySpec = kf.getKeySpec(keyPair.getPublic(), ECPublicKeySpec.class);

      // import public key into other provider
      kf = KeyFactory.getInstance("EC", provVerify);
      publicKey = (PublicKey)kf.generatePublic(publicKeySpec);      
    }
    catch (InvalidKeySpecException ex)
    {                       
      ex.printStackTrace();
    }


      // do test        
      Signature sig = Signature.getInstance("SHA256withECDSA", provSign);
      Signature ver = Signature.getInstance("SHA256withECDSA", provVerify);

      byte[] data = new byte[64];

      // sign
      sig.initSign(privateKey);
      sig.update(data);
      byte [] sign = sig.sign();

      // Working Signature verification
      ver.initVerify(publicKey);
      ver.update(data);
      if (ver.verify(sign) == false)
      {
        throw new Exception("Signature Verification failed");
      }

问题是符号以某种方式编码(我认为是 DER 格式),长度在 70 到 72 字节之间,但我需要 64 字节(未编码/原始)签名。

我尝试过的: 转换为固定长度的 64 字节签名

      DerInputStream derInputStream = new DerInputStream(sign);
      DerValue[] values = derInputStream.getSequence(2);
      byte[] random = values[0].getPositiveBigInteger().toByteArray();
      byte[] signature = values[1].getPositiveBigInteger().toByteArray();


      // r and s each occupy half the array
      // Remove padding bytes
      byte[] tokenSignature = new byte[64];
      System.arraycopy(random, random.length > 32 ? 1 : 0, tokenSignature, random.length < 32 ? 1 : 0,
              random.length > 32 ? 32 : random.length);
      System.arraycopy(signature, signature.length > 32 ? 1 : 0, tokenSignature, signature.length < 32 ? 33 : 32,
              signature.length > 32 ? 32 : signature.length);

      System.out.println("Full Signature length: "+tokenSignature.length+" r length: "+random.length+" s length"+signature.length);

现在如何检查 64 字节的 tokenSignature ??? 我不知道如何将 64 字节的 tokenSignature 转换回正确的格式

      ver.initVerify(publicKey);
      ver.update(data);
      if (ver.verify(???) == false)
      {
        throw new Exception("Signature Verification failed");
      }

我已经使用 BouncyCastle ECDSASigner 实现了 64 字节签名验证。但我不能使用 ECDSASigner,因为它没有扩展 SignatureSpi,因此不能与 JCE 兼容的加密服务器一起使用。

【问题讨论】:

  • 使用DEROutputStream将两个整数排列成一个序列?
  • 感谢 Maarten 现在我明白了。我已经用 DEROutputStream 试过了,第一次就失败了。
  • @MaartenBodewes 您可以将您的评论添加为答案:-)
  • 我今天会尝试,但如果它没有出现,您可能需要添加您的代码作为答案。
  • @MaartenBodewes 好的,我已经添加了答案

标签: java cryptography jce elliptic-curve ecdsa


【解决方案1】:

感谢@MaartenBodewes,我现在可以正常工作了

//How to Check Signature
byte[] r = Arrays.copyOfRange(tokenSignature, 0,tokenSignature.length/2);
byte[] s = Arrays.copyOfRange(tokenSignature, tokenSignature.length/2,tokenSignature.length);

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DEROutputStream derOutputStream = new DEROutputStream(byteArrayOutputStream);
ASN1EncodableVector v=new ASN1EncodableVector();
v.add(new ASN1Integer(new BigInteger(1,r)));
v.add(new ASN1Integer(new BigInteger(1,s)));
derOutputStream.writeObject(new DERSequence(v));
byte[] derSignature = byteArrayOutputStream.toByteArray();

ver.update(data);
if (ver.verify(derSignature) == false)
{
  throw new Exception("Signature Verification failed");
}

【讨论】:

  • 是的,这看起来像我的代码。我当然不会使用文字来表示签名大小(在copyOfRange 中),但否则解决方案应该是正确的。
  • 快速提问:ASN1Integer 的构造函数总是产生一个正整数吗?
  • 我也想先使用 new BigInteger(1,r) 但签名总是正确的。 (在 for 循环中测试了 100 次)
  • 安全总比抱歉好。百万分之一的机会出现十分之九。
  • 我还打印了 r 的第一个字节,它适用于负数和正数。但我也认为它更好,因此改变了答案。
【解决方案2】:

它使用SHA256withPLAIN-ECDSA 而不是SHA256withECDSA 工作

请看下面

Java ECDSAwithSHA256 signature with inconsistent length

【讨论】:

    猜你喜欢
    • 2019-05-25
    • 1970-01-01
    • 2020-05-12
    • 2022-01-18
    • 2012-11-12
    • 2018-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多