1. 程式人生 > >談談PBOC3.0中使用的國密SM2演算法

談談PBOC3.0中使用的國密SM2演算法

轉載請註明出處

http://blog.csdn.net/pony_maggie/article/details/39780825

作者:小馬

一 知識準備

SM2是國密局推出的一種他們自己說具有自主智慧財產權的非對稱商用密碼演算法。本身是基於ECC橢圓曲線演算法的,所以要講sm2, 先要弄懂ECC。

完全理解ECC演算法需要一定的數學功底,因為涉及到射影平面座標系,齊次方程求解, 曲線的運算規則等概念。這裡不做過多的數學分析(主要是我自己也沒有完全整明白)。想要深入瞭解ECC的我推薦網名為ZMWorm 的大牛在多年前寫的<<橢圓曲線ECC加密演算法入門介紹>>。此人是早年看雪論壇中的一個版主,對演算法和密碼學很有研究。

本篇的主旨還是希望能以簡單通俗的語言,講清楚PBOC3.0認證過程中,所用到的SM2的相關概念,包括它的實現,使用等。

1 橢圓曲線到底是什麼樣的


圖1

圖2

上面是兩個不同橢圓曲線在座標系中的幾何表示, 不過這個座標系不是二維座標系,而是射影座標系。可以用空間思維想像一下(但是注意不是三維座標系),打個比方,你晚上站在一個路燈前面,地上有你的影子,你本身是在一個二維座標系(把你想像成一個紙片),和你的影子一起構成一個射影座標系。

曲線的每一個點, 用三個參量表示, (X,Y,Z)。我們知道在二維座標系裡的每個圖形都遵循一個方程,比如直接的二元一次方程是y=kx+b, 圓的方程是(x-a)2+(y-b)2=r2, 橢圓曲線在射影座標系裡也有自己的定義:

Y2Z+a1XYZ+a3YZ2=X3+a2X2Z+a4XZ2+a6Z3

所有橢圓曲線上的點都滿足上述方程,a1,a2,a3,a4,a6是係數,決定曲線的形狀和位置。

二維座標和射影座標有一個對應關係,即x=X/Z, y=Y/Z, 這樣就可以把上面的方程轉成普通的二維座標系方程:

y2+a1xy+a3y= x3+a2x2+a4x+a6

2 離散的橢圓曲線

上面的座標系都是基於實數的,橢圓曲線看起來都是平滑的,如果我們限制曲線的點都必須是整數,曲線就變成離散的了,如圖3所示:


圖3

再進一步限制,要求整數必須大於0, 小於某個大整數P, 這樣就形成了一個有限域Fp.然後我們在這個有限域裡定義一些點與點之間的加減乘除運算規則,比如A點加B點得到C點(記做A+B≡C (mod p)),或者A點乘以n得到K點(記做A×n≡K (mod p))。至於具體規則細節可以不用關心,只要知道有這樣的操作即可。

選一條曲線,比如

y2=x3+ax+b

把它定義在Fp上, 要求a,b滿足:

4a3+27b2≠0 (mod p)

我們把這樣的曲線記為Ep(a,b)

加解密是基於這樣的數學難題,K=kG,其中 K,G為Ep(a,b)上的點,k是整數,小於G點(注意區分,不是我們平常說的那個意思)的階(不用關心什麼是點的階)。給定k和G,計算K很容易;但給定K和G,求k就困難了。這樣,G就叫做基點,k是私鑰,K是公鑰。

最後總結。描述一條Fp上的橢圓曲線,有六個參量: 
T=(p,a,b,G,n,h)。


p 、a 、b 用來確定一條橢圓曲線, 
G為基點, 
n為點G的階, 
h 是橢圓曲線上所有點的個數m與n相除的整數部分)

知識準備階段知道這麼多就可以了。

二 SM2在PBOC認證中的使用

1 簽名和驗籤的原理

