999精品在线视频,手机成人午夜在线视频,久久不卡国产精品无码,中日无码在线观看,成人av手机在线观看,日韩精品亚洲一区中文字幕,亚洲av无码人妻,四虎国产在线观看 ?

支持國產密碼算法的OpenSSL設計實現及應用

2018-02-28 02:51:04蔡成杭
信息安全研究 2018年2期

蔡成杭

(北京江南天安科技有限公司 北京 100088)

OpenSSL[1]是一套應用廣泛的、開源的支持傳輸層安全協議的密碼學基礎庫和工具集合,囊括主要的密碼算法、常用的密鑰和證書封裝管理功能及SSL/TLS協議,并提供豐富的API,以供應用系統集成、程序開發、測試或其他目的使用.它廣泛地集成在各種類型的操作系統中(Linux、BSD家族、MacOS等),即使某些操作系統(Windows、傳統的Unix等)沒有將其集成為組件,通過源代碼下載,也可以十分輕松地構建OpenSSL的開發及應用環境.

作為基礎組件之一,OpenSSL簡潔、明了、豐富的應用接口,可簡單、便捷地構筑安全領域等方面的應用,從而深受廣大IT愛好者的喜愛.因此,基于OpenSSL的應用十分廣泛,特別是涉及到安全功能的應用系統和中間件,許多都是基于OpenSSL來構建的.如我們常用的SSH,Apache,Tomcat,Nginx,MySQL等知名系統,都是依賴OpenSSL來構建其安全體系的.

在中國,也有大量的安全領域的應用是依賴OpenSSL來構造的.但原始OpenSSL中并不包含符合中國標準的商用密碼算法(簡稱國密)和安全通信協議,構建的安全應用也不符合中國商用密碼行業標準,不利于國產密碼的推廣.因此,通過對OpenSSL的改造,讓OpenSSL支持國產密碼算法及通信協議,這是十分必要的.

江南天安于2017年推出了天安版國密OpenSSL,將國產密碼算法及國密TLS協議[2]集成進OpenSSL中,實現了支持國產密碼認證體系以及國密TLS(以下簡稱CNTLS)協議,可替換原來基于OpenSSL的上層應用,也可在此基礎上便捷地構建符合國密標準的應用系統.

本文以OpenSSL的穩定版本1.0.2h為例,詳細介紹支持國產密碼算法的OpenSSL在CentOS 6.5操作系統中的實現及應用.

1 國產密碼OpenSSL的實現

國產密碼OpenSSL除具備原始OpenSSL的功能特色外,同時兼具著以下幾點:

1) 支持國產密碼算法,提供國產密碼算法開發和應用接口;

2) 包含國產密碼算法的對象標識符;

3) 支持國產密碼認證體系;

4) 實現國密TLS協議,提供國密TLS協議的開發和應用接口;

5) 支持以引擎或內置的方式,實現需硬件支持的國產密碼算法,如:SM1,SSF33等.

1.1 國產密碼OpenSSL的系統架構

國產密碼OpenSSL的系統架構如圖1所示,可粗略分為:基礎函數庫、EVP密碼算法封裝接口庫、X509/PKCS認證接口庫、SSL/TLS/CNTLS協議接口庫、應用及開發接口等.

圖1 國產密碼OpenSSL系統架構及依賴關系圖

圖1中,基礎函數庫包括:國際對稱加密算法、信息摘要算法、公開密鑰算法的軟實現;國產對稱加密算法SM4[3]、信息摘要算法SM3[4]、公開密鑰算法SM2[5](可視為橢圓曲線公鑰算法[6]的一條特定曲線)的軟實現;硬件加速或實現引擎接口;錯誤處理接口;BIO抽象輸入/輸出接口;數據結構等等.

EVP密碼算法封裝接口庫包括:對國際密碼算法、國產密碼算法、硬件實現算法的統一調用接口,它依賴于基礎函數庫,同時也提供信息摘要、自動完成公鑰算法的數字簽名和驗證以及數字信封等等接口,供X509/PKCS,SSL/TLS/CNTLS接口庫調用.

X509/PKCS接口庫:X509或PKCS認證體系的標準接口庫,此接口庫需支持國密碼認證體系.

SSL/TLS/CNTLS接口庫:提供SSL/TLS/標準接口,同時添加支持CNTLS的API及支持雙證書體系(目前只支持RSA和SM2的雙證書)的接口API.

1.2 國產密碼算法對象標識的集成

國產密碼算法對象標識[7]簡稱國密OID,用來在OpenSSL及標準的X509/PKCS認證體系中標識國產密碼算法及國產密碼算法的使用方式.

在原始的OpenSSL中集成國產密碼算法對象標識,需要在OpenSSL的原代碼中作如下修改:

1) 編輯OpenSSL源代碼目錄crypto/objects中的objects.txt文件,并在其尾部添加如下數行:

1 2 156 10197 1:SM-SCHEME:sm-scheme

sm-scheme 102 :SM1:sm1

:SM1-CBC:sm1-cbc

:SM1-ECB:sm1-ecb

:SM1-CFB:sm1-cfb

:SM1-OFB:sm1-ofb

sm-scheme 103 :SSF33:ssf33

:SSF33-CBC:ssf33-cbc

:SSF33-ECB:ssf33-ecb

:SSF33-CFB:ssf33-cfb

:SSF33-OFB:ssf33-ofb

sm-scheme 104 :SM4:sm4

:SM4-CBC:sm4-cbc

:SM4-ECB:sm4-ecb

:SM4-CFB:sm4-cfb

:SM4-OFB:sm4-ofb

sm-scheme 201 :ZUC:zuc

:EEA3-128:eea3-128

:EIA3-128:eia3-128

sm-scheme 301 :SM2:sm2

sm2 1 :sm2signature

sm2 2 :sm2keyagreement

sm2 3 :sm2encrypt

sm-scheme 401 :SM3:sm3

sm3 1 :SM3-DIGEST:sm3-digest

sm3 2 :HMAC-SM3:hmac-sm3

sm-scheme 501:SM2-SM3:sm3WithSM2Sign

sm-scheme 504:RSA-SM3:sm3WithRSAEncryption

sm-scheme 504:RSA-SM3-2:sm3WithRSA

2) 編輯OpenSSL源代碼目錄crypto/objects中的obj_xref.txt文件,并在其尾部添加如下數行:

sm3WithRSAEncryption sm3 rsaEncryption

sm3WithRSA sm3 rsa

sm3WithSM2Sign sm3 X9_62_id_ecPublicKey

3) 進入OpenSSL源代碼目錄crypto/objects,依次執行如下2條命令:

perl objects.pl objects.txt obj_mac.num obj_mac.h

perl objxref.pl obj_mac.num obj_xref.txt > obj_xref.h

這樣即可將國產密碼算法及其使用方法的對象標識添加到OpenSSL中.

1.3 國產密碼算法的OpenSSL實現

國產密碼算法已經公開的算法有:SM2公鑰算法、SM3信息摘要算法、SM4對稱密碼算法、祖沖之流密碼算法和SM9標識密碼算法.

本文只介紹SM2、SM3和SM4算法的OpenSSL實現,這些算法也是目前應用最為廣泛的國產密碼算法.同時,為兼顧被大量使用的SM1和SSF33算法,實現了SM1和SSF33算法的EVP接口,以便在需要時,通過引擎的方式來使用硬件實現SM1和SSF33算法.

1.3.1SM4算法的實現

在國產密碼OpenSSL中,SM4對稱加密算法的實現分為基礎庫軟實現和EVP封裝接口實現2個部分.

