在 Java 中生成 X509Certificate 的主题哈希

Posted

技术标签:

【中文标题】在 Java 中生成 X509Certificate 的主题哈希【英文标题】:Generate Subject Hash of X509Certificate in Java 【发布时间】:2015-07-27 11:36:57 【问题描述】:

我目前正在尝试使用 Java 安全 API 和 BouncyCastle 生成主题哈希。

当我使用 Openssl 库时,我会这样做:

openssl x509 -in  /Users/Sn0wfreezeDev/Downloads/Test.pem -hash

这会生成一个简短的 8 位哈希 1817886a

这是我的 Java 代码

X509Certificate cert = CertManager.getCertificate(number, c);  
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
System.out.println("  Subject " + cert.getSubjectDN());
System.out.println("   Issuer  " + cert.getIssuerDN());
sha1.update(cert.getSubjectDN().getName().getBytes());
String hexString =  bytesToHex(sha1.digest());
System.out.println("   sha1    " + hexString);
System.out.println();

【问题讨论】:

您将需要主题可分辨名称。为此(在 Java 中),请参阅 Getting the Subject and Issuer Distinguished Names of an X509 Certificate。 你能提供最终的工作代码吗? 我正在使用byte []sha1Bytes0_3 = Arrays.copyOfRange( sha1.digest(), 0, 4); String hexString = getHex(sha1Bytes0_3);,但得到的哈希值与 openssl 计算的哈希值不同。 【参考方案1】:

这会生成一个简短的 8 位哈希 1817886a

OpenSSL 有两种形式:

$ cd openssl-1.0.2-src
$ grep -R X509_subject_name_hash *
...
crypto/x509/x509.h:unsigned long X509_subject_name_hash(X509 *x);
crypto/x509/x509.h:unsigned long X509_subject_name_hash_old(X509 *x);
crypto/x509/x509_cmp.c:unsigned long X509_subject_name_hash(X509 *x)
crypto/x509/x509_cmp.c:unsigned long X509_subject_name_hash_old(X509 *x)
...

在 Java 中生成 X509Certificate 的主题哈希...

这是来自crypto/x509/x509_cmp.c的来源:

unsigned long X509_subject_name_hash(X509 *x)

    return (X509_NAME_hash(x->cert_info->subject));


#ifndef OPENSSL_NO_MD5
unsigned long X509_subject_name_hash_old(X509 *x)

    return (X509_NAME_hash_old(x->cert_info->subject));

#endif

最后:

unsigned long X509_NAME_hash(X509_NAME *x)

    unsigned long ret = 0;
    unsigned char md[SHA_DIGEST_LENGTH];

    /* Make sure X509_NAME structure contains valid cached encoding */
    i2d_X509_NAME(x, NULL);
    if (!EVP_Digest(x->canon_enc, x->canon_enclen, md, NULL, EVP_sha1(),
                    NULL))
        return 0;

    ret = (((unsigned long)md[0]) | ((unsigned long)md[1] << 8L) |
           ((unsigned long)md[2] << 16L) | ((unsigned long)md[3] << 24L)
        ) & 0xffffffffL;
    return (ret);


#ifndef OPENSSL_NO_MD5
unsigned long X509_NAME_hash_old(X509_NAME *x)

    EVP_MD_CTX md_ctx;
    unsigned long ret = 0;
    unsigned char md[16];

    /* Make sure X509_NAME structure contains valid cached encoding */
    i2d_X509_NAME(x, NULL);
    EVP_MD_CTX_init(&md_ctx);
    EVP_MD_CTX_set_flags(&md_ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
    if (EVP_DigestInit_ex(&md_ctx, EVP_md5(), NULL)
        && EVP_DigestUpdate(&md_ctx, x->bytes->data, x->bytes->length)
        && EVP_DigestFinal_ex(&md_ctx, md, NULL))
        ret = (((unsigned long)md[0]) | ((unsigned long)md[1] << 8L) |
               ((unsigned long)md[2] << 16L) | ((unsigned long)md[3] << 24L)
            ) & 0xffffffffL;
    EVP_MD_CTX_cleanup(&md_ctx);

    return (ret);

#endif

i2d_X509_NAME 使用RFC 2459(和其他地方)将X509_NAME 编码为标准表示。例如,它用于证书主题和颁发者名称。

您可以通过openssl x509 -in &lt;cert&gt; -text -noout 之类的命令查看 OpenSSL 对名称字符串的用途。它看起来类似于 C=US, ST=California, L=Mountain View, O=Google Inc, CN=www.google.com(取自 Google 证书)。


在 Java 中生成 X509Certificate 的主题哈希...

总体而言,您正在生成主题的专有名称字符串的散列并返回一个无符号长整数。 unsigned long 实际上是一个截断的散列。

X509_subject_name_hash 使用 SHA-1,X509_subject_name_hash_old 使用 MD5。


(comment) ... 他们如何使用 sha1 散列来生成短散列

OpenSSL 提供截断散列的十六进制编码。整个哈希在md 中。 md 将是 16 字节 (MD5) 或 20 字节 (SHA-1)。

选择字节[0,3]md[0]md[1]md[2]md[3] 上的位操作会发生截断。

8位数字来自于对4个字节进行十六进制编码。

【讨论】:

好的,我怎么可能在 Java 中获得相同的 8 位哈希? 我知道我也找到了 OpenSSL 代码。但我不明白他们如何使用 sha1 哈希来生成短哈希 有人知道如何在 Bash 中执行此操作吗?我知道 x509 -hash-issuer_hash 。获取确切的源数据是我正在寻找的。​​span>

以上是关于在 Java 中生成 X509Certificate 的主题哈希的主要内容,如果未能解决你的问题,请参考以下文章

在 IST 时区 java 中生成时间戳

在 Java 中生成当前时间戳

在Java中生成随机单词?

如何在java中生成一个随机的15位长[重复]

java 在java中生成一定长度的整数数组

在 java/scala 中生成 ecdsa 32 字节私钥