2

我正在实现椭圆曲线数字签名算法(ECDA),而没有重用使用 java 编程实现的标准。我的程序是这样的。

     public BigInteger[] sign(){

    // signature generation here

        rs[0] = r;
         rs[1] = s;
        return rs;
      }

但我希望它返回字节数组,就像在实现的标准算法中一样。

    public byte[] sign()
    {      
           // my signature generation here already done
            rs[0] = r;
            rs[1] = s;   
          // help me here what shall i do instead of returning array of two BigIntegers
    }  

数字签名的 sign() 方法如何将 (r,s) 值组合到数组字节中以返回调用主体。在调用主体时,我必须返回 get (r,s) 对以验证我的签名。

         // something like this
            public boolean verify(byte[] sigbyte )
              {

                 rArr =  // help me here
                 sArr = // help me here
                r =new BigInteger(rArr);
                s = new BigInteger(sArr);

               // i have already done signature verification here

               }           
4

2 回答 2

3

FIPS 186-3 未指定如何将 ECDSA 签名序列化为字节数组。

我所知道的所有 API 和协议都使用两种不同的标准方式之一:

1) 零填充 r 和 s 所以它们是 n 的长度(G 的阶)并将它们连接起来:

byte[] rArr = toUnsignedByteArray(r);
byte[] sArr = toUnsignedByteArray(s);
int nLength = (n.bitLength()+7)/8;
byte[] res = new byte[2*nLength];
System.arraycopy(rArr, 0, res, nLength - rArr.length, rArr.length);
System.arraycopy(sArr, 0, res, 2* nLength - sArr.length, nLength);

toUnsignedByteArray是这样的:

byte[] toUnsignedByteArray(BigInteger bi){
  byte[] ba = bi.toByteArray();
  if(ba[0] != 0){
    return ba;
  }
  else
  {
    byte[] ba2 = new byte[ba.length - 1];
    System.arraycopy(ba, 1, ba2, 0, ba.length - 1);
    return ba2;
  }
}

2) 使用以下 ASN.1 结构的 DER 编码:

ECDSASignature ::= SEQUENCE {
    r   INTEGER,
    s   INTEGER
}

假设 n 最多为 487 位(如果您需要支持更大的密钥,长度编码会变得更复杂一些):

byte[] rArr = r.toByteArray();
byte[] sArr = s.toByteArray();
byte[] res = new byte[6 + rArr.length + sArr.length];
res[0] = 0x30;
res[1] = 4 + rArr.length + sArr.length;
res[2] = 0x02;
res[3] = rArr.length;
System.arraycopy(rArr, 0, res, 4, rArr.length);
res[4+rArr.length] = 0x02;
res[5+rArr.length] = sArr.length;
System.arraycopy(sArr, 0, res, 6+rArr.length, sArr.length);

解码做这样的事情(这是非常基本的,非防错解码):

if(enc[0]!=0x30) return false; // bad encoding
if(enc[1]&0x80!=0) return false; // unsupported length encoding
if(enc[2]!=0x02) return false; // bad encoding
if(enc[3]&0x80!=0) return false; // unsupported length encoding
int rLength = enc[3];
byte[] rArr = new byte[rLength];
System.arraycopy(enc, 4, rArr, 0, rLength);
BigInteger r = new BigInteger(rArr);
if(enc[4+rLength]!=0x02) return false; // bad encoding
if(enc[5+rLength]&0x80!=0) return false; // unsupported length encoding
int sLength = enc[5+rLength];
byte[] sArr = new byte[sLength];
System.arraycopy(enc, 6+rLength, sArr, 0, sLength);
BigInteger s = new BigInteger(sArr);

Java 安全架构使用第二种编码。

于 2012-06-07T05:55:08.927 回答
1

FIPS 186-3 文档指定了算法,但没有讨论如何在更高级别的协议中使用该算法。我最熟悉的将签名编码为字节数组的标准是RFC 3279,特别是第 2.2.2 节。这可能是默认 Oracle 提供程序使用的编码方法。

于 2012-06-07T03:09:35.953 回答