1.3.1.1SM4基礎庫軟實現

SM4算法原理及FK,CK,SBOX的值請參見:GM/T 0002—2012 《SM4分組密碼算法》,這里不再贅述.

1) 定義SM4密鑰數據結構及常量,如下:

struct sm4_key_st

{

uint32_t key[32];

};

typedef struct sm4_key_st SM4_KEY;

const unsigned FK[4]={…};

const unsigned CK[32]={…};

const unsigned char SBOX[25]={…};

2) 定義相關的宏(也可以用函數實現)

32 b循環位移:

#define RSL(A, I) (((A)<<(I))|((A)>>(32-(I))))

密鑰擴展線性變換:

#define LCK(A) ((A)^(RSL((A), 13))^(RSL((A), 23)))

輪密鑰生成:

#define KERF_K(K0, K1, K2, K3, K4, CK, RK)

K4=(K1)^(K2)^(K3)^(CK), K4=NT(K4),

RK=K4=(K0)^LCK(K4)

加解密線性變換:

#define LC(A) ((A)^

(RSL((A), 2))^(RSL((A), 10))^

(RSL((A), 18))^(RSL((A), 24)))

加解密非線性變換:

#define NT(A)

((SBOX[((A)>>24)]<<24)|

(SBOX[(((A)>>16) & 0xFF)]<<16)|

(SBOX[(((A)>>8) & 0xFF)]<<8)|

(SBOX[((A) & 0xFF)]))

加解密輪處理:

#define RF_E(X0, X1, X2, X3, X4, RK)

X4=(X1)^(X2)^(X3)^(RK),

X4=NT(X4),

X4=(X0)^LC(X4)

3) 實現密鑰初始化函數

int SM4_set_key(const unsigned char*userKey, size_t length, SM4_KEY*key)

{

unsigned*rk=key->key;

unsigned K[5];

int loop;

for (loop=0; loop<4; loop++)

{

K[i]=__bswap_32(*((unsigned*)

(userKey+i*4))); /*需包含

endian.h*/

K[i] ^=FK[i];

}

for (loop=0; loop<32; loop++)

KERF_K(K[loop %5], K[(loop+1)

%5], K[(loop+2) %5],

K[(loop+3) %5],K[(loop+4) %5],

CK[loop], rk[loop]);

return 1;

}

4) 實現SM4加密函數

void SM4_encrypt(const unsigned char*in, unsigned char*out, const SM4_KEY*key)

{

const unsigned*rk=key->key;

unsigned X[5];

int loop;

for (loop=0; loop<4; loop++)

X[loop]=__bswap_32(*((unsigned*)

(in+loop*4)));

for (loop=0; loop<32; loop++)

RF_E(X[loop %5], X[(loop+1) %5],

X[(loop+2) %5], X[(loop+3) %5],

X[(loop+4) %5], rk[loop]);

for ((loop=0; loop<4; loop++)

*((unsigned*)(out+loop*4))=

X[loop];

}

5) 實現SM4解密函數

void SM4_decrypt(const unsigned char*in, unsigned char*out, const SM4_KEY*key)

{

const unsigned*rk=key->key;

unsigned X[5];

int loop;

for (loop=0; loop<4; loop++)

X[loop]=__bswap_32(*((unsigned*)

(in+loop*4)));

for (loop=0; loop<32; loop++)

RF_E(X[loop %5], X[(loop+1) %5],

X[(loop+2) %5], X[(loop+3) %5],

X[(loop+4) %5], rk[31-loop]);

for ((loop=0; loop<4; loop++)

*((unsigned*)(out+loop*4))=

X[loop];

}

到此為止,SM4基礎軟實現已經完成.需要注意的是,在此實現的源代碼中,需要包括頭文件endian.h,否則是找不到函數__bswap_32的.

1.3.1.2SM4EVP封裝接口實現

SM4的EVP封裝接口需要實現4種加密模式,分別是ECB,CBC,CFB和OFB模式.其實現過程如下.

1) 定義EVP封裝的SM4數據結構如下:

typedef struct { SM4_KEY ks;} EVP_SM4_KEY;

2) 實現EVP_CIPHER結構的成員函數init:

static int sm4_init(EVP_CIPHER_CTX*ctx, const unsigned char*key, const unsigned char*iv, int enc)

{

EVP_SM4_KEY*dat=(EVP_SM4_KEY*)

ctx->cipher_data;

SM4_set_key(key, 16, &(dat->ks));

return 1;

}

3) 實現EVP_CIPHER結構的成員函數do_cipher,因為要實現4種模式,因此要實現4種版的do_cipher:

static int sm4_cbc(EVP_CIPHER_CTX*ctx, unsigned char*out, const unsigned char*in, size_t inl)

{

if (ctx->encrypt)

CRYPTO_cbc128_encrypt(in,out,length,

&((EVP_SM4_KEY*)ctx->

cipher_data)->ks,

ctx->iv,

(block128_f) SM4_encrypt);

else

CRYPTO_cbc128_decrypt(in,out,length,

&((EVP_SM4_KEY*)ctx->

cipher_data)->ks,

ctx->iv,

(block128_f) SM4_decrypt);

return 1;

}

static int sm4_ecb(EVP_CIPHER_CTX*ctx, unsigned char*out, const unsigned char*in, size_t inl)

{

size_t i, bl;

bl=ctx->cipher->block_size;

if (inl

return 1;

inl-=bl;

if (ctx->encrypt)

for (i=0; i<=inl; i+=bl)

SM4_encrypt(in+i, out+i,

&((EVP_SM4_KEY*)ctx->

cipher_data)->ks);

else

for (i=0; i<=inl; i+=bl)

SM4_decrypt(in+i, out+i,

&((EVP_SM4_KEY*)ctx->

cipher_data)->ks);

return 1;

}

static int sm4_cfb(EVP_CIPHER_CTX*ctx, unsigned char*out, const unsigned char*in, size_t inl)

{

CRYPTO_cfb128_encrypt(in,out,inl,

&((EVP_SM4_KEY*)ctx->

cipher_data)->ks,

ctx->iv,

&ctx->num,

ctx->encrypt,

(block128_f) SM4_encrypt);

return 1;

}

static int sm4_ofb(EVP_CIPHER_CTX*ctx, unsigned char*out, const unsigned char*in, size_t inl)

{

CRYPTO_ofb128_encrypt(in,out,inl,

&((EVP_SM4_KEY*)ctx->

cipher_data)->ks,

ctx->iv,

&ctx->num,

(block128_f) SM4_encrypt);

return 1;

}

4) 填充EVP_CIPHER結構,實現SM4的4種EVP封閉接口:

static const EVP_CIPHER sm4_ecb={

NID_sm4_ecb,

16,16, 0, EVP_CIPH_ECB_MODE,

sm4_init, sm4_ecb, NULL,

sizeof(EVP_SM4_KEY),

NULL, NULL, NULL, NULL

};

const EVP_CIPHER*EVP_sm4_ecb(void) { return &sm4_ecb; }

static const EVP_CIPHER sm4_cbc={

NID_sm4_cbc,

16, 16, 16, EVP_CIPH_CBC_MODE,

sm4_init, sm4_cbc, NULL,

sizeof(EVP_SM4_KEY),

NULL, NULL, NULL, NULL

};

const EVP_CIPHER*EVP_sm4_cbc(void) {return &sm4_cbc;}

