跳到主要内容
ecdsa.com
Java (JDK)Java

错误信息

java.security.SignatureException: Invalid encoding for signature

`Caused by` 中的详细信息会随破坏 DER 解析的那个字节而变化(例如“Invalid lenByte”或“Not the correct tag”)。反过来的错误则悄无声息:把 DER 签名传给 `SHA256withECDSAinP1363Format` 不会抛出异常,验证结果只是 `false`。

Java (JDK) — Signature.verify with SHA256withECDSA (SunEC)

含义

`SHA256withECDSA` 按 ASN.1 DER(`30 … 02 r 02 s`)解析 ECDSA 签名,而你传入的字节不是 DER。几乎在所有情况下,它们都是另一种标准编码:JWS、WebCrypto 和许多非 Java 库使用的定长 r‖s 拼接格式(IEEE P1363)——P-256 为 64 字节。JDK 也能验证这种格式,对应的算法名称是 `SHA256withECDSAinP1363Format`。

原因

修复方法

  1. 1.

    用 P1363 算法验证原始签名

    自 JDK 9 起,SunEC 提供程序为 SHA-1/224/256/384/512 与 ECDSA 的组合提供这类算法。无需转换。

    java
    // JWS (ES256) and WebCrypto subtle.sign() produce raw r||s; WebAuthn and X.509 use DER.
    Signature verifier = Signature.getInstance("SHA256withECDSAinP1363Format");
    verifier.initVerify(publicKey);
    verifier.update(signedBytes);
    boolean ok = verifier.verify(rawSignature); // 64 bytes for P-256
  2. 2.

    或者把 r‖s 转换为 DER

    如果某个 API 坚持要求 DER(例如 HSM 提供程序),就把 r 和 s 编码为最短形式的 DER INTEGER,放进一个 SEQUENCE 中。DER ⇄ 原始格式转换器可以检查你的转换结果。

    java
    static byte[] p1363ToDer(byte[] raw) {
      int n = raw.length / 2;
      byte[] r = derInteger(java.util.Arrays.copyOfRange(raw, 0, n));
      byte[] s = derInteger(java.util.Arrays.copyOfRange(raw, n, raw.length));
      var out = new java.io.ByteArrayOutputStream();
      out.write(0x30);
      out.write(r.length + s.length); // < 128 for P-256 and P-384
      out.writeBytes(r);
      out.writeBytes(s);
      return out.toByteArray();
    }
    
    static byte[] derInteger(byte[] v) {
      int i = 0;
      while (i < v.length - 1 && v[i] == 0) i++;
      boolean pad = (v[i] & 0x80) != 0;
      byte[] out = new byte[2 + (pad ? 1 : 0) + v.length - i];
      out[0] = 0x02;
      out[1] = (byte) (out.length - 2);
      System.arraycopy(v, i, out, pad ? 3 : 2, v.length - i);
      return out;
    }

相关错误

浏览完整的签名错误库