自定义java工具类读取DER格式数据

一、概述

在证书编码中,主要使用的是两种编码结构,一个是PEM编码,另一种就是DER编码( Distinguished Encoding Rules,可辨别编码规则 )。而DER编码同时也是ASN.1(抽象语法标记)的一个子集。
  一个标准的ASN.1 编码对象有四个域:对象标识域、数据长度域、数据域以及结束标志(可选,在长度不可知情况下需要,openssl中没有该标志)。详细的解释请看百度百科: http://baike.baidu.com/view/100318.htm 。
本篇就简单的对一个DER格式的文件进行解析,并介绍相应的工具类。

二、解析DER格式文件

创建一个工具类,类名就是DerAnalysis,创建构造方法:

public DerAnalysis(byte abyte[]) {

  mRecord = abyte;  //需要解析的DER格式文件转成的数组
  mTlvOffset = 0;  //数据指定位
  mCurOffset = 0;  // 数据偏移索引
  mObjLength = abyte.length; //待解析数据长度
  mIsDer = parseCurrentData(); // 判断是否满足Der编码结构

}

在构造方法中将数据进行初始化,保存数据的长度及默认偏移位和数据指定位,其中数据指定位指的是可以再次声明构造方法,将数据指定区域向后偏移一定的长度,这属于对当前类的扩展了,就不做说明了。

parseCurrentData方法:

private boolean parseCurrentData() {

        try
        {
            if (this.mRecord[this.mCurOffset] != 0)
            {
                // 如果指定位置是 0xFF 直接结束
                if ((0xFF & this.mRecord[this.mCurOffset]) == 255)
                    return false;

                // 如果指定位置+1位小于 0x81 ,说明指定位置之后紧接着的字节就是数据的长度值。
                if ((0xFF & this.mRecord[(1 + this.mCurOffset)]) < 128)
                {

                    this.mCurDataLength = (0xFF & this.mRecord[(1 + this.mCurOffset)]);
                    this.mCurDataOffset = (2 + this.mCurOffset);

                    return true;
                }

                // 如果指定位置+1 位大于等于 0x81 (也就是表示长度位 是一位还是二位 )
                while (this.mCurDataLength + this.mCurDataOffset <= this.mTlvOffset + this.mObjLength)
                {
//
                    //如果 mCurOffset + 1 位 是0x81 ,长度是一个字节表示
                    if ((0xFF & this.mRecord[(1 + this.mCurOffset)]) == 129){

                        this.mCurDataLength = (0xFF & this.mRecord[(2 + this.mCurOffset)]);
                        this.mCurDataOffset = (3 + this.mCurOffset);
                        return true;
                    }
                    //如果 mCurOffset + 1 位 是0x82 ,长度是二个字节表示
                    if ((0xFF & this.mRecord[(1 + this.mCurOffset)]) == 130) {

                        this.mCurDataLength = ((0xFF & this.mRecord[(2 + this.mCurOffset)]) << 8 | 0xFF & this.mRecord[(3 + this.mCurOffset)]);
                        this.mCurDataOffset = (4 + this.mCurOffset);
                        return true;
                    }
                    break;
                }
            }

            return false;
        }
        catch (ArrayIndexOutOfBoundsException localArrayIndexOutOfBoundsException)
        {
            return false;
        }
    }

当前方法是将DER格式的数据进行相应解析,重点是将数据位及数据长度位的指定索引进行读取。从上述方法中,可以看出,我只做了长度位在两个字节以内的文件进行判断,因为在一般的证书文件中,长度位一般都不会超过2个字节,要知道2个字节的长度就达到65535字节了。
同时,在DER格式标准中,长度位是高位优先的,所以在计算长度的时候将首位进行向左位移8位。

对外提供的方法:

数据长度区域索引、数据区域索引都已经找到了。就只需要提供几个方法,将解析的结果对外暴露了。

isDir:

public boolean isDir()
    {
        return this.mIsDer;
    }

这个方法在解析时,是第一个被调用的,先判断是否满足DER结构,否则就不要进行下去了。

getTag:

public int getTag()
    {
        if (!this.mIsDer)
            return 0;
        return (0xFF & this.mRecord[this.mCurOffset]);
    }

此方法是获取对象标识符数据,在当前类中,我默认标记位都只是一个字节,如果各位的需求文档中表明不止一个字节表明对象标识符,那么就需要进行相应的修改了,这里只是作为一个基本的写法,就不扩展了。

拿到对象标识符进行判断是否和认为的标识符一样,如果一样就可以提取数据了。

getData:

public byte[] getData()
    {
        if(!mIsDer)
        {
            return null;
        } else{

            try {

                byte abyte1[] = null;
                byte abyte0[] = null;

                if ((0xFF & mRecord[mCurDataOffset]) == 0) {

                    abyte1= new byte[mCurDataLength-1];
                    System.arraycopy(mRecord , mCurDataOffset+1 , abyte1 , 0,mCurDataLength-1);
                } else {

                    abyte0 = new byte[mCurDataLength];
                    System.arraycopy(mRecord, mCurDataOffset, abyte0, 0, mCurDataLength);
                }


                return abyte1 != null ? abyte1 : abyte0;

            }catch (Exception e) {

                e.printStackTrace();

                return null;
            }


        }
    }

此方法就是获取数据的方法,在方法中会有个判断,如果获取到的数据域第一个数据是0x00,那么认为此字节是补位的,并非是真正的数据,所以从此字节之后的数据进行提取。

在DER解析时,数据域不可能只有一个数据块的,所以需要进行下一个数据块的解析,如果从工具类外部进行操作的话,显得很是冗余,所以应该在工具类中进行下一个数据块的解析。

nextObject:

public boolean nextObject()
    {
        if (!this.mIsDer)
            return false;
        this.mCurOffset = (this.mCurDataOffset + this.mCurDataLength);
        this.mIsDer = parseCurrentData();
        return this.mIsDer;
    }

执行此方法,将数据偏移向后,便可以开始下一个数据块的解析了。具体的方法还是按照上述提供的方法。

三、总结

在工具类进行解析DER格式数据的时候,在同一个层级的数据块向后解析时,调用的是nextObject方法,而如果是当前数据块中包含其他的数据块,如果是这种结构,就需要重新将获取到的数据(getData方法)作为新的要解析的数据,重新执行构造方法了。

上述便是简单解析DER格式数据的工具类说明,代码比较简单,所需要的是要对DER格式文件有一定的了解。如果需要扩展请自行定义。

你可能感兴趣的:(数据解析,java,数据,编码)