static const EVP_CIPHER sm4_cfb={

NID_sm4_cfb,

1, 16, 16, EVP_CIPH_CFB_MODE,

sm4_init, sm4_cfb, NULL,

sizeof(EVP_SM4_KEY),

NULL, NULL, NULL, NULL

};

const EVP_CIPHER*EVP_sm4_cfb(void) {return &sm4_cfb;}

static const EVP_CIPHER sm4_ofb={

NID_sm4_ofb,

1, 16, 16, EVP_CIPH_OFB_MODE,

sm4_init, sm4_ofb, NULL,

sizeof(EVP_SM4_KEY),

NULL, NULL, NULL, NULL

};

const EVP_CIPHER*EVP_sm4_ofb(void) {return &sm4_ofb;}

5) 修改OpenSSL源代碼目錄crypto/evp中的evp.h,并添加對SM4 EVP封裝接口的4種模式的定義,如下所示:

const EVP_CIPHER*EVP_sm4_ecb(void);

const EVP_CIPHER*EVP_sm4_cbc(void)

const EVP_CIPHER*EVP_sm4_cfb(void)

const EVP_CIPHER*EVP_sm4_ofb(void)

6) 修改OpenSSL源代碼目錄crypto/evp中的c_allc.c,并將SM4 EVP封裝接口的4種模式加入到OpenSSL系統中去,如下所示:

EVP_add_cipher(EVP_sm4_cbc());

EVP_add_cipher(EVP_sm4_cfb());

EVP_add_cipher(EVP_sm4_ecb());

EVP_add_cipher(EVP_sm4_ofb());

EVP_add_cipher_alias(SN_sm4_cbc, ″SM4″);

其中,在EVP封裝的接口中,EVP_sm4_cbc作為SM4的默認算法.

1.3.2SM1和SSF33的EVP接口

參照SM4 EVP封裝接口的實現,實現SM1和SSF33的EVP接口,并將其中的EVP_CIPHER的成員函數init,do_cipher置NULL(空)即可.

1.3.3SM3摘要算法的實現

在國產密碼OpenSSL中,SM3作為信息摘要函數,也分為基礎庫軟實現和EVP封裝接口的實現.

1.3.3.1SM3基礎庫軟實現

SM3算法的原理請參見GM/T 0004—2012 《SM3密碼雜湊算法》.

1) 定義SM3算法的結構如下所示:

typedef struct SM3state_st

{

SM3_LONG digest[8];

SM3_LONG Nl, Nh;

SM3_LONG data[64];

unsigned int num;

} SM3_CTX;

2) 實現SM3初始化函數如下所示:

int SM3_Init(SM3_CTX*c)

{

memset(c, 0, sizeof(SM3_CTX));

c->digest[0]=0x7380166F;

c->digest[1]=0x4914B2B9;

c->digest[2]=0x172442D7;

c->digest[3]=0xDA8A0600;

c->digest[4]=0xA96F30BC;

c->digest[5]=0x163138AA;

c->digest[6]=0xE38DEE4D;

c->digest[7]=0xB0FB0E4E;

return 1;

}

3) 在SM3實現的源代碼文件中,通過如下定義來實現SM3算法:

static void SM3_block_data_order(SM3_CTX*ctx, const void*in, size_t num);

#define DATA_ORDER_IS_BIG_ENDIAN

#define HASH_LONG SM3_LONG

#define HASH_CTX SM3_CTX

#define HASH_CBLOCK SM3_CBLOCK

#define HASH_MAKE_STRING(c, s) do

{

SM3_LONG ll;

unsigned int nn;

for (nn=0; nn

4; nn++)

{

ll=(c)->digest[nn];

(void)HOST_l2c(ll, (s));

}

} while (0)

#define HASH_UPDATE SM3_Update

#define HASH_TRANSFORM SM3_Transform

#define HASH_FINAL SM3_Final

#define HASH_BLOCK_DATA_ORDER SM3_block_data_order

#include ″md32_common.h″

4) 在SM3_block_data_order函數中,實現消息擴展及消息壓縮如下:

#define RSL(A, I) (((A)<<(I))|((A)>>(32-(I))))

#define FF0_15(X, Y, Z) ((X)^(Y)^(Z))

#define FF16_63(X, Y, Z) (((X) & (Y))|((X) & (Z))|((Y) & (Z)))

#define GG0_15(X, Y, Z) ((X)^(Y)^(Z))

#define P0(X) ((X)^RSL((X), 9)^RSL((X), 17))

#define P1(X) ((X)^RSL((X), 15)^RSL((X), 23))

static void SM3_block_data_order(SM3_CTX*ctx, const void*in, size_t num)

{

int j;

SM3_LONG W[68], W1[64];

SM3_LONG A, B, C, D, E, F, G, H, SS1, SS2, TT1, TT2, T0_15, T16_63;

const unsigned char*pblock=(const unsigned char*)in;

while (num--)

{

for (j=0; j<16; j++) {

HOST_c2l(pblock, W[j]);

}

for (j=16; j<68; j++) {

W[j]=W[j-16]^W[j-9]^

RSL(W[j-3], 15);

W[j]=P1(W[j])^

RSL(W[j-13], 7)^W[j-6];

}

for (j=0; j<64; j++) {

W1[j]=W[j]^W[j+4];

}

A=ctx->digest[0],

B=ctx->digest[1],

C=ctx->digest[2],

D=ctx->digest[3];

E=ctx->digest[4],

F=ctx->digest[5],

G=ctx->digest[6],

H=ctx->digest[7];

T0_15=0x79CC4519, T16_63=

0x7A879D8A;

for (j=0; j<16; j++) {

SS1=RSL(A, 12)+E+

RSL(T0_15, j),

SS1=RSL(SS1, 7);

SS2=SS1^RSL(A, 12);

TT1=FF0_15(A, B, C)+D+

SS2+W1[j];

TT2=GG0_15(E, F, G)+H+

SS1+W[j];

D=C;

C=RSL(B, 9);

B=A;

A=TT1;

H=G;

G=RSL(F, 19);

F=E;

E=P0(TT2);

}

for (j=16; j<64; j++) {

SS1=RSL(A, 12)+E+

RSL(T16_63, (j % 32)),

SS1=RSL(SS1, 7);

SS2=SS1^RSL(A, 12);

TT1=FF16_63(A, B, C)+D+

SS2+W1[j];

TT2=GG16_63(E, F, G)+H+

SS1+W[j];

D=C;

C=RSL(B, 9);

B=A;

A=TT1;

H=G;

G=RSL(F, 19);

F=E;

E=P0(TT2);

}

ctx->digest[0] ^=A;

ctx->digest[1] ^=B;

ctx->digest[2] ^=C;

ctx->digest[3] ^=D;

ctx->digest[4] ^=E;

ctx->digest[5] ^=F;

ctx->digest[6] ^=G;

ctx->digest[7] ^=H;

}

}

到此為止,SM3信息摘要算法的基礎庫軟實現完成.

1.3.3.2SM3EVP封裝接口實現

SM3算法EVP封裝接口的實現,首先需要實現結構EVP_MD的成員函數init,update,final;然后需要填充EVP_MD的數據結構的每一項,實現EVP_sm3();然后在evp.h中申明EVP_sm3(void)接口,最后在c_alld.c文件中,將EVP_sm3()加載到OpenSSL的EVP接口庫中.

1) EVP_MD成員函數的實現

static int init(EVP_MD_CTX*ctx)

{

return SM3_Init(ctx->md_data);

}

static int update(EVP_MD_CTX*ctx, const void*data, size_t count)

{

return SM3_Update(ctx->md_data,

data, count);

}

