【问题标题】:generate a SHA256withDSA signature using Go and then verify it using Java使用 Go 生成 SHA256withDSA 签名,然后使用 Java 进行验证
【发布时间】:2021-10-31 12:23:18
【问题描述】:
  1. 使用 sha256 orign 数据然后 DSA 签名
  2. 按十六进制打印签名结果
  3. 使用 Java SHA256withDSA 验证第一步结果返回 false
  4. 我知道 sha256 然后 DSA 是 SHA256 与 DSA 的区别
  5. 如何在 Go 中生成一个可以在 Java 中使用 sha256withdsa 进行验证的符号?
  6. Go 还有其他第三方库支持吗?

package main

import (
    "crypto/dsa"
    "crypto/rand"
    "crypto/sha256"
    "encoding/asn1"
    "encoding/hex"
    "fmt"
    "golang.org/x/crypto/ssh"
    "math/big"
)

func main() {
    pemData := []byte("-----BEGIN DSA PRIVATE KEY-----\n" +
        "MIIBvAIBAAKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR\n" +
        "+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb\n" +
        "+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdg\n" +
        "UI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlX\n" +
        "TAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCj\n" +
        "rh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQB\n" +
        "TDv+z0kqAoGBAIb9o0KPsjAdzjK571e1Mx7ZhEyJGrcxHiN2sW8IztEbqrKKiMxp\n" +
        "NlTwm234uBdtzVHE3uDWZpfHPMIRmwBjCYDFRowWWVRdhdFXZlpCyp1gMWqJ11dh\n" +
        "3FI3+O43DevRSyyuLRVCNQ1J3iVgwY5ndRpZU7n6y8DPH4/4EBT7KvnVAhR4Vwun\n" +
        "Fhu/+4AGaVeMEa814I3dqg==\n" +
        "-----END DSA PRIVATE KEY-----")

    p, _ := ssh.ParseRawPrivateKey(pemData)
    pp := p.(*dsa.PrivateKey)

    hashed := []byte{1}
    sha256 := sha256.New()
    sha256.Write(hashed)
    hashed = sha256.Sum(nil)
    r, s, _ := dsa.Sign(rand.Reader, pp, hashed)
    type dsaSignature struct {
        R, S *big.Int
    }
    var ss dsaSignature
    ss.S = s
    ss.R = r
    signatureBytes, _ := asn1.Marshal(ss)
    fmt.Println(hex.EncodeToString(signatureBytes))
}

Java

 @Test
    public void ttt() throws InvalidKeySpecException, NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        String pubKey = "-----BEGIN PUBLIC KEY-----\n" +
                "MIIBuDCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9E\n" +
                "AMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f\n" +
                "6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv\n" +
                "8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtc\n" +
                "NrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwky\n" +
                "jMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/h\n" +
                "WuWfBpKLZl6Ae1UlZAFMO/7PSSoDgYUAAoGBAIb9o0KPsjAdzjK571e1Mx7ZhEyJ\n" +
                "GrcxHiN2sW8IztEbqrKKiMxpNlTwm234uBdtzVHE3uDWZpfHPMIRmwBjCYDFRowW\n" +
                "WVRdhdFXZlpCyp1gMWqJ11dh3FI3+O43DevRSyyuLRVCNQ1J3iVgwY5ndRpZU7n6\n" +
                "y8DPH4/4EBT7KvnV\n" +
                "-----END PUBLIC KEY-----";
        String publicKeyPEM = pubKey
                .replace("-----BEGIN PUBLIC KEY-----\n", "")
                .replaceAll(System.lineSeparator(), "")
                .replace("-----END PUBLIC KEY-----", "");
        byte[] publicEncoded = Base64.decodeBase64(publicKeyPEM);
        KeyFactory keyFactory1 = KeyFactory.getInstance("DSA");
        X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicEncoded);
        DSAPublicKey pubKeyy = (DSAPublicKey) keyFactory1.generatePublic(publicKeySpec);

        Signature sig1 = Signature.getInstance("SHA256withDSA");
        sig1.initVerify(pubKeyy);
        byte[] orignData = new byte[]{1};
        sig1.update(orignData);
        boolean flag = sig1.verify(HexUtil.decodeHex("first step result"));
        System.out.println(flag);
    }

【问题讨论】:

  • 检查是否提供和需要相同的编码。过去我遇到过类似的问题,一种语言以 base64 格式提供哈希值,而另一种语言则要求以十六进制格式提供哈希值。使用分组密码时,默认填充字符也可能有所不同。
  • java sha256 与 golang sha256 相等,但 java sha256withdsa 与 golang sha256 then dsa 没有区别

标签: java go sha256 dsa


【解决方案1】:

遵循 FIPS186-4 第 4.6 和 4.7 节 golang 应该这样

hashed := []byte{1}
pemData, _ := ioutil.ReadFile("config/secure/private.key")
p, err := ssh.ParseRawPrivateKey(pemData)
pp, ok := p.(*dsa.PrivateKey)
h := sha256.New()
h.Write(hashed)
hashed := h.Sum(nil)
if pp.Q.BitLen()/8 < len(hashed) {
    hashed = hashed[0 : pp.Q.BitLen()/8]
}
r, s, _ := dsa.Sign(rand.Reader, pp, hashed)
var ss dsaSignature
ss.S = s
ss.R = r
hashed, _ = asn1.Marshal(ss)

【讨论】:

    【解决方案2】:

    我在java中找到

    package sun.security.provider;
    
    private BigInteger generateS(BigInteger var1, BigInteger var2, BigInteger var3, BigInteger var4) throws SignatureException {
            byte[] var5;
            try {
                var5 = this.md.digest();
            } catch (RuntimeException var9) {
                throw new SignatureException(var9.getMessage());
            }
    
            // this line split the hash256 redsult
            int var6 = var2.bitLength() / 8;
            if (var6 < var5.length) {
                var5 = Arrays.copyOfRange(var5, 0, var6);
            }
    
            BigInteger var7 = new BigInteger(1, var5);
            BigInteger var8 = var4.modInverse(var2);
            return var1.multiply(var3).add(var7).multiply(var8).mod(var2);
    }
    

    你可以在 golang 中使用

        hashed := []byte{1}
        sha256 := sha2562.New()
        sha256.Write(hashed)
        hashed = sha256.Sum(nil)
        // you must split it 
        hashed = hashed[0:20]
    
    

    原因是 sha256 结果在 java 中拆分,但 glang 没有拆分

    【讨论】:

    • 你的意思是截断,而不是分割; split 将使用这两个部分。您的代码似乎是反汇编; actual source for example here 更清晰。是的,如果散列大于 q 的大小,则截断散列是 FIPS186-4 第 4.6 和 4.7 节要求的;该标准指定位,而 Java 代码使用 8 位字节,但这没关系,因为所有批准的 q 大小都是 8 位的倍数,所以都是批准的哈希值。如果 Go 不做这种截断,它就违反了标准。
    • 注意如果你想要SHA256的security级别(128位)你应该使用3072位p和256位q的DSA,这样就不会有截断因为哈希大小与 q 大小匹配;请参阅 FIPS186-4 中的第 4.2 节。 (而不是参考 SP800-57,您可以相信我的话,4 种标准 p、q 尺寸(L、N 值)的强度为 80、112、112、128。)但这对 SO 来说是题外话。
    猜你喜欢
    • 2015-08-25
    • 1970-01-01
    • 2014-03-22
    • 1970-01-01
    • 2012-02-04
    • 2021-02-05
    • 1970-01-01
    • 2017-11-03
    • 1970-01-01
    相关资源
    最近更新 更多