前面提到根據係數的不同,ECC曲線可以有很多,SM2使用其中一種,這就表明它的曲線方程,以及前面說到的六個參量都是固定的。根據國密局給出的規範定義如下:

  1. y2=x3+ax+b  
  2. p=FFFFFFFE FFFFFFFFFFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFF  
  3. a=FFFFFFFE FFFFFFFFFFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFC  
  4. b=28E9FA9E 9D9F5E344D5A9E4B CF6509A7 F39789F5 15AB8F92 DDBCBD41 4D940E93  
  5. n=FFFFFFFE FFFFFFFFFFFFFFFF FFFFFFFF 7203DF6B 21C6052B 53BBF409 39D54123  
  6. Gx=32C4AE2C 1F198119 5F990446 6A39C994 8FE30BBF F2660BE1 715A4589 334C74C7  
  7. Gy=BC3736A2 F4F6779C 59BDCEE3 6B692153 D0A9877C C62A4740 02DF32E5 2139F0A0  


這裡容易引起一個誤解,會認為引數都固定了,公私鑰是不是隻能有一對?當然不是,注意前面提到的K=kG的模型,K才是公鑰,所以公鑰其實是曲線在離散座標系中,滿足條件的一個曲線上的點。可以有很多個。另外, 從這幾個參量可以獲知PBOC 3.0的公鑰長度都是256位。

基於這種離散橢圓曲線原理的SM2演算法一般有三種用法,簽名驗籤,加解密, 金鑰交換。PBOC 3.0中的離線資料認證只用到簽名驗籤的功能。終端關心的是如何驗籤,卡片則要考慮如何實現生成簽名。

2 基於openssl實現sm2

這裡給出一個基於openssl的sm2實現, 如果不瞭解openssl,可以先搜尋一下相關知識,這裡不講解。openssl已經實現ECC演算法介面,也就是核心已經有了,實現sm2其實並不難,關鍵是理解它裡面各種介面如何使用。下面就分析一個終端驗籤的sm2實現。另外需要說明這裡給出的只是程式碼片段,僅供理解用。

函式介面

  1. int SM2_Verify(BYTE* Px,BYTE* Py, BYTE* DataIn,DWORD DataLen, BYTE* sigrs)  

前兩個引數是K的座標值,也就是公鑰。Datain和datalen分別是明文資料和其長度,最後一個引數是待驗證的簽名。

曲線的引數常量定義如下:

  1. ////////////////////////////////////////////////////////////////
  2. staticconstchar *group_p ="FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF";  
  3. staticconstchar *group_a ="FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC";  
  4. staticconstchar *group_b ="28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93";  
  5. staticconstchar *group_Gx ="32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7";  
  6. staticconstchar *group_Gy ="BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0";  
  7. staticconstchar *group_n = "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123";  
  8. staticconstchar *ENTL_ID ="008031323334353637383132333435363738";  
  9. #define SM2_KEY_LENGTH 32 //256位曲線
  10. ////////////////////////////////////////////////////////////////

ENTL_ID是pboc規範中指定的用於SM3產生摘要的報文頭資料。

  1. strcpy(szBuff, ENTL_ID);  
  2. strcat(szBuff,group_a);  
  3. strcat(szBuff,group_b);  
  4. strcat(szBuff,group_Gx);  
  5. strcat(szBuff,group_Gy);  
  6. AscToBcd(szDataForDigest,(unsigned char *)szBuff, nLen);  
  7.       ….  
  8. SM3(szDataForDigest,nLen+SM2_KEY_LENGTH*2, digestZA);  
  9. memcpy(szDataForDigest,digestZA, ECC_LENGTH);  
  10. memcpy(szDataForDigest+ECC_LENGTH,DataIn, DataLen);  
  11. SM3(szDataForDigest,DataLen+ECC_LENGTH, digestH);  
第一步,對上述資料用sm3做摘要,摘要的結果與資料拼接後再做摘要。
  1. p = BN_new();  
  2. a = BN_new();  
  3. b = BN_new();  
  4. group = EC_GROUP_new(EC_GFp_mont_method());  
  5. BN_hex2bn(&p, group_p))  
  6. BN_hex2bn(&a, group_a))  
  7. BN_hex2bn(&b, group_b))  
這裡是把定義的曲線常量轉換成大數表式,這樣才能使用openssl中的介面。