static int final(EVP_MD_CTX*ctx,

unsigned char*md)

{

return SM3_Final(md, ctx->md_data);

}

2) 填充EVP_MD結構,并實現EVP_sm3()

static const EVP_MD sm3_md={

NID_sm3,

0, 32,

EVP_MD_FLAG_PKEY_METHOD_SIGNATURE|EVP_MD_FLAG_DIGALGID_ABSENT,

init, update, final,

NULL,

NULL,

EVP_PKEY_NULL_method,

64,

sizeof(EVP_MD*)+sizeof(SM3_CTX),

NULL

};

const EVP_MD*EVP_sm3(void) {return (&sm3_md);}

3) 在OpenSSL源代碼目錄crypto/evp中,修改文件c_alld.c,以便加載EVP_sm3()接口

EVP_add_digest(EVP_sm3());

EVP_add_digest_alias(SN_sm3WithRSA Encryption, SN_sm3WithRSA).

1.3.4SM2公鑰算法的實現

國產密碼算法SM2公鑰算法是一條特定的曲線橢圓曲線公鑰算法,它基于ECC的通用算法,按照中國國家密碼管理局發布的GM/T 0003—2012 《SM2橢圓曲線公鑰密碼算法》標準,定義了SM2簽名[8]、驗證、公鑰加密[9]、私鑰解密、密鑰協商[10]算法5種算法.

其中,SM2簽名/驗證算法,是對原始信息經過SM3算法做摘要后的結果進行處理的.按照GM/T 0003—2012 《SM2橢圓曲線公鑰密碼算法》(第二部分的6.1節)要求,先要計算Z值,然后再將Z值和原始信息一起做摘要,最后對摘要值進行簽名/驗證.

因此,SM2公鑰算法中還需要定義計算Z值的函數.

1.3.4.1Z值的計算

按照GM/T 0003—2012 《SM2橢圓曲線公鑰密碼算法》(第二部分的6.1節)所述,Z值的計算如下:

ZA=H256(ENT LA‖IDA‖a‖b‖xG‖xG‖

xA‖yA),

其中,H256指的是256 b的信息摘要算法,在國產密碼算法中,此處只取值為SM3信息摘要算法;ENT LA是指由可辨別標識IDA的位(bit)數,轉換而成的2 B;IDA為用戶可辨別標識,當此標識不存在或者未提供時,其值默認為:1234567812345678;a,b為素域橢圓曲線方程y2=x3+ax+b的參數[11]; a的值為FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFC;b的值為28E9FA9E 9D9F5E34 4D5A9E4B CF6509A7 F39789F5 15AB8F92 DDBCBD41 4D940E93;xG,xG為基點坐標,其值為:Gx=32C4AE2C 1F198119 5F990446 6A39C994 8FE30BBF F2660BE1 715A4589 334C74C7,Gy=BC3736A2 F4F6779C 59BDCEE3 6B692153 D0A9877C C62A4740 02DF32E5 2139F0A0;xA,yA為簽名者的公鑰坐標.

Z值計算函數原型為:int ECDSA_sm2_get_Z(const EC_KEY*ec_key, const EVP_MD*md, const char*uid, int uid_len, unsigned char*z_buf, size_t*z_len).

其實現很簡單,在此略過.

1.3.4.2SM2簽名驗證算法實現

國產密碼OpenSSL中的SM2簽名、驗證算法函數,依據《GM/T 0003—2012 SM2橢圓曲線公鑰密碼算法 第2部分:數字簽名算法》中所述的原理,按照OpenSSL接口的方式,設計如下:

1) 數字簽名生成函數

① 在OpenSSL中,SM2簽名的對象是經過SM3做過摘要的結果,因此,其原型設計為

ECDSA_SIG*sm2_do_sign(const unsigned char*dgst, int dgst_len, EC_KEY*eckey),

其中,dgst為信息摘要值,長度為32 B;eckey為簽名的SM2密鑰對;成功簽名后返回ECDSA_SIG數據結構,否則返回NULL值.

② 函數的簽名過程和其實現代碼如下.

S0:將輸入的參數摘要值的數據類型轉換為整數e;實現如下(e為大數變量):

BN_bin2bn(dgst, dgst_len, e);

S1:用隨機數發生器產生隨機數k ∈[1,n-1];實現如下(k為大數變量,order為SM2參數基點G的階):

do {

if (!BN_rand_range(k, order)) return

NULL;

} while (BN_is_zero(k));

S2:計算橢圓曲線點(x1,y1)=[k]G,并將x1的數據類型轉換為整數;其實現為(group為SM2曲線的GROUP值,tmp_point為EC_POINT的變量,x1為大數變量):

if (!EC_POINT_mul(group, tmp_point,

k, NULL, NULL, NULL))

return NULL;

if (!EC_POINT_get_affine_coordinates_

GFp(group, tmp_point, x1, NULL, NULL))

return NULL;

S3:計算r=(e+x1) modn,若r=0或r+k=n則返回S1;其實現為:

if (!BN_mod_add(r, e, x1, order, NULL))

return NULL;

if (!BN_mod_add(x1, ret->r, k, order,

NULL)) return NULL;

if (BN_is_zero(r)‖BN_is_zero(x1)) goto

S1;

S4:計算s=((1+dA)-1*(k-r *dA)) mod n,若s=0則返回S1;其實現為(s為大數變量,d為SM2密鑰對中的私鑰):

if (!BN_mod_add(x1, d, BN_value_one(),

order, NULL)) return NULL;

if (!BN_mod_inverse(s, x1, order,

NULL)) return NULL;

if (!BN_mod_mul(x1, r, d, order,

NULL)) return NULL;

if (!BN_mod_sub(x1, k, x1, order,

NULL)) return NULL;

if (!BN_mod_mul(s, s, x1, order,

NULL)) return NULL

if (BN_is_zero(ret->s)) goto S1;

S5:將r, s的值填充ECDSA_SIG結構,返回ECDSA_SIG結構.實現如下:

ECDSA_SIG*sig;

sig->r=r;

sig->s=s;

return sig;

2) 數字簽名驗證函數

① 國產密碼OpenSSL中,SM2數據簽名驗證函數的原型設計為

int sm2_do_verify(const unsigned char*dgst, int dgst_len, const ECDSA_SIG*sig, EC_KEY*eckey),

其中,dgst為原始信息的摘要值;sig為需要驗證的簽名值;驗證成功返回1,失敗返回0.

② 國產密碼OpenSSL中,SM2數據簽名驗證流程和實現代碼如下.

B0:將輸入的摘要值轉換成 e1;實現如下(e1為大數變量):

if (!BN_bin2bn(dgst, dgst_len, e1))

return 0;

B1:檢驗sig->r∈[1,n-1]是否成立,若不成立則驗證不通過;

B2:檢驗sig->s∈[1,n-1]是否成立,若不成立則驗證不通過;實現如下(order為SM2曲線參數,為基點G的階):

if (BN_is_zero(e1)) return 0;

if (BN_ucmp(e1, order)>=0) return 0;

B3:計算t=(sig->r+ sig->s) mod n,若t=0,則驗證不通過;

if (!BN_mod_add(t, sig->r, sig->s,

order, NULL)) return 0;

if (BN_is_zero(t)) return 0;

B4:計算橢圓曲線點(x1, y1)=[sig->s]G+[t]PA;其實現如下(group為SM2曲線的GROUP值,point為EC_POINT類型變量,pub_key為驗證用的SM2公鑰):

if (!EC_POINT_mul(group, point, sig->

s, pub_key, t, NULL)) return 0;

