将Java转换为Python的DSA签名

2 投票
2 回答
1003 浏览
提问于 2025-04-16 23:38

有没有人知道我该如何把这段Java代码转换成Python呢?

/**
* signs the data for the account account
*/
private byte[] sign(String pkStr,byte[] data, String keyType) throws Exception {
    BASE64Decoder decoder = new BASE64Decoder();
    KeyFactory keyFac = null;
    //instantiate the key factory based on the key alg type
    if(keyType.equals("DSA")){
        keyFac = KeyFactory.getInstance("DSA");
    }else if(keyType.equals("RSA")){
        keyFac = KeyFactory.getInstance("RSA");
    }

    //generate the public key
    PKCS8EncodedKeySpec dprks = new PKCS8EncodedKeySpec(decoder.decodeBuffer(pkStr));
    PrivateKey pk = keyFac.generatePrivate(dprks);

    return(signBytes(data,pk,keyType));
}

    /**
* sign the data with the key
*/
private byte[] signBytes(byte [] data,
    PrivateKey signingPrivateKey, String keyType)throws Exception {

    Signature dsa = null;
    //instantiate the signature alg based on the key type
    if(keyType.equals("DSA")){
        dsa = Signature.getInstance("SHA1withDSA");
    }else if(keyType.equals("RSA")){
        dsa = Signature.getInstance("SHA1withRSA");
    }
    /* Initializing the object with a private key */
    dsa.initSign(signingPrivateKey);

    /* Update and sign the data */
    dsa.update(data);
    byte[] sig = dsa.sign();
    return sig;
}

看起来“keyType”总是被传递为“DSA”,所以我查看了M2Crypto.DSA,这个看起来不错。不过,DSA.sign函数返回的是一个包含两个字节字符串的元组,我对该怎么处理这些返回值一点头绪都没有。

2 个回答

1

根据这个链接 http://download.oracle.com/javase/1.5.0/docs/guide/security/CryptoSpec.html#AppB(有点奇怪的是,它有两个附录B,你需要往下滚动才能看到第二个),Java使用了一种叫做ASN.1的编码方式,具体格式是 SEQUENCE ::= { r INTEGER, s INTEGER }

你可以在Python中使用pyasn1库来生成(和解析)这种格式,链接是 http://pyasn1.sourceforge.net/

ASN.1是一种编码二进制数据的标准。所以上面提到的信息说明了Java代码是如何把Python代码返回的两个值结合在一起的。你也可以这样做,从而保持签名的字节格式一致。

3

DSA签名是由一对整数(分别称为rs)组成的。DSA标准并没有规定如何将这样的签名编码成字节序列。因此,使用DSA签名的每个协议都需要定义自己的编码方式。

常用的DSA签名编码有两种;一种是将rs的无符号大端编码直接连接在一起,这两个值的长度都要规范到公钥中q参数的字节长度(“子群大小”,通常是一个160位的质数,因此生成的签名是40个字节)。M2Crypto.DSA的文档比较简短,但我猜它返回的rs已经是这种格式。

Java使用另一种编码方式,这种方式基于ASN.1。这种编码在X.509及其相关的所有内容中使用(包括SSL/TLS中的签名)。ASN.1是一种用于表示和序列化结构化数据的通用标准。在这种情况下,签名应该是一个包含两个INTEGER值(rs,按顺序)的ASN.1 SEQUENCE的序列化。根据ASN.1和DER编码规则,签名的格式应该是:

0x30 A 0x02 B R 0x02 C S

其中:

  • Rr的无符号大端编码,长度要最小:这意味着第一个字节的值应该在0到127之间,只有当第二个字节的值在128到255之间时,第一个字节的值才可以是0。换句话说,要将r编码为一个字节序列,遵循大端规则(最重要的字节在前),确保前面尽量少有零位,但至少要保留一个(这就是“有符号”编码的意思:因为r是正数,所以它的最高位必须是0)。由于r是一个在0q-1之间的整数,R的长度最多比q的长度多一个字节,但也可能更小。

  • Ss的无符号大端编码(处理方式与r相同;注意:RS的长度可能不同)。

  • B是一个单字节,表示R的长度(以字节为单位)。

  • C是一个单字节,表示S的长度(以字节为单位)。

  • A是一个单字节,表示B+C+2(即A后面内容的字节长度)。

为基于ASN.1的DSA签名编写专门的编码和解码函数有点繁琐,但并不难;只需确保生成的RS序列大小正确。或者,你也可以使用现有的ASN.1编码/解码库,虽然这可能有些过于复杂,但根据你的情况可能会更简单。

撰写回答