使用 Java 将 P1363 格式转换为 ASN.1/DER 格式

2022-01-10 00:00:00 cryptography sign java ecdsa

我有一个使用 MSRCrypto 签署我的 nonce 的服务器.我需要在 Java 中验证随机数.众所周知,MSRCrypto 以 P1363 格式发送它,Java 库要求它以 DER 格式发送.

I have a server which uses MSRCrypto to sign my nonce. I need to verify that nonce in Java. It is well know that MSRCrypto sends it in P1363 format and Java Library requires it in DER format.

由于我是客户端,我无法更改服务器代码.服务器正在使用 SHA386withECDSA

I cannot change the server code as I am the client. The server is using SHA386withECDSA

1) 谁能给我提供准确的代码片段,以便在 Java 中将其从 P1363 格式转换为 ASN.1,反之亦然(ASN.1 到 P1363).我尝试了一些代码片段,但无法使其工作(因为这些片段是用 C、C++ 编写的).

1) Can someone provide me with exact code snippet to convert it from P1363 format to ASN.1 and vice-versa(ASN.1 to P1363) in Java. I tried a few code snippets but was not able to make it work (because those snippets were in C, C++).

2) 是否有一个库可以用来进行这些转换而无需自己编写.Bouncy Castle 是否提供此功能?

2) Is there a library which I can use to do these conversion without writing it myself. Like does Bouncy Castle provide this?

我也知道我可以将 BouncyCastle 与 SHAXwithPLAIN-ECDSASHAXwithCVC-ECDSA 一起使用.然而,Bouncy Castle/Spongy Castle 在 Android 上运行时会很慢,因为它不进行本地调用.Java 9 也提供该支持,但我仍在使用 Java 8.

I am also aware that I can use BouncyCastle with SHAXwithPLAIN-ECDSA or with SHAXwithCVC-ECDSA. However Bouncy Castle/ Spongy Castle is slow when running this on Android because it does not do native calls. The support is also available in Java 9 but I am still using Java 8.

推荐答案

BouncyCastle 没有将一种签名格式直接转换为另一种的功能.它确实有一个通用的 ASN.1 编码/解码库(对于 DER 和 BER,尽管加密几乎完全使用 DER)可以处理 ASN.1 的一半,但你仍然需要做普通"(P1363,CVC,PKCS11,Microsoft)一半,在输入(解码)端非常容易,但在输出(编码)端却有点困难.对于这种格式,您需要知道并使用曲线顺序的八位字节大小(或者更准确地说是生成器和子组顺序,有时与基础曲线不同),我在这里称之为 n.

BouncyCastle doesn't have a facility to directly convert one signature format to the other. It does have a general-purpose ASN.1 encoding/decoding library (for both DER and BER, although crypto uses almost entirely DER) which can handle the ASN.1 half, but you still have to do the 'plain' (P1363, CVC, PKCS11, Microsoft) half, which is dead easy on the input (decode) side but a little harder on the output (encode) side. For that format you need to know and use the size in octets of the curve order (or more exactly the generator and subgroup order, which sometimes differs from the underlying curve), which I call n here.

我展示了非常有限的错误处理,包括抛出一个无意义的异常并让 JVM 显示它.在真正的程序中,您会希望做得更好,但您想做的事情会有所不同.

I show very limited error handling, consisting of throwing an uninformative Exception and letting the JVM display it. In a real program you will want to do better, but what you will want to do varies.

static void SO61860104Convert1 (String[] args) throws Exception {
    int n = 32; // for example assume 256-bit-order curve like P-256
    byte[] plain = Files.readAllBytes(Paths.get(args[0]));

    // common
    BigInteger r = new BigInteger (+1, Arrays.copyOfRange(plain,0,n));
    BigInteger s = new BigInteger (+1, Arrays.copyOfRange(plain,n,n*2));

    // with BouncyCastle
    ASN1EncodableVector v = new ASN1EncodableVector();
    v.add(new ASN1Integer(r)); v.add(new ASN1Integer(s));
    Files.write(Paths.get(args[1]), new DERSequence(v) .getEncoded() );

    // without
    byte[] x1 = r.toByteArray(), x2 = s.toByteArray(); 
    // already trimmed two's complement, as DER wants
    int len = x1.length + x2.length + (2+2), idx = len>=128? 3: 2;
    // the len>=128 case can only occur for curves of 488 bits or more,
    // and can be removed if you will definitely not use such curve(s)
    byte[] out = new byte[idx+len]; out[0] = 0x30; 
    if( idx==3 ){ out[1] = (byte)0x81; out[2] = (byte)len; } else { out[1] = (byte)len; }
    out[idx] = 2; out[idx+1] = (byte)x1.length; System.arraycopy(x1, 0, out, idx+2, x1.length);
    idx += x1.length + 2;
    out[idx] = 2; out[idx+1] = (byte)x2.length; System.arraycopy(x2, 0, out, idx+2, x2.length);
    Files.write(Paths.get(args[2]), out);
}
static void SO61860104Convert2 (String[] args) throws Exception {
    int n = 32; // for example assume 256-bit-order curve like P-256
    byte[] der = Files.readAllBytes(Paths.get(args[0]));
    BigInteger r, s;
    byte[] out;

    // with BouncyCastle
    ASN1Sequence seq = ASN1Sequence.getInstance(der);
    r = ((ASN1Integer)seq.getObjectAt(0)).getValue();
    s = ((ASN1Integer)seq.getObjectAt(1)).getValue();
    // common output
    out = new byte[2*n]; toFixed(r, out, 0, n); toFixed(s, out, n, n);
    Files.write(Paths.get(args[1]), out);

    // without
    if( der[0] != 0x30 ) throw new Exception();
    int idx = der[1]==0x81? 3: 2; // the 0x81 case only occurs for curve over 488 bits
    if( der[idx] != 2 ) throw new Exception();
    r = new BigInteger (1, Arrays.copyOfRange(der,  idx+2, idx+2+der[idx+1]));
    idx += der[idx+1] + 2;
    if( der[idx] != 2 ) throw new Exception();
    s = new BigInteger (1, Arrays.copyOfRange(der,  idx+2, idx+2+der[idx+1]));
    if( idx + der[idx+1] + 2 != der.length ) throw new Exception();
    // common output
    out = new byte[2*n]; toFixed(r, out, 0, n); toFixed(s, out, n, n);
    Files.write(Paths.get(args[2]), out);
}
static void toFixed (BigInteger x, byte[] a, int off, int len) throws Exception {
    byte[] t = x.toByteArray();
    if( t.length == len+1 && t[0] == 0 ) System.arraycopy (t,1, a,off, len);
    else if( t.length <= len ) System.arraycopy (t,0, a,off+len-t.length, t.length);
    else throw new Exception();
}

相关文章