B5:將x1的數據類型轉換為整數,計算R=(e1+x1) mod n,檢驗R=sig->r是否成立,若成立則驗證通過;否則驗證不通過,其實現如下(R為大數變量):

if (!EC_POINT_get_affine_coordinates_

GFp(group, point, x1, NULL, NULL))

return 0;

if (!BN_nnmod(x1, x1, order, NULL))

return 0;

if (!BN_mod_add(R, e1, x1, order,

NULL)) return 0;

return (BN_ucmp(R, sig->r)==0);

1.3.4.3SM2公鑰加密及密鑰協商算法實現

SM2公鑰加密算法包括:公鑰加密算法、私鑰解密算法,其原理可參見《GM/T 0003—2012 SM2橢圓曲線公鑰密碼算法 第4部分:公鑰加密算法》.其中涉及到密鑰派生函數KDF,可依據ANSI X9.63—2001規范實現(與OpenSSL 1.0.2h中ECDH_KDF_X9_62的實現兼容).設計其原型為:

SM2ENC*sm2_encrypt(const unsigned char*in, size_t inlen, const EVP_MD*md, EC_KEY*ec_key);

int sm2_decrypt(unsigned char*out, size_t*outlen, const SM2ENC*in, const EVP_MD*md, EC_KEY*ec_key);

int KDF_GMT003_2012(unsigned char*out, size_t outlen, const unsigned char*Z, size_t Zlen, const unsigned char*SharedInfo, size_t SharedInfolen, const EVP_MD*md);

其中,SM2ENC的數據結構為:

struct sm2enc_st {

ASN1_INTEGER*x;

ASN1_INTEGER*y;

ASN1_OCTET_STRING*m;

ASN1_OCTET_STRING*c;

};

typedef struct sm2enc_st SM2ENC;

SM2密鑰協商算法的原理可參見《GM/T 0003—2012 SM2橢圓曲線公鑰密碼算法 第3部分:密鑰交換協議》.在OpenSSL中的設計原型為:

int SM2Kap_compute_key(void*out, size_t outlen, int server;

const char*peer_uid, int peer_uid_len, const char*self_uid, int self_uid_len;

const EC_KEY*peer_ecdhe_key, const EC_KEY*self_ecdhe_key;

const EC_KEY*peer_pub_key, const EC_KEY*self_eckey, const EVP_MD*md).

其中:out為協商的結果,其輸出長度由outlen指定;server為指示出己方是發起方/響應方標識,0為發起方;非0為響應方;peer_uid為對方可辨別標識,其長度由peer_uid_len指定;如果此項為NULL,或者peer_uid_len為0,此標識取默認值:1234567812345678;self_uid為己方可辨別標識,其長度由self_uid_len指定,如果此項為NULL,或者self_uid_len為0,此標識取默認值:1234567812345678;peer_ecdhe_key為對方SM2臨時公鑰;self_ecdhe_key為己方SM2臨時公鑰;peer_pub_key為對方SM2證書公鑰;self_eckey為己方SM2私鑰;md為指定的摘要算法,對于SM2來說,此值默認為EVP_sm3().

協商成功返回1,否則返回0或者負值.

由于篇幅所限,本節所有函數根沒有給出實現.如果有需要此源碼部分,可從https://github.com/jntass/TASSL獲取.

1.3.4.4SM2公鑰算法EVP接口實現

首先,國產密碼OpenSSL將SM2視為ECC的一條特定曲線,因此需要將SM2的算法內置于ECC之中;也就是說,需要實現ECDSA調用接口、ECDH調用接口、EVP_PKEY的調用接口.

1) ECDSA調用接口

在OpenSSL源代碼目錄crypto/ecdsa的ecs_ossl.c文件,需要對函數:ecdsa_do_sign,ecdsa_do_verify以及對ecdsa_sign_setup作出修改;修改方式如下:

在函數ecdsa_do_sign的變量申明之后,添加如下語句:

if (EC_GROUP_get_curve_name(EC_KEY_get0_group(eckey))==NID_sm2)

return sm2_do_sign(dgst, dlen, a, b, eckey);

在函數ecdsa_do_verify的變量申明之后,添加如下語句:

if (EC_GROUP_get_curve_name(EC_KEY_get0_group(eckey))==NID_sm2)

return sm2_do_verify(dgst, dgst_len, sig, eckey);

在函數ecdsa_sign_setup的變量申明之后,添加如下語句:

if (EC_GROUP_get_curve_name(EC_KEY_get0_group(eckey))==NID_sm2)

return 1;

這樣即可實現ECDSA自動調用SM2的簽名驗證算法.

2) ECDH調用接口

SM2由于不支持ECDH算法,而由SM2密鑰協商取而代之,因此,需要防止SM2曲線運用于ECDH算法中.修改方式如下:

在OpenSSL源代碼目錄crypto/ecdh的ech_ossl.c中,修改ecdh_compute_key函數,在其變量申明之后,添加如下語句:

if (EC_GROUP_get_curve_name(EC_KEY_get0_group(ecdh))==NID_sm2)

return-1;

3) EVP_PKEY的調用接口

此接口是通過修改OpenSSL源代碼目錄crypto/ec中的文件ec_pmeth.c來實現的,其實現方法為:

① 數據結構EVP_PKEY_EC的尾部,添加如下成員:

int server;

char*peer_id;

char*self_id;

int peerid_len;

int selfid_len;

EC_KEY*peer_ecdhe_key;

EC_KEY*self_ecdhe_key;

int encdata_format;

② 在函數pkey_ec_init,pkey_ec_copy,pkey_ec_cleanup中需要對所添加的成員進行初始化或者清理工作;

③ 需要添加pkey_ec_encrypt和pkey_ec_decrypt,以便實現EVP對SM2公鑰加密以及私鑰解密的調用;

④ 需要定義一些宏,以實現EVP接口對SM2的參數設置,并且在pkey_ec_ctrl中實現相關宏的功能,具體宏定義如下:

# define EVP_PKEY_CTRL_SET_PEER_ID (EVP_PKEY_ALG_CTRL+11)

# define EVP_PKEY_CTRL_SET_SELF_ID (EVP_PKEY_ALG_CTRL+12)

# define EVP_PKEY_CTRL_SET_SERVER (EVP_PKEY_ALG_CTRL+13)

# define EVP_PKEY_CTRL_SET_PEER_ECDHE (EVP_PKEY_ALG_CTRL+14)

# define EVP_PKEY_CTRL_GEN_SELF_ECDHE (EVP_PKEY_ALG_CTRL+15)

# define EVP_PKEY_CTRL_GET_SELF_ECDHE (EVP_PKEY_ALG_CTRL+16)

# define EVP_PKEY_CTRL_SET_SELF_ECDHE (EVP_PKEY_ALG_CTRL+17)

# define EVP_PKEY_CTRL_SET_ENCDATA (EVP_PKEY_ALG_CTRL+18)

# define EVP_PKEY_CTX_set_sm2_peer_id(ctx, uid, uid_len)

EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, EVP_PKEY_OP_DERIVE,

EVP_PKEY_CTRL_SET_PEER_ID, uid_len, (void*)uid)

# define EVP_PKEY_CTX_set_sm2_self_id(ctx, uid, uid_len)

EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, EVP_PKEY_OP_DERIVE,

EVP_PKEY_CTRL_SET_SELF_ID, uid_len, (void*)uid);

# define EVP_PKEY_CTX_set_sm2_server_tag(ctx, tag)

EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, EVP_PKEY_OP_DERIVE,