Group是ECC中的曲線組,它是ECC演算法的核心,為什麼這麼說呢? 因為這個group裡的所有欄位就確定了曲線的所有資訊, 後面會看到,這裡只是用EC_GROUP_new生成一個空的group, 然後由p,a,b等引數來填充group, 再以這個group為基礎去生成曲線上的點。

  1. if (!EC_GROUP_set_curve_GFp(group, p, a, b,ctx))  
  2. {  
  3.                    gotoerr_process;  
  4. }  
  5. P = EC_POINT_new(group);  
  6. Q = EC_POINT_new(group);  
  7. R = EC_POINT_new(group);  
  8. if (!P || !Q || !R)  
  9. {  
  10.          gotoerr_process;  
  11. }  
這一段就確定了group的所有資訊,並且根據group生成三個曲線上的點(點一定在曲線上,這個很重要)。
  1. <span style="white-space:pre">    </span>x = BN_new();  
  2.          y= BN_new();  
  3.          z= BN_new();  
  4.          if(!x || !y || !z)  
  5.          {  
  6.                    gotoerr_process;  
  7.          }  
  8.          //Gx
  9.          if(!BN_hex2bn(&x, group_Gx))  
  10.          {  
  11.                    gotoerr_process;  
  12.          }  
  13.          if(!EC_POINT_set_compressed_coordinates_GFp(group, P, x, 0, ctx))  
  14.          {  
  15.                    gotoerr_process;  
  16.          }  
  17.          if(!BN_hex2bn(&z, group_n))  
  18.          {  
  19.                    gotoerr_process;  
  20.          }  
  21.          if(!EC_GROUP_set_generator(group, P, z, BN_value_one()))  
  22.          {  
  23.                    gotoerr_process;  
  24.          }  
  25.          if(!EC_POINT_get_affine_coordinates_GFp(group, P, x, y, ctx))  
  26.          {  
  27.                    gotoerr_process;  
  28.          }  

這一段首先是設定n到group, n前面講過,是曲線的階。另外就是由G點座標x,y確定點P,這裡我其實也有點不太明白,G點座標y本來就是已知的,為什麼要再通過曲線變換表式生成y,是不是想要對比前面的group資訊是否正確?只是為了校驗?

  1. if ((eckey = EC_KEY_new()) == NULL)  
  2.          {  
  3.                    gotoerr_process;  
  4.          }  
  5.          if(EC_KEY_set_group(eckey, group) == 0)  
  6.          {  
  7.                    gotoerr_process;  
  8.          }  
  9. EC_KEY_set_public_key(eckey, P);  
  10.          if(!EC_KEY_check_key(eckey))  
  11.          {  
  12.                    gotoerr_process;  
  13.          }  
  14.          if(SM2_do_verify(1, digestH, SM2_KEY_LENGTH, signature, sig_len, eckey) != 1)  
  15.          {  
  16.                    gotoerr_process;  
  17.          }  

這段比較好理解,生成公鑰eckey,並由eckey最終驗籤。驗籤的執行函式sm2_do_verify有點複雜,這裡不做過多的解釋(其實是我解釋不清楚, 哇哈哈),在一個國外的網站上找到一個比較好的描述,拿過來用用。



3 離線資料認證使用sm2的具體流程

我假設看這篇文章的人對PBOC 2.0中基於RSA國際演算法的離線資料認證流程已經比較瞭解,相關概念不再過多描述,重點關注二者的差異性。

另外,簡單說一下sm3,因為下面會用到。sm3是一個類似hash的雜湊演算法,即給定一個輸入(一般很長),產生一個固定長度的輸出(sm3是32個位元組,hash是20個位元組)。它有兩個特點:

1 不可逆性,即無法由輸出推匯出輸入。

2 不同的輸入,產生不同的輸出。

下面就拿終端SDA認證卡片來看看sm2如何在pboc中使用的。

首先,髮卡行公鑰等資料(還包括公鑰,演算法標識,有效期等資訊)被CA私鑰簽名(注意不是加密)生成髮卡行公鑰證書,這個證書會個人化到ic卡中。這些資料如下圖所示:


圖4

簽名其實就是對這個表裡的資料做一系列運算,最終生成一個64位元組的資料,分別由各32位元組的r和s拼接而成(r和s只是個符號而已,沒有特別意思)。這64位元組的簽名拼接到圖4後面就是髮卡行公鑰證書。

這裡與國際演算法的公鑰證書就有明顯的區別了。國際演算法證書是密文的,髮卡行公鑰用rsa加密。而國密的公鑰證書完全是明文。用資料舉例,下面這些資料來自pboc3.0的卡片送檢指南,就是檢測時要求個人化到卡里的資料。

  1. 【髮卡行公鑰】 :  
  2. 173A31DD681C6F8FE3BA6C354AD3924A4ADFD15EB0581BC1B37A1EB1C88DA29B47155F62FCF4CCCD201B134351A049D77E81F6A6C66E9CB32664F41348DA11F  
  3. 【CA雜湊值】(r||s) :  
  4. 3499A2A0A7FED8F74F119B416FF728BA98EF0A32A36BCCB8D0110623D466425CA44C68F8E49121D9BFA9484CAEF9B476C5EB576D1A8DD6BC4A0986AF4134ABAF  
  5. 【Tag_90 】(髮卡行公鑰證書) :  
  6. 1262280001122000000204001140173A31DD681C6F8FE3BA6C354AD3924A4ADFD15EB0581BC1B37A1EB1C88DA29B47155F62FCF4CCCD201B134351A049D77E86A6C66E9CB32664F41348DA11F63499A2A0A7FED8F74F119B416FF728BA98EF0A32A36BCCB8D0110623D466425CA44C68F8E49121D9BFA9484CAEF9B476C5E56D1A8DD6BC4A0986AF4134ABAF  

可以自己的解析一下,看看是否和上面表格中的資料一致, 並且都是明文。

終端在讀記錄階段獲取髮卡行公鑰證書,國際演算法需要用rsa公鑰解密整個證書,然後驗證hash,通過後取出髮卡行公鑰。而國密演算法,終端只要用sm2公鑰驗64位元組的簽名,通過後直接取明文髮卡行公鑰,所以國密的驗籤的動作其實就相當於國際裡的rsa解密和hash對比兩個動作。

接著終端進行靜態資料簽名的驗證,情況類似,對於國密,這些資料都是明文形式,後面拼接64位元組的sm2簽名,這64位元組是用髮卡行私鑰對明文資料簽名得到的。終端要做的就是拿剛剛獲取的髮卡行公鑰對這64位元組資料驗證即可,驗證通過就表示SDA通過。

四 PBOC為什麼要選擇國密

首先從技術角度,實現同樣的計算複雜度,ECC的計算量相對RSA較小,所以效率高。RSA現在在不斷的增加模長,目前都用到了2048位。並不是說現在一定要用2048位才是安全的,只是它的安全性更高,破解難度更大,這個要綜合考慮,位數高也意味著成本高。有些不差錢的大公司比如谷歌就已經未雨綢繆的把2048位用在了它們的gmail郵箱服務中。PBOC3.0 認證中目前只用到1984位,其實也是相對安全的。

不過這個觀點目前還存在爭議。前段時間在清華大學聽了一個關於密碼演算法的課,清華有個教授認為sm2並不見得比rsa更高階,只是sm2的原理比rsa難理解,所以大部分人認為它會相對安全些。一旦橢圓曲線被大家研究透了,sm2的光環也可能就此褪去。當然這個也是他個人的觀點。

另外一個因素,要從國家戰略的角度考慮,RSA之前一直被傳與美國安全域性合作,在演算法中加入後門,這種事是寧可信其有的。國密演算法咱起碼是自己研發的東西,所有的過程細節都一清二楚, 不用擔收後門的事情。

央行現在非常重視國產安全晶片的推進工作,前些天央行的李曉楓還公開強調未來金融IC卡晶片要國產化,國密演算法是其中很關鍵的一步。未來國產晶片加國密演算法,才會有真正自主, 安全的國產金融IC卡產品。