X.509 证书的 Java 1.7 主题哈希 OpenSSL 1.0+ 兼容 [英] Java 1.7 Subject Hash of X.509 Certificate OpenSSL 1.0+ Compatible

查看:31
本文介绍了X.509 证书的 Java 1.7 主题哈希 OpenSSL 1.0+ 兼容的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经为此苦苦挣扎了几天.我正在开发一个在嵌入式 Linux 环境中运行的 Java 1.7 应用程序.OpenSSL 不可用,我无法控制设备上操作系统映像中的内容.我需要计算自签名 X.509 证书的主题哈希,产生与 OpenSSL 1.0+ 相同的结果.这个现有的答案让我开始了:

I've been struggling with this for a couple of days. I'm working on a Java 1.7 app running in an embedded Linux environment. OpenSSL is not available and I don't have control over what is in the OS image on the device. I need to compute the subject hash of a self-signed X.509 certificate, producing the same result as OpenSSL 1.0+. This existing answer got me started:

新的主题哈希openssl算法不同

我的测试应用中的代码如下所示.我的计算适用于主题名称仅包含 CN 值的证书,但不适用于指定了任何其他主题组件(OU、O、L、ST 或 C)的证书.对于这些证书,整个主题(减去介绍序列)的哈希不匹配.根据上面的答案,我提取了每个组件(使用 getObjectAt( ) 方法)并单独散列每个组件(不高兴),颠倒它们的顺序并将它们全部散列(不高兴),以及一些其他的变体主题.我一直在努力避免我担心下载 OpenSSL 源代码并使其运行更耗时的工作,以便我可以检查中间结果并查看我哪里出错了.或许做过这件事的人可以提供一些指导.

The code from my test app appears below. My computation works for certs whose subject name contains only a CN value, but it does not work for certs with any other subject component (OU, O, L, ST, or C) specified. For those certs the hash on the entire subject (less the intro sequence) does not match. Per the above answer, I have extracted each component (using the getObjectAt( ) method) and hashed each of them them alone (no joy), reversed their order and hashed them all (no joy), and a number of other variations on that theme. I have been trying to avoid what I fear will be the more time-consuming effort of downloading the OpenSSL source and getting it running so I can examine intermediate results and see where I am going wrong. Perhaps someone who has done this can provide some guidance.

private static void getSubjectHash( X509Certificate x509Cert )
{
    try {
        // get the subject principal
        X500Principal x500Princ = x509Cert.getSubjectX500Principal( );

        // create a new principal using canonical name (order, spacing, etc.) and get it in ANS1 DER format
        byte[] newPrincEnc = new X500Principal( x500Princ.getName( X500Principal.CANONICAL ) ).getEncoded( );

        // read it in as an ASN1 Sequence to avoid custom parsing
        ASN1InputStream aIn = new ASN1InputStream( newPrincEnc );
        ASN1Sequence seq = (ASN1Sequence) aIn.readObject( );

        List<byte[]> terms = new ArrayList<>( );
        int finalLen = 0;
        int i = 0;

        // hash the encodables for each term individually and accumulate them in a list
        for ( ASN1Encodable asn1Set : seq.toArray( ) ) {
            byte[] term = ( (ASN1Set) asn1Set ).getEncoded( );
            terms.add( term );
            finalLen += term.length;

            // digest the term
            byte[] hashBytes = truncatedHash( getDigest( term ), 4 );
            printByteArray( String.format( "hash of object at %d:", i++ ), hashBytes );

            System.out.println( "" );
        }


        // hash all terms together in order of appearance
        int j = 0;
        byte[] finalEncForw = new byte[finalLen];
        for ( byte[] term : terms )
            for ( byte b : term )
                finalEncForw[j++] = b;

        // digest and truncate
        byte[] hashBytes = truncatedHash( getDigest( finalEncForw ), 4 );

        printByteArray( "hash of all terms in forward order", hashBytes );
        System.out.println( "" );


        // hash all terms together in reverse order
        j = 0;
        byte[] finalEncRev = new byte[finalLen];
        for ( int k = terms.size( ) - 1; k >= 0; --k )
            for ( byte b : terms.get( k ) )
                finalEncRev[j++] = b;

        // digest and truncate
        hashBytes = truncatedHash( getDigest( finalEncRev ), 4 );

        printByteArray( "hash of all terms in reverse order", hashBytes );
    }
    catch ( Exception ex ) {
        throw new RuntimeException( "uh-oh" );
    }
}

private static byte[] getDigest( byte[] toHash )
{
    MessageDigest md;

    try {
        md = MessageDigest.getInstance( "SHA1" );
    }
    catch ( NoSuchAlgorithmException nsa ) {
        throw new RuntimeException( "no such algorithm" );
    }

    return md.digest( toHash );
}

private static byte[] truncatedHash( byte[] hash, int truncatedLength )
{
    if ( truncatedLength < 1 || hash.length < 1 )
        return new byte[0];

    byte[] result = new byte[truncatedLength];

    for ( int i = 0; i < truncatedLength; ++i )
        result[truncatedLength - 1 - i] = hash[i];

    return result;
}

private static void printByteArray( String name, byte[] bytes )
{
    System.out.println( name + " length=" + String.valueOf( bytes.length ) );
    for ( byte b: bytes ) {
        System.out.print( String.format( "%02X ", Byte.toUnsignedInt( b ) ) );
    }

    System.out.println( );
}

推荐答案

好的,现在就用胶带吧.这似乎适用于我可以测试的所有证书.这是 getSubjectHash 方法的重写版本:

Okay, duct tape it is, for now. This appears to work for all the certs I have available to test. This is the re-written version of the getSubjectHash method:

private static void getSubjectHash( X509Certificate x509Cert )
{
    try {
        // get the subject principal
        X500Principal x500Princ = x509Cert.getSubjectX500Principal( );

        // create a new principal using canonical name (order, spacing, etc.) and get it in ANS1 DER format
        byte[] newPrincEnc = new X500Principal( x500Princ.getName( X500Principal.CANONICAL ) ).getEncoded( );

        // read it in as an ASN1 Sequence to avoid custom parsing
        ASN1InputStream aIn = new ASN1InputStream( newPrincEnc );
        ASN1Sequence seq = (ASN1Sequence) aIn.readObject( );

        List<byte[]> terms = new ArrayList<>( );
        int finalLen = 0;
        int i = 0;

        // hash the encodables for each term individually and accumulate them in a list
        for ( ASN1Encodable asn1Set : seq.toArray( ) ) {
            byte[] term = ( (ASN1Set) asn1Set ).getEncoded( );
            term[9] = 0x0c; // change tag from 0x13 (printable string) to 0x0c
            terms.add( term );
            finalLen += term.length;

            // digest the term
            byte[] hashBytes = truncatedHash( getDigest( term ), 4 );
            printByteArray( String.format( "hash of object at %d:", i++ ), hashBytes );

            System.out.println( "" );
        }


        // hash all terms together in order of appearance
        int j = 0;
        byte[] finalEncForw = new byte[finalLen];
        for ( byte[] term : terms )
            for ( byte b : term )
                finalEncForw[j++] = b;

        // digest and truncate
        byte[] hashBytes = truncatedHash( getDigest( finalEncForw ), 4 );

        printByteArray( "hash of all terms in forward order", hashBytes );
        System.out.println( "" );

    }
    catch ( Exception ex ) {
        throw new RuntimeException( "uh-oh" );
    }
}

这篇关于X.509 证书的 Java 1.7 主题哈希 OpenSSL 1.0+ 兼容的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