EVP_PKEY_CTRL_SET_SERVER, tag, NULL)

# define EVP_PKEY_CTX_set_sm2_peer_ecdhe(ctx, ecdhe)

EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, EVP_PKEY_OP_DERIVE,

EVP_PKEY_CTRL_SET_PEER_ECDHE, 0, (void*)ecdhe)

# define EVP_PKEY_CTX_gen_sm2_ecdhe_key(ctx, ecdhe)

EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, EVP_PKEY_OP_DERIVE|

EVP_PKEY_OP_KEYGEN, EVP_PKEY_CTRL_GEN_SELF_ECDHE, 0, (void*)ecdhe)

# define EVP_PKEY_CTX_get_sm2_ecdhe_key(ctx, ecdhe)

EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, EVP_PKEY_OP_DERIVE|

EVP_PKEY_OP_KEYGEN, EVP_PKEY_CTRL_GET_SELF_ECDHE, 0, (void*)ecdhe)

# define EVP_PKEY_CTX_set_sm2_ecdhe_key(ctx, ecdhe)

EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, EVP_PKEY_OP_DERIVE|

EVP_PKEY_OP_KEYGEN, EVP_PKEY_CTRL_SET_SELF_ECDHE, 0, (void*)ecdhe)

# define EVP_PKEY_CTX_set_sm2_encdata_format(ctx, format)

EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, EVP_PKEY_OP_ENCRYPT|

EVP_PKEY_OP_DECRYPT, EVP_PKEY_CTRL_SET_ENCDATA, format, NULL)

⑤ 需要修改pkey_ec_kdf_derive函數,增加對SM2密鑰協商的支持,修改方式為在其變量申明之后,添加如下代碼:

if (EC_GROUP_get_curve_name(EC_KEY_get0_group(ctx->pkey->pkey.ec))==NID_sm2)

{

if (!ctx->pkey‖!ctx->peerkey)

return 0;

if (!key‖(*keylen==0))

return 0;

outlen=*keylen;

ret=SM2Kap_compute_key(key, outlen,

dctx->server, dctx->peer_id,

dctx->peerid_len, dctx->self_id,

dctx->selfid_len,

dctx->peer_ecdhe_key, dctx->

self_ecdhe_key, ctx->peerkey->

pkey.ec,

ctx->pkey->pkey.ec, dctx->

kdf_md);

if (ret<=0)

return ret;

return 1;

}

⑥ 需要在數據結構常量ec_pkey_meth中,對其成員encrypt,decrypt分別賦值為:pkey_ec_encrypt和pkey_ec_decrypt.

4) 國產密碼OpenSSL中,SM2密鑰需要能夠自動完成對于原始信息的簽名和驗證功能,但由于SM2對原始信息簽名和驗證處理的特殊性,在做摘要之前,需要將簽名者的Z值對原始信息進行補償運算(即:將Z值與原始信息串接在一起),因此需要對OpenSSL源代碼目錄crypto/evp中的m_sigver.c進行修改,以完成SM2的自動簽名、驗證流程.修改方法如下:

在do_sigver_init函數成功返回前,添加如下代碼:

if (ctx->pctx->pkey->type==EVP_PKEY_EC) {

if (EC_GROUP_get_curve_name(EC_KEY_get0_group(ctx->pctx->pkey->

pkey.ec))

==NID_sm2) {

unsigned char ex_dgst[EVP_MAX_MD_

SIZE];

size_t ex_dgstlen=EVP_MAX_MD_

SIZE;

if (!ECDSA_sm2_get_Z(ctx->pctx->

pkey->pkey.ec, type, NULL,

0, ex_dgst, &ex_dgstlen))

return 0;

if (!EVP_DigestUpdate(ctx, ex_dgst,

ex_dgstlen))

return 0;

}

}

1.3.4.5將SM2曲線內置于OpenSSL中

關于國產密碼OpenSSL中,SM2公鑰算法的實現其實有一個前提,那就是SM2曲線必須為OpenSSL所支持的曲線.

原始的OpenSSL中,并不支持SM2曲線,因此需要通過修改源代碼的方式,將SM2曲線參數添加到OpenSSL中.

實現方法是修改OpenSSL源代碼目錄crypto/ec中的文件ec_curve.c.首先在此文件的數據結構_ec_list_element_st之前,添加如下數據結構:

static const struct {

EC_CURVE_DATA h;

unsigned char data[0+32*6];

} _EC_SM2={

{

NID_X9_62_prime_field, 0, 32, 1

},

{

/*not need seed*/

/*p=FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFF*/

0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,

0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,

/*a=FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFC*/

0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,

0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC,

/*b=28E9FA9E 9D9F5E34 4D5A9E4B CF6509A7 F39789F5 15AB8F92 DDBCBD41 4D940E93*/

0x28, 0xE9, 0xFA, 0x9E, 0x9D, 0x9F, 0x5E, 0x34,

0x4D, 0x5A, 0x9E, 0x4B, 0xCF, 0x65, 0x09, 0xA7,

0xF3, 0x97, 0x89, 0xF5, 0x15, 0xAB, 0x8F, 0x92,

0xDD, 0xBC, 0xBD, 0x41, 0x4D, 0x94, 0x0E, 0x93,

/*Gx=32C4AE2C 1F198119 5F990446 6A39C994 8FE30BBF F2660BE1 715A4589 334C74C7*/

0x32, 0xC4, 0xAE, 0x2C, 0x1F, 0x19, 0x81, 0x19,

0x5F, 0x99, 0x04, 0x46, 0x6A, 0x39, 0xC9, 0x94,

0x8F, 0xE3, 0x0B, 0xBF, 0xF2, 0x66, 0x0B, 0xE1,

0x71, 0x5A, 0x45, 0x89, 0x33, 0x4C, 0x74, 0xC7,

/*Gy=BC3736A2 F4F6779C 59BDCEE3 6B692153 D0A9877C C62A4740 02DF32E5 2139F0A0*/

0xBC, 0x37, 0x36, 0xA2, 0xF4, 0xF6, 0x77, 0x9C,

0x59, 0xBD, 0xCE, 0xE3, 0x6B, 0x69, 0x21, 0x53,

0xD0, 0xA9, 0x87, 0x7C, 0xC6, 0x2A, 0x47, 0x40,

0x02, 0xDF, 0x32, 0xE5, 0x21, 0x39, 0xF0, 0xA0,

/*n=FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF 7203DF6B 21C6052B 53BBF409 39D54123*/

0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,

0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,

0x72, 0x03, 0xDF, 0x6B, 0x21, 0xC6, 0x05, 0x2B,

0x53, 0xBB, 0xF4, 0x09, 0x39, 0xD5, 0x41, 0x23

}

};

然后,在數據結構常量curve_list中,添加SM2曲線的數據(在任意正確位置都可以):

{NID_sm2, &_EC_SM2.h, 0, ″SM2 curve over a 256 bit prime field″}.

到此為止,OpenSSL中即可全面地對國產密碼算法SM2公鑰算法進行支持.

1.4 其他國密算法

如果需要國產密碼OpenSSL支持其他的國密算法,可以借鑒以上所述的方式來進行添加.

1.5 國密TLS協議的實現

國密TLS協議的實現需要根據GM/T 0024—2014《SSL VPN技術規范》,并實現國產密碼認證體系,在標準的SSL/TLS協議的基礎上來實現.

通過對比標準的TLS1.1協議,國密TLS1.1協議有如下特點:

1) 傳輸入層的版本號為0x0101,而不是標準的TLS1.1協議的0x0302;

