Java 数字签名的How sign()方法将(r,s)值组合到数组字节中以返回调用主体

Java 数字签名的How sign()方法将(r,s)值组合到数组字节中以返回调用主体,java,cryptography,Java,Cryptography,我正在实现椭圆曲线数字签名算法ECDA,而没有重用使用java编程实现的标准算法 public BigInteger[] sign(){ // signature generation here rs[0] = r; rs[1] = s; return rs; } 但我希望它返回字节数组,就像在标准算法中实现的那样 public byte[] sign() {

我正在实现椭圆曲线数字签名算法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值组合到数组字节中以返回调用主体。在调用body时,我必须将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

               }           

FIPS 186-3文件规定了算法,但未说明如何在更高级别协议中使用该算法。我最熟悉的将签名编码到字节数组的标准是,特别是第2.2.2节。这可能是默认Oracle提供程序使用的编码方法。

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

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

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

1零pad 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安全体系结构使用第二种编码。

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

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

1零pad 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安全体系结构使用。

为什么要将其作为字节[]?根据FIPS 186-3第C.2.1节,应为0和1的字符串位字符串。由于它是一个字节数组,所以在将字节[]转换为字符串时,很难去掉s字节的额外前导零。还是我遗漏了什么?通常只使用八位字节。您需要删除或附加左侧大小的00h八位字节值,这是正确的。您必须将字节数组复制到另一个字节数组。困难的不是真的。一个重要的,很难得到正确的和浪费记忆?当然可以。为什么要它作为字节[]?根据FIPS 186-3第C.2.1节,应为0和1的字符串位字符串。由于它是一个字节数组,所以在将字节[]转换为字符串时,很难去掉s字节的额外前导零。还是我遗漏了什么?通常只使用八位字节。您需要删除或附加左侧大小的00h八位字节值,这是正确的。您必须将字节数组复制到另一个字节数组。困难的不是真的。一个重要的,很难得到正确的和浪费记忆?当然可以。我想在签名验证中恢复原始的r,s对。在第一个代码中,很容易将r,s恢复为大整数。但是第二个我发现很难添加一些想法或代码,这是错误的,ASN.1整数和Java整数一样是有符号的。基本上,你不需要任何转换。一个I2OS整数到八位字节字符串函数也会更好,如果只用于内存使用的话。你能修改一下吗+1@owlstead:已将ASN.1编码转换回补码-2对不起,我手工编码ASN.1后的时间太长了。你能扩展第二条评论吗,我不确定我能看到问题所在。嗯,再想想可能没问题。感谢您播放ASN.1部分。这是对ASN.1进行编码的一种有点直接的方式,但可能已经足够了。@owlstead:我真的认为,对于如此小的结构和如此低的层次,依赖ASN.1-encoder//parser是不正确的。我敢肯定,我做的事情与我上次实施ECDSA时没有太大不同,那是在5年前。我可能会使用ByteArrayAppender,并在长度编码上做更多的工作,但我认为在这个级别上不太合适。在PKCS1v1.5中,通常也不会看到用于DigestInfo的ASN.1引擎。我想恢复原始版本
r、 我的签名验证中有一对。在第一个代码中,很容易将r,s恢复为大整数。但是第二个我发现很难添加一些想法或代码,这是错误的,ASN.1整数和Java整数一样是有符号的。基本上,你不需要任何转换。一个I2OS整数到八位字节字符串函数也会更好,如果只用于内存使用的话。你能修改一下吗+1@owlstead:已将ASN.1编码转换回补码-2对不起,我手工编码ASN.1后的时间太长了。你能扩展第二条评论吗,我不确定我能看到问题所在。嗯,再想想可能没问题。感谢您播放ASN.1部分。这是对ASN.1进行编码的一种有点直接的方式,但可能已经足够了。@owlstead:我真的认为,对于如此小的结构和如此低的层次,依赖ASN.1-encoder//parser是不正确的。我敢肯定,我做的事情与我上次实施ECDSA时没有太大不同,那是在5年前。我可能会使用ByteArrayAppender,并在长度编码上做更多的工作,但我认为在这个级别上不太合适。在PKCS1 v1.5中,你通常也看不到用于DigestInfo的ASN.1引擎。它是基于ANSI X9.62/63的,据我所知,这个RFC是符合的。另一种表示方法通常使用两个大端整数,其大小与Rasmus指出的顺序相同。它是基于ANSI X9.62/63的,据我所知,该RFC符合ANSI X9.62/63。另一种表示方法通常使用两个大端整数,其大小与Rasmus指出的顺序相同。