劉 聰
(德州學(xué)院 信息管理學(xué)院,山東 德州 253000)
分組密碼“Kuznyechik”是俄羅斯密碼標(biāo)準(zhǔn)算法集合(稱為GOST算法)之一。該算法能夠保證信息在傳輸過程中的機密性和完整性,它符合現(xiàn)代密碼的要求,具有安全、高效率、易用和靈活等優(yōu)點。適用于開發(fā)、運用到各種用途的現(xiàn)代信息系統(tǒng)中,保護數(shù)據(jù)的安全。為了提高Kuznyechik算法在實際應(yīng)用中的加密速度,本文根據(jù)Kuznyechik算法的特點,與Java線程池技術(shù)結(jié)合,提出采用線程池技術(shù)并行加密Kuznyechik的方法。
通常情況下,加密程序都是單線程運行的。為了提高程序的加密速度,可以設(shè)置多個線程去運行。Java語言對多線程提供了非常優(yōu)秀的支持,基本的Java線程模型有Thread類、Runnable接口、Callable接口、Future接口等。
線程池就是在程序運行時創(chuàng)建一定數(shù)量的線程保存起來,當(dāng)需要時,從線程池中取出一個線程。完成相應(yīng)任務(wù)后再放回到線程池中。通過線程池,可以有效降低資源消耗、提高程序響應(yīng)速度、提高線程的可管理性。根據(jù)線程池的工作原理,算法程序需要線程池管理器、工作線程、任務(wù)隊列、數(shù)據(jù)加密實體幾個部分。
Kuznyechik在俄羅斯聯(lián)邦GOST R 34.12-2015的國家標(biāo)準(zhǔn)中定義,它有128比特的塊大小和256比特的密鑰長度。該算法是俄羅斯密碼標(biāo)準(zhǔn)算法集合(稱為GOST算法)之一。該算法可以在硬件和軟件中實現(xiàn),能夠保證信息在傳輸過程中的機密性和完整性,它符合現(xiàn)代密碼的要求。該標(biāo)準(zhǔn)適用于開發(fā),運用到各種用途的現(xiàn)代信息系統(tǒng)。
該算法包括密鑰編排和加密兩部分,密鑰編排算法是Feistel結(jié)構(gòu)的,有左右兩支,在密鑰編排算法中,主密鑰k為256比特,分為k1和k2,各128比特。首先k1與常數(shù)c1相加,將得到的結(jié)果經(jīng)過s盒,L置換,然后與k2相加,得左支到k1′,之前k1的值變成k2′。密鑰編排流程如圖1所示:
圖1 密鑰編排流程圖
經(jīng)過8輪相同的運算,最后得到編排后的密鑰k3和k4,然后以同樣的方式生成k5和k6、k7和k8、k9和k10。其中L置換經(jīng)過下面公式得到:
其中a是128位的,經(jīng)過線性反饋移位寄存器轉(zhuǎn)16拍,其中轉(zhuǎn)換的定義如下:
其中ai是8位,a16是經(jīng)過Ⅰ運算得到的,Ⅰ運算的定義如下:
I(a15,…,a0)=?(148·Δ(a15)+32·Δ(a14)
+133·Δ(a13)+16·Δ(a12)+194·Δ(a11)
+192·Δ(a10)+1·Δ(a9)+251·Δ(a8)
+1·Δ(a7)+192·Δ(a6)+194·Δ(a5)+16·Δ(a4))
+133·Δ(a3)+32·Δ(a2)+148·Δ(a1)+1·Δ(a0))
I運算的輸入是16個8位二進制數(shù),輸出為1個8位二進制數(shù)。表示將8位二進制數(shù)轉(zhuǎn)換成有限域內(nèi)的元素,▽是它的逆運算。經(jīng)過有限域內(nèi)的乘法運算,然后相加,最后轉(zhuǎn)換成1個8位二進制輸出。計算有限域乘法,首先要確定GF(2)上的8次不可約多項式。在該算法中,這個不可約多項式為
在加密算法中,明文塊a為128比特,密鑰k1,…,k10分別為128位,經(jīng)過10輪運算,分別使用密鑰k1,…,k10。加密算法定義如下:
具體加密流程如圖2所示:
圖2 加密流程圖
Kuznyechik要進行10輪循環(huán)加密,因為根據(jù)算法的特點,事先進行密鑰編排,得到10個輪密鑰進行循環(huán)加密。根據(jù)加密算法的特點,本文設(shè)計了適用于Kuznyechik算法加密的線程池。首先創(chuàng)建任務(wù)實體,要加密數(shù)據(jù)的輸入和輸出保存到任務(wù)實體的屬性中。
public class encryptionData{
public String inputData;
public String outputData;
public String getInputData(){
return inputData;
}
public String getOutputData(){
return outputData;
}
public void setInputData(String inputData) {
this.inputData=inputData;
}
public void setOutputData(String outputData) {
this.outputData=outputData;
}
在主線程中,首先創(chuàng)建隊列和工作線程,然后創(chuàng)建任務(wù)實體,通過set方法給實體的屬性賦值,然后將加密數(shù)據(jù)對象實體加入到任務(wù)隊列中,最后不斷循環(huán)檢測工作線程的隊列是否為空,如果為空,說明數(shù)據(jù)已經(jīng)加密完成,線程池銷毀[3-4]。在主線程工作的同時,工作線程不斷從任務(wù)隊列中取出任務(wù)實體,獲取要加密的內(nèi)容,不斷進行加密,加密結(jié)束存儲密文,當(dāng)工作線程將所有任務(wù)實體處理完后,隊列為空,線程池銷毀。具體運行流程如圖3所示。
圖3 Java線程池的kuznyechik算法流程圖
根據(jù)算法原理和加密流程,本文采用128bit的密鑰,需要加密10輪。首先需要設(shè)計線程池管理類,該類內(nèi)部成員包括線程池實體、任務(wù)隊列和工作線程實體,線程池實體使用單例模式來設(shè)計,并加入同步鎖保證線程池對象的唯一[5]。任務(wù)隊列用來存放加密實體對象,工作線程不斷從任務(wù)隊列中取出任務(wù)實體,從任務(wù)實體中獲取需要加密的明文,然后調(diào)用加密方法進行輪加密運算。以下是線程池管理類的主要代碼:
public class ThreadPool{
private static ThreadPool instance=null;
public List<encryptionData> taskQueue = Collections.synchronizedList (new LinkedList<encryptionData>());//任務(wù)隊列
//工作線程(真正執(zhí)行任務(wù)的線程)
private WorkThread workQueue;
private ThreadPool(){
workQueue=new WorkThread();
}
public static synchronized ThreadPool getInstance(){
if(instance==null)
instance=new ThreadPool();
return instance;
}
public void addTask(encryptionData task){
//對任務(wù)隊列的操作上鎖
synchronized(taskQueue){
if(task!=null){
taskQueue.add(task);
taskQueue.notifyAll();
}
}
}
public void destory(){
workQueue.stopThread();
workQueue=null;
//對任務(wù)隊列的操作上鎖
synchronized(taskQueue){
taskQueue.clear();
}
}
工作線程的主要代碼如下:
public void run(){
while(isRuning){
encryptionData temp=null;
//對任務(wù)隊列的操作要上鎖
synchronized(taskQueue){
//任務(wù)隊列為空,等待新的任務(wù)加入
while(isRuning&&taskQueue.isEmpty()){
try{
taskQueue.wait(10);
}catch(InterruptedException e){
e.printStackTrace();
}
}
i( fisRuning)
temp=taskQueue.remove(0);
}
i(ftemp!=null){
isWaiting=false;
//執(zhí)行加密函數(shù)
Encryption.encresult(temp.getInputData());
isWaiting=true;
}
}
在加密函數(shù)中,主要通過明文與密鑰相加運算,然后通過S盒和L運算,得到密文,其中,S盒和L運算的主要代碼如下:
進s盒運算,使用以下方法來實現(xiàn),其中s盒的長度為256,返回的結(jié)果是s盒的輸出。
public static char[] sbox(char x[]){
for(int i=0;i<16;i++){
a[i]=(char)x[i];
c[i]= (char) s[a[i]];
}
return c;
}
L運算主要進行了16次R運算,主要代碼如下:
for(int i=0;i<16;i++){
char e= ltrans(k11);
for(int j=0;j<15;j++){
k11[j]=k11[j+1];
}
k11[15] =e;
}
其中,ltrans表示l運算。在l運算中,主要用到的是GF(2^8)有限域內(nèi)乘法,這個域的最大值為256。有限域內(nèi)的乘法使用以下的方法實現(xiàn):
public static char multiply(char a, char b){
char temp[]={a,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
char tempmultiply=0x00;
int i=0;
for(i=1;i< 8;i++){
temp[i] =XTIME(temp[i-1]);
}
tempmultiply=(char)((b&0x01)*a);
for(i=1;i<=7;i++){
tempmultiply^=(((b >> i)&0x01)*temp[i]);
}
return tempmultiply;
}
在二進制中,所有的數(shù)都能用0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80異或得到,任何一個數(shù)b和a進行相乘,都可以表示為b和0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80分別相乘,然后異或得到。只要計算出b和這幾個數(shù)的乘積,一切結(jié)果都可以得到。XTIME方法的含義是求一個數(shù)與0x02的乘積,如果求一個數(shù)乘以2,一般是左移一位,本算法中,不可約多項式為p(x)=x8+x7+x6+x+1,如果最高位為1,再繼續(xù)左移會超出最大值,這時,左移1位的同時要異或0xc3。XTIME的具體實現(xiàn)代碼如下:
public static char XTIME(char x) {
int k=0x00;
if((x&0x80)!=0)
k=0xc3;
return(char)((((x << 1)^k))&0xff);
}
經(jīng)過8次temp[i]=XTIME(temp[i-1])運算,temp數(shù)組中包含8個字符,分別為:a*0x01,a*0x02,a*0x04,a*0x08,a*0x10,a*0x20,a*0x40,a*0x80。然后經(jīng)過8次tempmulti?ply^=(((b >> i)&0x01)*temp[i])運算,另一個乘數(shù)b首先進行1位右移,再和0x01與運算,然后分別和這8個字符相乘,把相乘的結(jié)果進行相加,得到a和b的乘積。
本文使用Java語句,在eclipse平臺進行測試,硬件采用Intel(R)CoreI-5(TM)i5-8400(主頻2.8GHZ,6核)32G內(nèi)存的臺式機作為平臺進行測試。測試用到了加速比,未采用線程池和采用線程池程序加密算法的運行時間分別記為τa和τb,經(jīng)過測試,結(jié)果如下表1所示:
表1 加密驗證結(jié)果
測試表明,在數(shù)據(jù)量比較少時,采用線程池比未采用線程池加密效率略高,但是隨著數(shù)據(jù)量的增大,采用線程池明顯比不采用線程池加密效率明顯增高。隨著數(shù)據(jù)量的增加,加速比逐漸變陡。下面是原始的測試數(shù)據(jù),圖4-圖7:
圖4 1k數(shù)據(jù)加密示意圖
圖5 10k數(shù)據(jù)加密示意圖
圖6 100k數(shù)據(jù)加密示意圖
圖7 1000k數(shù)據(jù)加密示意圖
本文通過設(shè)計線程池,基于Java線程池技術(shù),對Kuznyechik算法進行了改進,通過eclipse平臺進行測試,測試結(jié)果表明,隨著數(shù)據(jù)量的增大,能夠明顯增加加密的效率,縮短加密時間,為大數(shù)據(jù)環(huán)境下的加密奠定了基礎(chǔ)。在未來的研究中,將通過GPU的并行計算,來進一步提高加密效率。