2) 需要雙證書支持,即簽名證書和加密證書同時工作,而標準的TLS協議無需雙證書支持;

3) 客戶端需要獨立API接口來完成國密TLS的建鏈過程;

4) 無需校驗對方是否支持SM2曲線,因為使用國密套件,對方一定會支持SM2曲線.

1.5.1國密TLS協議握手流程

國密TLS協議其實主要體現在建鏈過程之中.圖2所示,是完整的建鏈過程.同時,國密TLS協議也支持會話重用,重用流程如圖3所示.

圖2 國密TLS協議握手流程

圖3 會話重用流程

1.5.2國產密碼雙證書認證體系的實現

在OpenSSL實現的SSL/TLS中,設計有支持RSA的雙證書認證體系,但OpenSSL并沒有提供RSA雙證書體系的API;對于ECC的證書認證體系來說,其簽名和數據加密或密鑰協商均使用同一套證書與其對應的私鑰來完成.

而國產密碼認證體系要求使用雙證書[12-14],無論是RSA證書還是SM2證書.因此,國密TLS協議中所屬套件均要求使用雙證書認證,即簽名證書與其對應的私鑰只用于簽名和驗證書;而數據加密或者密鑰協商需要使用加密或者密鑰協商證書與其對應的私鑰.

要在OpenSSL中實現國密TLS協議,就必須要添加對ECC(SM2其實也是一種特定的ECC)的雙證書認證.

至于SM2雙證書在何時使用,可參見圖2.在國密TLS協議的握手過程之中,ServerKeyExchange和ClientKeyExchange過程需要用到加密或密鑰協商證書進行密鑰協商;同時它也需要使用數字簽名證書(客戶端除外)進行數字簽名;客戶端CertificateVerify過程需要使用數字簽名證書進行數字簽名;而服務端GetClientKeyExchange和GetClientCertVerify過程需要使用數字簽名證書進行簽名驗證.

在國產密碼OpenSSL中,原始的SSL/TLS證書認證體系API可直接用于數字簽名證書的認證接口;針對RSA和SM2證書,設計了如下的一套加密證書接口API:

int SSL_use_enc_RSAPrivateKey(SSL*ssl, RSA*rsa);

int SSL_use_enc_RSAPrivateKey_ASN1(SSL*ssl, unsigned char*d, long len);

int SSL_use_enc_RSAPrivateKey_file(SSL*ssl, const char*file, int type);

int SSL_use_enc_PrivateKey(SSL*ssl, EVP_PKEY*pkey);

int SSL_use_enc_PrivateKey_ASN1(int type, SSL*ssl, const unsigned char*d, long len);

int SSL_use_enc_PrivateKey_file(SSL*ssl, const char*file, int type);

int SSL_CTX_use_enc_RSAPrivateKey(SSL_CTX*ctx, RSA*rsa);

int SSL_CTX_use_enc_RSAPrivateKey_ASN1(SSL_CTX*ctx, const unsigned char*d, long len);

int SSL_CTX_use_enc_RSAPrivateKey_file(SSL_CTX*ctx, const char*file, int type);

int SSL_CTX_use_enc_PrivateKey(SSL_CTX*ctx, EVP_PKEY*pkey);

int SSL_CTX_use_enc_PrivateKey_ASN1(int type, SSL_CTX*ctx, const unsigned char*d, long len)

int SSL_CTX_use_enc_PrivateKey_file(SSL_CTX*ctx, const char*file, int type);

而針對OpenSSL中經過適當修改后,可適應雙證書認證體系的API如下:

int SSL_use_certificate(SSL*ssl, X509*x);

int SSL_use_certificate_file(SSL*ssl, const char*file, int type);

int SSL_use_certificate_ASN1(SSL*ssl, const unsigned char*d, int len);

int SSL_CTX_use_certificate(SSL_CTX*ctx, X509*x);

int SSL_CTX_use_certificate_file(SSL_CTX*ctx, const char*file, int type);

int SSL_CTX_use_certificate_ASN1(SSL_CTX*ctx, int len,const unsigned char*d);

由這2組API構成了國密TLS雙證書認證體系.

1.5.3國密TLS套件的實現

目前,國密TLS協議中定義了許多密碼套件,可用于建立國密TLS協議的鏈路.其中,SM9和RSA相關密碼套件由于使用不多或者有其局限性,這里不作論述,在此,著重說明ECC-SM4-SM3(EC-SM1-SM3和它相同,只不過SM1需要硬件支持)和ECDHE-SM4-SM3(ECDHE-SM1-SM3和它相同,只不過SM1需要硬件支持)這2個密碼套件的實現.

在國產密碼OpenSSL中,ECC-SM4-SM3,ECDHE-SM4-SM3這2個密碼套件的實現,對于客戶端來說需要實現SendClientKeyExchange和Get ServerKeyExchange這2個過程;而對于服務端來說,需要實現Send ServerKeyExchange和GetClientKeyExchange這2個過程.

1.5.3.1ECC-SM4-SM3密碼套件的實現

ECC-SM4-SM3這個密碼套件是指客戶端無證書,或者客戶端只有驗證服務端證書的證書鏈(它一般指的是CA的證書),此時與國密TLS服務器所建的鏈就是使用ECC-SM4-SM3這個密碼套件.

此時,SendServerKeyExchange過程作如下處理:

1) 對客戶端隨機數+服務端隨機數+服務端加密/協商證書進行數字簽名;

2) 發送此簽名的ASN1的編碼結果.

Get ServerKeyExchange過程作如下處理:

1) 取服務端加密證書;

2) 對客戶端隨機數+服務端隨機數+服務端加密/協商證書進行簽名驗證;

3) 成則繼續,否則中斷握手.

SendClientKeyExchange過程作如下處理:

1) 在48 B的緩沖區中,填入0x0101(國密TLS的版本號)+46 B的隨機數,構成預主密鑰;

2) 使用服務端加密證書對此預主密鑰加密;

3) 發送加密結果的ASN1的編碼結果.

GetClientKeyExchange過程作如下處理:

1) 使用加密私鑰對客戶端發來的結果作解密運算,得到預主密鑰;

2) 使用預主密鑰計算主密鑰;

3) 保留主密鑰,以便會話重用.

1.5.3.2ECDHE-SM4-SM3密碼套件的實現

ECDHE-SM4-SM密碼套件是在服務端和客戶端之間,使用SM2密鑰協商協議,生成48 B主密鑰,然后再完成握手過程.

此時,SendServerKeyExchange過程作如下處理:

1) 生成服務端臨時SM2密鑰對,取臨時公鑰的字符串值;

2) 對臨時公鑰的內容進行簽名;

3) 將臨時公鑰和數字簽名發送給客戶端.

Get ServerKeyExchange過程作如下處理:

1) 取服務端發送的數據,并對其進行簽名驗證;如果驗證失敗則中斷握手過程;

2) 取服務端臨時密鑰對并保存.

SendClientKeyExchange過程作如下處理:

1) 生成客戶端臨時公鑰;

2) 使用默認ID為1234567812345678,調用SM2密鑰協商過程,生成48 B的預主密鑰;

3) 向服務端發送客戶端的臨時公鑰.

GetClientKeyExchange過程作如下處理:

1) 取客戶端發送的臨時公鑰;

2) 使用默認ID為1234567812345678,調用SM2密鑰協商過程,生成48 B的預主密鑰;

3) 計算主密鑰并保存,以便會話重用.

2 國產密碼OpenSSL的應用

