ASN.1简介及OpenSSL中ASN.1接口使用举例
ASN.1(Abstract Syntax Notation One)是一套標準,是描述數據的表示、編碼傳輸、解碼的靈活的記法。它提供了一套正式、無歧義和精確的規則以描述獨立于特定計算機硬件的對象結構。OpenSSL的編碼方法就是基于該標準。ASN.1是一種結構化的數字對象描述語言,它包括兩部分:數據描述語言和數據編碼規則。ASN.1的數據描述語言允許用戶自定義基本的數據類型,并可以通過簡單的數據類型組成更復雜的數據類型。
ASN.1是ISO和ITU-T的聯合標準,它本身只定義了表示信息的抽象句法,但是沒有限定其編碼的方法。各種ASN.1編碼規則提供了由ASN.1描述其抽象句法的數據的值的傳送語法(具體表達)。標準的ASN.1編碼規則有基本編碼規則(BER,Basic Encoding Rules)、規范編碼規則(CER,Canonical Encoding Rules)、唯一編碼規則(DER,Distinguished Encoding Rules)、壓縮編碼規則(PER,Packed Encoding Rules)和XML編碼規則(XER,XML Encoding Rules)。這些編碼方法規定了將數字對象轉換成應用程序能夠處理、保存和網絡傳輸的二進制編碼形式的一組規則。PEM編碼全稱是Privacy Enhanced Mail,是一種保密郵件的編碼標準。ASN.1與特定的ASN.1編碼規則一起通過使用獨立于計算機架構和編程語言的方法來描述數據結構,為結構化數據的交互提供了手段,特別是在網絡環境的應用程序。
OpenSSL的PEM編碼就是在DER編碼基礎上進行BASE64編碼,然后添加一些頭尾信息組成的,如在生成的rsa private.pem中頭信息為-----BEGIN RSA PRIVATE KEY-----,尾信息為-----END RSA PRIVATE KEY-----,中間的數據部分即是對DER進行base64編碼后的結果。
注:以上內容主要整理自網絡。
ASN.1里定義的每個基本對象都有一個對應的數字標識tag,在進行二進制編碼的時候需要使用該標識,定義的數字標識在asn1.h頭文件中,如下所示:
/* ASN.1 tag values */
# define V_ASN1_EOC                      0
# define V_ASN1_BOOLEAN                  1 /**/
# define V_ASN1_INTEGER                  2
# define V_ASN1_BIT_STRING               3
# define V_ASN1_OCTET_STRING             4
# define V_ASN1_NULL                     5
# define V_ASN1_OBJECT                   6
# define V_ASN1_OBJECT_DESCRIPTOR        7
# define V_ASN1_EXTERNAL                 8
# define V_ASN1_REAL                     9
# define V_ASN1_ENUMERATED               10
# define V_ASN1_UTF8STRING               12
# define V_ASN1_SEQUENCE                 16
# define V_ASN1_SET                      17
# define V_ASN1_NUMERICSTRING            18 /**/
# define V_ASN1_PRINTABLESTRING          19
# define V_ASN1_T61STRING                20
# define V_ASN1_TELETEXSTRING            20/* alias */
# define V_ASN1_VIDEOTEXSTRING           21 /**/
# define V_ASN1_IA5STRING                22
# define V_ASN1_UTCTIME                  23
# define V_ASN1_GENERALIZEDTIME          24 /**/
# define V_ASN1_GRAPHICSTRING            25 /**/
# define V_ASN1_ISO64STRING              26 /**/
# define V_ASN1_VISIBLESTRING            26/* alias */
# define V_ASN1_GENERALSTRING            27 /**/
# define V_ASN1_UNIVERSALSTRING          28 /**/
# define V_ASN1_BMPSTRING                30ASN.1編碼:編碼的實際數據由字符串和配置信息確定。字符串的一般格式是:零個或多個逗號分隔的修飾符,后跟一個類型,后跟一個可選的冒號和一個值。
[modifier,]type[:value]modeifier:修飾符,支持的類型包括EXPLICIT、IMPLICIT、SEQWRAP、FORMAT等。
type:支持的類型包括BOOLEAN、INTEGER、ENUMERATED、BITSTRING等。
value:數據。
關于配置文件的更多介紹可以參考:https://www.openssl.org/docs/manmaster/man3/ASN1_generate_v3.html
通過調用ASN1_generate_nconf函數接口,格式如:"IA5STRING:https://blog.csdn.net/fengbingchun";通過調用i2d_ASN1_OCTET_STRING方式接口,格式如:"https://blog.csdn.net/fengbingchun",測試代碼如下:
int test_openssl_asn1_simple_encode()
{// test 1const char* src = "IA5STRING:https://blog.csdn.net/fengbingchun";CONF* nconf = nullptr;ASN1_TYPE* encoded = ASN1_generate_nconf(src, nconf);if (!encoded) {fprintf(stderr, "fail to asn1 encode: %s\n", src);return -1;}// test 2const char* src2 = "https://blog.csdn.net/fengbingchun";ASN1_STRING asn1str;memset(&asn1str, 0, sizeof(ASN1_STRING));ASN1_STRING_set(&asn1str, src2, strlen(src2));const char *value = reinterpret_cast<char*>(ASN1_STRING_data(&asn1str));fprintf(stdout, "the value is: %s, strlen: %u\n", value, strlen(value));std::unique_ptr<unsigned char[]> encoded2(new unsigned char[strlen(src2) + 2]);unsigned char* p = encoded2.get();int encoded2_len = i2d_ASN1_OCTET_STRING(&asn1str, &p);fprintf(stdout, "encoded length: %d\n", encoded2_len);#ifdef _MSC_VERconst char* name = "E:/GitCode/OpenSSL_Test/testdata/simple2.der";
#elseconst char* name = "testdata/simple2.der";
#endifFILE* fp = fopen(name, "wb");if (!fp) {fprintf(stderr, "fail to open file: %s\n", name);return -1;}fwrite(encoded2.get(), 1, strlen(src2) + 2, fp);fclose(fp);return 0;
}通過配置文件生成,如simple.conf,內容如下:
asn1 = IA5STRING:https://blog.csdn.net/fengbingchun通過openssl asn1parse命令將simple.conf進行asn1編碼,編碼后的輸出文件為simple.der,執行結果如下:
一般情況下,ASN.1編碼多數是通過命令直接生成,如生成rsa私鑰的配置文件rsa_private_key.conf,格式如下:
asn1=SEQUENCE:private_key
[private_key]
version=INTEGER:0
n=INTEGER:0xBB6FE79432CC6EA2D8F970675A5A87BFBE1AFF0BE63E879F2AFFB93644D4D2C6D000430DEC66ABF47829E74B8C5108623A1C0EE8BE217B3AD8D36D5EB4FCA1D9
e=INTEGER:0x010001
d=INTEGER:0x6F05EAD2F27FFAEC84BEC360C4B928FD5F3A9865D0FCAAD291E2A52F4AF810DC6373278C006A0ABBA27DC8C63BF97F7E666E27C5284D7D3B1FFFE16B7A87B51D
p=INTEGER:0xF3929B9435608F8A22C208D86795271D54EBDFB09DDEF539AB083DA912D4BD57
q=INTEGER:0xC50016F89DFF2561347ED1186A46E150E28BF2D0F539A1594BBD7FE46746EC4F
exp1=INTEGER:0x9E7D4326C924AFC1DEA40B45650134966D6F9DFA3A7F9D698CD4ABEA9C0A39B9
exp2=INTEGER:0xBA84003BB95355AFB7C50DF140C60513D0BA51D637272E355E397779E7B2458F
coeff=INTEGER:0x30B9E4F2AFA5AC679F920FC83F1F2DF1BAF1779CF989447FABC2F5628657053A通過openssl asn1parse命令將rsa_private_key.conf進行asn1編碼,編碼后的輸出文件為rsa_private_key.der,執行結果如下:
ASN.1解碼:
對上面生成的simple.der進行解碼,可調用d2i_ASN1_IA5STRING相關函數,測試代碼如下:
int test_openssl_simple_decode()
{
#ifdef _MSC_VERconst char* name = "E:/GitCode/OpenSSL_Test/testdata/simple.der";
#elseconst char* name = "testdata/simple.der";
#endifFILE* fp = fopen(name, "rb");if (!fp) {fprintf(stderr, "fail to open file: %s\n", name);return -1;}fseek(fp, 0, SEEK_END);long length = ftell(fp);rewind(fp);std::unique_ptr<unsigned char[]> data(new unsigned char[length + 1]);data.get()[length] = '\0'; // in order to be correct fprintf %sfread(data.get(), 1, length, fp);fclose(fp);if (data.get()[0] != V_ASN1_IA5STRING) {fprintf(stderr, "fail to get asn1 tag value: %d, %d\n", data.get()[0], V_ASN1_IA5STRING);return -1;}fprintf(stdout, "decode data length: %d\n", data.get()[1]);fprintf(stdout, "decode data: %s\n", (char*)(data.get() + 2));const unsigned char* p = data.get();ASN1_IA5STRING* str = ASN1_IA5STRING_new();d2i_ASN1_IA5STRING(&str, &p, length);fprintf(stdout, "decode data: %s\n", str->data);ASN1_IA5STRING_free(str);return 0;
}通過命令對上面生成的simple2.der進行解碼,執行命令及結果如下:與原始數據一致
對上面生成的rsa_private_key.der進行解碼,測試代碼如下:
typedef struct RSA_PRIVATE_KEY_st {ASN1_INTEGER* version;ASN1_INTEGER* n;ASN1_INTEGER* e;ASN1_INTEGER* d;ASN1_INTEGER* p;ASN1_INTEGER* q;ASN1_INTEGER* exp1;ASN1_INTEGER* exp2;ASN1_INTEGER* coeff;
} RSA_PRIVATE_KEY;
DECLARE_ASN1_FUNCTIONS(RSA_PRIVATE_KEY);ASN1_SEQUENCE(RSA_PRIVATE_KEY) = {ASN1_SIMPLE(RSA_PRIVATE_KEY, version, ASN1_INTEGER),ASN1_SIMPLE(RSA_PRIVATE_KEY, n, ASN1_INTEGER),ASN1_SIMPLE(RSA_PRIVATE_KEY, e, ASN1_INTEGER),ASN1_SIMPLE(RSA_PRIVATE_KEY, d, ASN1_INTEGER),ASN1_SIMPLE(RSA_PRIVATE_KEY, p, ASN1_INTEGER),ASN1_SIMPLE(RSA_PRIVATE_KEY, q, ASN1_INTEGER),ASN1_SIMPLE(RSA_PRIVATE_KEY, exp1, ASN1_INTEGER),ASN1_SIMPLE(RSA_PRIVATE_KEY, exp2, ASN1_INTEGER),ASN1_SIMPLE(RSA_PRIVATE_KEY, coeff, ASN1_INTEGER)
} ASN1_SEQUENCE_END(RSA_PRIVATE_KEY)
IMPLEMENT_ASN1_FUNCTIONS(RSA_PRIVATE_KEY)void print(const ASN1_INTEGER* str, const char* item)
{fprintf(stdout, "name: %s, type: %d, length: %d, data: ", item, str->type, str->length);for (int i = 0; i < str->length; ++i) {fprintf(stdout, "%02X", str->data[i]);}fprintf(stdout, "\n");
}int test_openssl_asn1_complex_decode()
{
#ifdef _MSC_VERconst char* name = "E:/GitCode/OpenSSL_Test/testdata/rsa_private_key.der";
#elseconst char* name = "testdata/rsa_private_key.der";
#endifFILE* fp = fopen(name, "rb");if (!fp) {fprintf(stderr, "fail to open file: %s\n", name);return -1;}fseek(fp, 0, SEEK_END);long length = ftell(fp);rewind(fp);std::unique_ptr<unsigned char[]> data(new unsigned char[length]);fread(data.get(), 1, length, fp);fclose(fp);// data.get()[0]: type tag indicating SEQUENCE, 0x30if (data.get()[0] != 0x30) {fprintf(stderr, "it's type should be SEQUENCE: %s, %x\n", name, data.get()[0]);return -1;}const unsigned char* p = data.get();RSA_PRIVATE_KEY* key = d2i_RSA_PRIVATE_KEY(nullptr, &p, length);if (!key) {fprintf(stderr, "fail to d2i_RSA_PRIVATE_KEY\n");return -1;}print(key->version, "version");print(key->n, "n");print(key->e, "e");print(key->d, "d");print(key->p, "p");print(key->q, "q");print(key->exp1, "exp1");print(key->exp2, "exp2");print(key->coeff, "coeff");RSA_PRIVATE_KEY_free(key);return 0;
}執行結果如下:與配置文件中的原始數據一致
通過命令對rsa_private_key.der進行解碼,執行結果如下:與上面通過code執行的結果一致
通過UE打開rsa_private_key.der,結果如下:
對UE打開的rsa_private_key.der進行說明:
0x30:說明ASN.1 tag為SEQUENCE類型;
0x82:指后面兩個字節的長度是long form;
0x013b:指明節點數據字節總長度;
0x02:version tag為INTEGER;
0x01:version長度為1;
0x00:version值為0x00;
0x02:n tag為INTEGER;
0x41:n長度為65,因為緊挨著的下一個字節是0x00,表示數據是正整數,所以n的實際長度為64,與code執行結果值一致;
通過命令對rsa_private_key.der解碼進行說明:ASN.1 der結構格式,第一行
(1).0:表示節點在整個文件中的偏移長度;
(2).d=0:表示節點深度;
(3).hl=4:表示節點頭字節長度;
(4).l=315:第一行中指所有節點數據字節長度;其它行表示當前節點數據字節長度;
(5).cons:表示該節點為結構節點,表示包含子節點或子結構體數據;其它行的prim表示該節點為原始節點,包含數據;
(6). SEQUENCE:表示ASN.1 tag類型;其它行的INTEGER也是ASN.1的一種tag類型;
(7).其它行的最后一列:表示節點數據。
以上代碼段的完整code見:GitHub/OpenSSL_Test
GitHub:https://github.com//fengbingchun/OpenSSL_Test
總結
以上是生活随笔為你收集整理的ASN.1简介及OpenSSL中ASN.1接口使用举例的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: C和C++安全编码笔记:整数安全
- 下一篇: 通过OpenSSL的接口实现Base64