國產密碼OpenSSL在保留OpenSSL特性的同時,也將國產密碼的相關特性集成到其中,可以應用到許多方面.

2.1 替換國產操作系統中的OpenSSL模塊

在世界范圍內,OpenSSL的使用極其廣泛.我國也有大量的應用軟件、系統軟件是依賴于OpenSSL構建的.

國產密碼OpenSSL不僅提供了國密算法函數,同時也保留了原版OpenSSL的所有功能特點,它可以無縫替換原版OpenSSL,包括命令行工具等等,從而實現應用軟件的密碼算法從國際算法到國密算法的遷移.

目前在大部分國產操作系統中都集成了OpenSSL.通過使用國產密碼OpenSSL替換國產操作系統中原有的OpenSSL,以實現對國密算法的支持.

2.2 學習和推廣國密算法

通過對國密OpenSSL源碼的分析和理解、對其API的應用,再比照國密標準,可以更容易地學習國密算法和國密規范,讓國密OpenSSL的使用者更加廣泛地、自由地使用國密算法,體會國密算法的優越性、安全性.

通過國產密碼OpenSSL可以加深對國密認證體系的認識.國密OpenSSL的雙證書體系不僅僅應用于SM2證書,同樣也可以應用于RSA證書.使國密雙證書的認證體系可以有著更好的應用前景.

2.3 構建支持SM2證書簽發的CA認證中心

國產密碼OpenSSL已經實現了對國產密碼認證體系的支持,參照我國關于CA系統的相關標準,可以構建符合中國標準的CA系統.

2.4 與其他開源軟件配套使用

國產密碼OpenSSL可以和nginx,HAproxy,apache等知名的開源軟件配套使用,從而構建支持國密算法的應用環境.具體構建方式為,將nginx,HAproxy等源代碼作適量修改,以支持國密雙證書體系及國密TLS協議,在國密OpenSSL的基礎上重新構建.

3 結束語

本著回報社會、推動國產密碼算法推廣的目的,江南天安已經將天安版國產密碼OpenSSL作為TaSSL項目開源(可以從https://github.com/jntass/TASSL獲取),本文中所有的實現,或者是未提供源代碼實現的部分都可以從TaSSL開源的項目中獲取,同時也有大量的示例程序,以供業界同仁參考,歡迎業界同仁和密碼技術愛好者下載使用,并且提供寶貴的意見.

本文僅僅是拋磚引玉,志在引起OpenSSL愛好者對國密的興趣和熱情.由于篇幅所限,許多的話題言而未盡,愿與國密同仁及愛好者共勉之,一同為推動國密事業而努力.

[1]OpenSSL Software Foundation. OpenSSL cryptography and SSL/TLS toolkit[OL]. [2018-01-15]. https://www.openssl.org

[2]中華人民共和國密碼行業標準. GM/T 0024—2014 SSL VPN技術規范[S]. 北京: 中國標準出版社, 2012

[3]中華人民共和國密碼行業標準. GM/T 0002—2012 SM4分組密碼算法[S]. 北京: 中國標準出版社, 2012

[4]中華人民共和國密碼行業標準. GM/T 0004—2012 SM3密碼雜湊算法[S]. 北京: 中國標準出版社, 2012

[5]中華人民共和國密碼行業標準. GM/T 0003—2012 SM2橢圓曲線公鑰密碼算法 第1部分: 總則[S]. 北京: 中國標準出版社, 2012

[6]Daniel R L, Brown. SEC 1: Elliptic curve cryptography[OL]. 2009 [2018-01-15]. http://www.secg.org/sec1-v2.pdf

[7]中華人民共和國密碼行業標準. GM/T 0006—2012 密碼應用標識規范[S]. 北京: 中國標準出版社, 2012

[8]中華人民共和國密碼行業標準. GM/T 0003—2012 SM2橢圓曲線公鑰密碼算法 第2部分: 數字簽名算法[S]. 北京: 中國標準出版社, 2012

[9]中華人民共和國密碼行業標準. GM/T 0003—2012 SM2橢圓曲線公鑰密碼算法 第4部分: 公鑰加密算法[S]. 北京: 中國標準出版社, 2012

[10]中華人民共和國密碼行業標準. GM/T 0003—2012 SM2橢圓曲線公鑰密碼算法 第3部分: 密鑰交換協議[S]. 北京: 中國標準出版社, 2012

[11]中華人民共和國密碼行業標準. GM/T 0003—2012 SM2橢圓曲線公鑰密碼算法 第5部分: 參數定義[S]. 北京: 中國標準出版社, 2012

[12]中華人民共和國密碼行業標準. GM/T 0009—2012 SM2密碼算法使用規范[S]. 北京: 中國標準出版社, 2012

[13]中華人民共和國密碼行業標準. GM/T 0014—2012 數字證書認證系統密碼協議規范[S]. 北京: 中國標準出版社, 2012

[14]中華人民共和國密碼行業標準. GM/T 0015—2012 基于SM2密碼算法的數字證書格式規范[S]. 北京: 中國標準出版社, 2012

主站蜘蛛池模板: 国产精品欧美在线观看| 久久精品娱乐亚洲领先| 亚洲看片网| 9966国产精品视频| 尤物精品国产福利网站| 精品久久人人爽人人玩人人妻| 99久久亚洲精品影院| 99在线小视频| 国产伦片中文免费观看| 亚洲侵犯无码网址在线观看| 精品国产香蕉在线播出| 亚洲天堂视频网站| 日韩福利视频导航| 国产伦片中文免费观看| 国产中文在线亚洲精品官网| 亚洲人成影视在线观看| 欧美综合成人| 伊人久久大香线蕉成人综合网| 亚洲国产黄色| 青草国产在线视频| h网址在线观看| 999精品视频在线| 亚洲成人黄色在线| 国产精品手机在线播放| 欧美精品H在线播放| 久久 午夜福利 张柏芝| 伊人大杳蕉中文无码| 亚洲AV无码一二区三区在线播放| 丁香婷婷激情网| 97国产一区二区精品久久呦| 国产99视频在线| 亚洲天堂久久| 婷婷开心中文字幕| 亚洲人成人伊人成综合网无码| 动漫精品中文字幕无码| 成人午夜视频在线| 国产亚洲精品精品精品| 67194亚洲无码| 久久国产精品夜色| 欧美啪啪网| 国产网站免费看| 91亚洲视频下载| 久久亚洲欧美综合| 日韩a级片视频| 国产在线自揄拍揄视频网站| 国产精品对白刺激| 欧美区一区二区三| 国产男女免费完整版视频| 国产无码制服丝袜| 国产一级无码不卡视频| 在线观看网站国产| 国产美女精品人人做人人爽| 欧美在线国产| 国产成人无码Av在线播放无广告| 91毛片网| 成人无码区免费视频网站蜜臀| 国产永久在线视频| 久久无码高潮喷水| 国产精品太粉嫩高中在线观看| 国产网友愉拍精品| 亚洲综合色婷婷| 91在线丝袜| 亚洲区视频在线观看| 永久成人无码激情视频免费| 一本色道久久88| 亚洲av色吊丝无码| 二级特黄绝大片免费视频大片| 亚洲永久视频| 色综合久久综合网| 国产成人毛片| 久久大香伊蕉在人线观看热2| 成人在线综合| 高h视频在线| 国产欧美日韩专区发布| 狠狠干欧美| www.99在线观看| 午夜不卡福利| 青草娱乐极品免费视频| 日韩精品毛片| 成人精品午夜福利在线播放| 国产免费羞羞视频| 午夜啪啪福利|