本人一直想找时间系统整理一下之前做过的一些实验,便于后续用到的时候可以尽快的使用,po出来也便于大家交流学习,有问题欢迎交流指正,与诸君共勉!
实验目的
(1)了解祖冲之密码算法的线性反馈移位寄存器;
(2)了解祖冲之密码算法的非线性函数;
(3)熟悉祖冲之密码算法的工作流程。
实验原理
图1 祖冲之密码算法结构图
祖冲之算法结构从逻辑上分为上中下三层, 如图1所示。上层是16级线性反馈移位寄存器(LFSR), 中层是比特重组(Bit Recondtruction,BR), 下层是非线性函数F。
下面是各层的具体解释。
1.线性反馈移位寄存器
线性反馈移位寄存器(LFSR)由16个31比特寄存器单元组成,每个单元在集合
中取值。
线性反馈移位寄存器的特征多项式是有限域GF()上的16次本原多项式
因此,其输出为有限域GF()上的m序列,具有良好的随机性。
线性反馈移位寄存器的工作模式有两种:初始化模式和工作模式。
(1)初始化模式
在初始化模式中,LFSR在接收一个31比特字u,u是由非线性函数F的32比特输出W通过舍弃最低位比特得到,即u=W>>1。计算过程如下:
LFSRWithInitialisationMode(u)
{
- ;
- ;
- 如果 ,则置 ;
- 。
}
(2)工作模式
在工作模式下,LFSR没有输入。计算过程如下:
LFSRWithWorkMode()
{
- ;
- 如果 ,则置 ;
- 。
}
比较两种模式,差别在于初始化时需要引入非线性函数F输出的W通过舍弃最低位比特得到u,而工作模式不需要。目的在于,引入非线性函数F的输出,使线性反馈移位寄存器的状态随机化。
LFSR的作用主要是为中层的比特重组(BR)提供随机性良好的输入驱动。
2.比特重组
比特重组从LFSR的寄存器单元中抽取128比特组成4个32比特字 ,其中前3个字将用于下层的非线性函数F,第四个字参与密钥流的计算。
具体计算过程如下:
BitReconstruction()
{
- ;
- ;
- ;
- ;
}
注意:对每个i(0≤i≤15), 的比特长是31,所以由 的第30到第15比特位构成,而不是第31到第16比特位。
3.非线性函数F
非线性函数F有2个32比特长的存储单元R1和R2,其输入为来自上层比特重组的3个32比特字X0,X1,X2,输出为一个32比特字W。因此,非线性函数F是一个把96比特压缩到32比特的一个非线性函数。具体计算过程如下:
F()
{
- ;
- ;
- ;
- ;
- ;
}
其中,S是32×32的S盒,L1、L2是线性变换。S盒及、的描述如下:
(1)S盒。32×32(即输入长和输出长都是32比特)的S盒S由4个并置的8×8的S盒构成,即
其中,,,于是有
的定义表省略。
设x是(或)的8比特长输入,将x写成两个十六进制数x=h| l,那么 (或 )的输出是S盒状态 (或 )表的第h行l列交叉位置的十六进制数。
设S的输入、输出分别为X(32比特长)和Y(32比特长),将X和Y分别表示成4个字节 , ,那么 ,(i=0,1,2,3)。
(2)线性变换、 。 、 为32比特线性变换,定义如下:
其中,符号a<<<n表示把a循环左移n位。
易知其中线性变换 与SM4密码中的线性变换L(B)相同。
在非线性函数F中采用非线性变换S盒的目的是提供混淆作用,采用线性变换L的目的是提供扩散作用。正是混淆和扩散相互配合提高了密码的安全性。
非线性函数F输出的W与比特重组(BR)输出的 异或,形成输出密钥序列Z。
4.密钥装载
密钥装载过程将128比特的初始密钥k和128比特的初始向量IV扩展为16个31比特长的整数,作为LFSR寄存器单元 的初始状态。
设k和IV分别为
和
其中, 和 均为8比特长字节,0≤i≤15。密钥装入过程如下:
(1)设D为240比特的常量,可按如下方式分成16个15比特的子串:
其中, 的二进制表示为:
(2)对0≤i≤15,取
实验内容及步骤
实验基本方法
用java语言编写祖冲之密码算法,输出加解密结果。
实验步骤
(1)祖冲之密码算法的输入输出;
(2)祖冲之密码算法的初始化;
(3)祖冲之密码算法的密钥流产生;
(4)祖冲之密码算法的加解密。
祖冲之密码的运行
算法的运行有两个阶段:初始化阶段和工作阶段。
-
初始化阶段
调用密钥装载过程,将128比特的初始密钥k和128比特的初始向量IV装入LFSR的寄存器单元变量s0,s1,...,s14,s15中,作为LFSR的初态,并置非线性函数F中的32比特存储单元R1和R2全为0.
然后重复执行以下过程32次:
- BitReconstruction();
- W=F(X0,X1,X2);
- LFSRWithInitialisationMode(u)。
-
工作阶段
初始化阶段之后,执行工作阶段。
首先执行以下过程一次,并将F的输出W丢弃:
- BitReconstruction();
- W=F(
(3) LFSRWithWorkMode(u)。
然后进入密钥输出阶段,其中每进行一次循环,执行以下过程一次,输出一个32比特的密钥字Z:
- BitConstruction():
- Z= F( ;
3.LFSRWithWorkMode()。
实验代码
package main.java;
import java.util.Scanner;
/**
* ZUC加解密系统用户交互主类
* @author AGATHA_66
* @date 2022年11月10日
* @todo
*/
public class ZUC_main {
/**
* 初始化操作之后功能选择菜单
*/
public static void menu(){
System.out.println("|=============请选择您的操作:=============|");
System.out.println("| 1.产生密钥流 |");
System.out.println("| 2.加密 |");
System.out.println("| 3.解密 |");
System.out.println("| 4.退出 |");
System.out.println("|=====================================|");
}
/**
* ZUC初始化用户交互过程
* @return 返回初始化后的ZUC对象
*/
public static ZUC ini_main() {
Scanner reader = new Scanner(System.in);
int[] iv = new int[16];
int[] key = new int[16];
System.out.println("请输入初始密钥KEY:--------------------");
for(int i = 0; i < 16;i++)
iv[i] = reader.nextInt();
System.out.println("请输入初始向量IV:--------------------");
for(int i = 0; i < 16;i++)
key[i] = reader.nextInt();
ZUC zuc = new ZUC();
//调用初始化函数
zuc.Initialization(key, iv);
return zuc;
}
public static void creat_KeyStream_main() {
Scanner reader = new Scanner(System.in);
System.out.println("请输入LENGTH:--------------");
int keyStreamSize = reader.nextInt();
long[] pKeyStream = new long[keyStreamSize];
ZUC.GenerateKeyStream(pKeyStream, keyStreamSize);
System.out.println("--------------------------");
System.out.println("生成密钥流:");
for (int i = 0; i < keyStreamSize; ++i) {
//System.out.print("第"+(i+1)+"个:");
System.out.print(Integer.toHexString((int)pKeyStream[i])+" ");
}
System.out.println();
}
/**
* 解密过程将接收到的16进制或10进制的密文转换为long数组 用户交互过程
* @return
*/
public static long[] getC(int keyStreamSize) {
Scanner reader = new Scanner(System.in);
System.out.println("-请选择密文进制数:(1)10进制;(2)16进制");
int choice2 = reader.nextInt();
System.out.println("请输入密文:");
long[] C = new long[keyStreamSize];
if(choice2==1) {//密文为10进制表示
for(int i = 0;i < keyStreamSize;i++)
C[i] = reader.nextLong();
}
else if(choice2==2) {//密文为16进制表示
String[] C0 = new String[keyStreamSize];
for(int i = 0;i < keyStreamSize;i++) {
C0[i] = reader.next();
C[i] = Long.parseLong(C0[i],16);//16进制转10进制
}
}
return C;
}
/**
* 主函数
* @param args
*/
public static void main(String[] args) {
boolean fig = true;
Scanner reader = new Scanner(System.in);
//初始化
ZUC zuc = ini_main();
System.out.println("ZUC初始化成功!");
menu();
int choice = reader.nextInt();
int choice0 = 0;
//防止输入不规范bug
if(choice == 1)choice0 = 1;
else if(choice == 2)choice0 = 2;
else if(choice == 3)choice0 = 3;
else if(choice == 4)choice0 = 4;
else System.out.println("请输入1~4的数字!");
switch (choice0){
case 1://选择产生密钥流
creat_KeyStream_main();
break;//产生密钥流
case 2://选择加密
//明文输入
System.out.println("请输入明文:--------------------");
String M = reader.next();
//产生密钥流(定长)
int keyStreamSize = M.length();
long[] pKeyStream = new long[keyStreamSize];
ZUC.GenerateKeyStream(pKeyStream, keyStreamSize);
//加密:
long[] C = zuc.Encry(M,pKeyStream);
char[] M2 = zuc.Decry(C,pKeyStream);
System.out.println();
break;
case 3://选择解密
//密文输入
System.out.println("请输入比特流比特长度LENGTH:");
int keyStreamSize1 = reader.nextInt();
long[] pKeyStream1 = new long[keyStreamSize1];
ZUC.GenerateKeyStream(pKeyStream1, keyStreamSize1);
long[] C1 = getC(keyStreamSize1);
//解密:
char[] mm = zuc.Decry(C1,pKeyStream1);
System.out.println();
break;
case 4://系统退出
System.out.println("系统已退出!");return;
default:
break;
}
reader.nextLine();//吸收回车符
System.out.println("按回车键退出");
reader.nextLine();
reader.close();
System.exit(0);
}
}
package main.java;
/**
* @author AGATHA
* @date 2022年11月8日
* @todo 祖冲之密码算法加密工具类
*/
public class ZUC {
/** 线性反馈移位寄存器状态 数组*/
private static int[] LFSR_S = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
/** F 寄存器 */
private static int F_R1 = 0;
private static int F_R2 = 0;
/** 比特重组输出数组 */
private static int[] BRC_X = {0,0,0,0};
/**S盒 */
private static int[] S0 = {
0x3e, 0x72, 0x5b, 0x47, 0xca, 0xe0, 0x00, 0x33, 0x04, 0xd1, 0x54, 0x98, 0x09, 0xb9, 0x6d, 0xcb,
0x7b, 0x1b, 0xf9, 0x32, 0xaf, 0x9d, 0x6a, 0xa5, 0xb8, 0x2d, 0xfc, 0x1d, 0x08, 0x53, 0x03, 0x90,
0x4d, 0x4e, 0x84, 0x99, 0xe4, 0xce, 0xd9, 0x91, 0xdd, 0xb6, 0x85, 0x48, 0x8b, 0x29, 0x6e, 0xac,
0xcd, 0xc1, 0xf8, 0x1e, 0x73, 0x43, 0x69, 0xc6, 0xb5, 0xbd, 0xfd, 0x39, 0x63, 0x20, 0xd4, 0x38,
0x76, 0x7d, 0xb2, 0xa7, 0xcf, 0xed, 0x57, 0xc5, 0xf3, 0x2c, 0xbb, 0x14, 0x21, 0x06, 0x55, 0x9b,
0xe3, 0xef, 0x5e, 0x31, 0x4f, 0x7f, 0x5a, 0xa4, 0x0d, 0x82, 0x51, 0x49, 0x5f, 0xba, 0x58, 0x1c,
0x4a, 0x16, 0xd5, 0x17, 0xa8, 0x92, 0x24, 0x1f, 0x8c, 0xff, 0xd8, 0xae, 0x2e, 0x01, 0xd3, 0xad,
0x3b, 0x4b, 0xda, 0x46, 0xeb, 0xc9, 0xde, 0x9a, 0x8f, 0x87, 0xd7, 0x3a, 0x80, 0x6f, 0x2f, 0xc8,
0xb1, 0xb4, 0x37, 0xf7, 0x0a, 0x22, 0x13, 0x28, 0x7c, 0xcc, 0x3c, 0x89, 0xc7, 0xc3, 0x96, 0x56,
0x07, 0xbf, 0x7e, 0xf0, 0x0b, 0x2b, 0x97, 0x52, 0x35, 0x41, 0x79, 0x61, 0xa6, 0x4c, 0x10, 0xfe,
0xbc, 0x26, 0x95, 0x88, 0x8a, 0xb0, 0xa3, 0xfb, 0xc0, 0x18, 0x94, 0xf2, 0xe1, 0xe5, 0xe9, 0x5d,
0xd0, 0xdc, 0x11, 0x66, 0x64, 0x5c, 0xec, 0x59, 0x42, 0x75, 0x12, 0xf5, 0x74, 0x9c, 0xaa, 0x23,
0x0e, 0x86, 0xab, 0xbe, 0x2a, 0x02, 0xe7, 0x67, 0xe6, 0x44, 0xa2, 0x6c, 0xc2, 0x93, 0x9f, 0xf1,
0xf6, 0xfa, 0x36, 0xd2, 0x50, 0x68, 0x9e, 0x62, 0x71, 0x15, 0x3d, 0xd6, 0x40, 0xc4, 0xe2, 0x0f,
0x8e, 0x83, 0x77, 0x6b, 0x25, 0x05, 0x3f, 0x0c, 0x30, 0xea, 0x70, 0xb7, 0xa1, 0xe8, 0xa9, 0x65,
0x8d, 0x27, 0x1a, 0xdb, 0x81, 0xb3, 0xa0, 0xf4, 0x45, 0x7a, 0x19, 0xdf, 0xee, 0x78, 0x34, 0x60
};
/** S盒 */
private static int[] S1 = {
0x55, 0xc2, 0x63, 0x71, 0x3b, 0xc8, 0x47, 0x86, 0x9f, 0x3c, 0xda, 0x5b, 0x29, 0xaa, 0xfd, 0x77,
0x8c, 0xc5, 0x94, 0x0c, 0xa6, 0x1a, 0x13, 0x00, 0xe3, 0xa8, 0x16, 0x72, 0x40, 0xf9, 0xf8, 0x42,
0x44, 0x26, 0x68, 0x96, 0x81, 0xd9, 0x45, 0x3e, 0x10, 0x76, 0xc6, 0xa7, 0x8b, 0x39, 0x43, 0xe1,
0x3a, 0xb5, 0x56, 0x2a, 0xc0, 0x6d, 0xb3, 0x05, 0x22, 0x66, 0xbf, 0xdc, 0x0b, 0xfa, 0x62, 0x48,
0xdd, 0x20, 0x11, 0x06, 0x36, 0xc9, 0xc1, 0xcf, 0xf6, 0x27, 0x52, 0xbb, 0x69, 0xf5, 0xd4, 0x87,
0x7f, 0x84, 0x4c, 0xd2, 0x9c, 0x57, 0xa4, 0xbc, 0x4f, 0x9a, 0xdf, 0xfe, 0xd6, 0x8d, 0x7a, 0xeb,
0x2b, 0x53, 0xd8, 0x5c, 0xa1, 0x14, 0x17, 0xfb, 0x23, 0xd5, 0x7d, 0x30, 0x67, 0x73, 0x08, 0x09,
0xee, 0xb7, 0x70, 0x3f, 0x61, 0xb2, 0x19, 0x8e, 0x4e, 0xe5, 0x4b, 0x93, 0x8f, 0x5d, 0xdb, 0xa9,
0xad, 0xf1, 0xae, 0x2e, 0xcb, 0x0d, 0xfc, 0xf4, 0x2d, 0x46, 0x6e, 0x1d, 0x97, 0xe8, 0xd1, 0xe9,
0x4d, 0x37, 0xa5, 0x75, 0x5e, 0x83, 0x9e, 0xab, 0x82, 0x9d, 0xb9, 0x1c, 0xe0, 0xcd, 0x49, 0x89,
0x01, 0xb6, 0xbd, 0x58, 0x24, 0xa2, 0x5f, 0x38, 0x78, 0x99, 0x15, 0x90, 0x50, 0xb8, 0x95, 0xe4,
0xd0, 0x91, 0xc7, 0xce, 0xed, 0x0f, 0xb4, 0x6f, 0xa0, 0xcc, 0xf0, 0x02, 0x4a, 0x79, 0xc3, 0xde,
0xa3, 0xef, 0xea, 0x51, 0xe6, 0x6b, 0x18, 0xec, 0x1b, 0x2c, 0x80, 0xf7, 0x74, 0xe7, 0xff, 0x21,
0x5a, 0x6a, 0x54, 0x1e, 0x41, 0x31, 0x92, 0x35, 0xc4, 0x33, 0x07, 0x0a, 0xba, 0x7e, 0x0e, 0x34,
0x88, 0xb1, 0x98, 0x7c, 0xf3, 0x3d, 0x60, 0x6c, 0x7b, 0xca, 0xd3, 0x1f, 0x32, 0x65, 0x04, 0x28,
0x64, 0xbe, 0x85, 0x9b, 0x2f, 0x59, 0x8a, 0xd7, 0xb0, 0x25, 0xac, 0xaf, 0x12, 0x03, 0xe2, 0xf2
};
/** 240比特常量D 16*15*/
private static int[] EK_d = {
0x44D7, 0x26BC, 0x626B, 0x135E, 0x5789, 0x35E2, 0x7135, 0x09AF,
0x4D78, 0x2F13, 0x6BC4, 0x1AF1, 0x5E26, 0x3C4D, 0x789A, 0x47AC
};
/**
* 模加 c = a + b mod 2^31 - 1
* (两个数的和 和 7FFFFFFF按位与 )+(两个数的和 和 80000000按位与的结果 无符号右移31位)
* 7:0111 8:1000
* @param a
* @param b
* @return
*/
private static int AddM(int a, int b) {
int c = a + b;
return (c & 0x7FFFFFFF) + ((c & 0x80000000) >>> 31);
}
/**
* 返回[(左移k位 )按位或(无符号右移31-k位)]和7FFFFFFF按位与的结果
* @param x
* @param k
* @return
*/
private static int MulByPow2(int x, int k) {
return ((((x) << k) | ((x) >>> (31 - k))) & 0x7FFFFFFF);
}
/**
* 初始化模式下,LFSR接收一个31比特字u
* v = (2^15)*s[15]+(2^17)*s[13]+(2^21)*s[10]+(2^20)*s[4]+(1+2^8)*s[0] mod (2^31-1);
s[16]=(v+u) mod (2^31-1);
* @param u 由非线性函数F的32比特输出W舍弃最低位(右移一位)得到
*
*/
private static void LFSRWithInitializationMode(int u) {
int f, v;
f = LFSR_S[0];
v = MulByPow2(LFSR_S[0], 8);
f = AddM(f, v);
v = MulByPow2(LFSR_S[4], 20);
f = AddM(f, v);
v = MulByPow2(LFSR_S[10], 21);
f = AddM(f, v);
v = MulByPow2(LFSR_S[13], 17);
f = AddM(f, v);
v = MulByPow2(LFSR_S[15], 15);
f = AddM(f, v);
//此时完成v的计算
f = AddM(f, u);
//状态转换(s[1],s[2],...,s[16])-→(s[0],s[1],...,s[15]) 这里s[16]即为现在的f
for (int i = 0; i < 15; ++i) {
LFSR_S[i] = LFSR_S[i + 1];
}
LFSR_S[15] = f;
}
/**
* 工作模式下 无输入
*/
private static void LFSRWithWorkMode() {
int f, v;
f = LFSR_S[0];
v = MulByPow2(LFSR_S[0], 8);
f = AddM(f, v);
v = MulByPow2(LFSR_S[4], 20);
f = AddM(f, v);
v = MulByPow2(LFSR_S[10], 21);
f = AddM(f, v);
v = MulByPow2(LFSR_S[13], 17);
f = AddM(f, v);
v = MulByPow2(LFSR_S[15], 15);
f = AddM(f, v);
/**状态更新*/
for (int i = 0; i < 15; ++i) {
LFSR_S[i] = LFSR_S[i + 1];
}
LFSR_S[15] = f;
}
/**
* Bit Reorganization Procedure 比特重组
* & 0x7FFF8000 << 1 截取高16位 >>>15
* & 0xFFFF <<16 截取低十六位
*/
private static void BitReorganization() {
BRC_X[0] = ((LFSR_S[15] & 0x7FFF8000) << 1) | (LFSR_S[14] & 0xFFFF);
BRC_X[1] = ((LFSR_S[11] & 0xFFFF) << 16) | (LFSR_S[9] >>> 15);
BRC_X[2] = ((LFSR_S[7] & 0xFFFF) << 16) | (LFSR_S[5] >>> 15);
BRC_X[3] = ((LFSR_S[2] & 0xFFFF) << 16) | (LFSR_S[0] >>> 15);
}
/**
* 非线性函数
* @return
*/
private static long F() {
long W, W1, W2, u, v;
W = (BRC_X[0] ^ F_R1) + F_R2;
W = W & 0x00000000FFFFFFFFL;
W1 = F_R1 + BRC_X[1];
W1 = W1 & 0x00000000FFFFFFFFL;
W2 = F_R2 ^ BRC_X[2];
W2 = W2 & 0x00000000FFFFFFFFL;
u = L1((W1 << 16) & 0x00000000FFFFFFFFL | (W2 >>> 16));
u = u & 0x00000000FFFFFFFFL;
v = L2((W2 << 16) & 0x00000000FFFFFFFFL | (W1 >>> 16));
v = v & 0x00000000FFFFFFFFL;
F_R1 = MAKEU32(S0[(int) (u >>> 24)], S1[(int) ((u >>> 16) & 0xFF)], S0[(int) ((u >>> 8) & 0xFF)], S1[(int) (u & 0xFF)]);
F_R2 = MAKEU32(S0[(int) (v >>> 24)], S1[(int) ((v >>> 16) & 0xFF)], S0[(int) ((v >>> 8) & 0xFF)], S1[(int) (v & 0xFF)]);
return W;
}
/**
* a循环左移k位
* @param a
* @param k
* @return a循环左移k位的结果
*/
private static long ROL(long a, int k) {
return (((a) << k) | ((a) >>> (32 - k)));
}
/** 线性变换 L1 */
private static long L1(long X) {
return (X ^ ROL(X, 2) ^ ROL(X, 10) ^ ROL(X, 18) ^ ROL(X, 24));
}
/** 线性变换 L2 */
private static long L2(long X) {
return (X ^ ROL(X, 8) ^ ROL(X, 14) ^ ROL(X, 22) ^ ROL(X, 30));
}
/** 产生32比特的字 */
private static int MAKEU32(int a, int b, int c, int d) {
return (int) (((long) a << 24) | ((long) b << 16) | ((long) c << 8) | ((long) d));
}
/** 产生31比特的字 */
private static int MAKEU31(int a, int b, int c){
return (int)(((long)(a) << 23) | ((long)(b) << 8) | (long)(c));
}
/**
* 初始化阶段
* 将初始密钥和初始向量装入LFSR作为其初态,并置R1和R2为0,然后进行一系列操作。
* @param k
* @param iv
*/
public static void Initialization(int[] k, int[] iv) {
long w;
/** 循环设置LFSR初态 */
for (int i = 0; i < 16; ++i) {
LFSR_S[i] = MAKEU31(k[i], EK_d[i], iv[i]);
}
long nCount = 32;
while (nCount > 0) {
BitReorganization();
w = F();
LFSRWithInitializationMode((int) (w >>> 1));
nCount--;
}//循环32次
BitReorganization();
F();
LFSRWithWorkMode();
}
/**
* 工作阶段产生密钥流
* @param pKeyStream
* @param KeyStreamLen
*/
public static void GenerateKeyStream(long[] pKeyStream, long KeyStreamLen) {
long W;
BitReorganization();
F();
LFSRWithWorkMode();
W = F();
//System.out.println("初始密钥字:"+W);
//System.out.println("--------------------------------------");
for (int i = 0; i < KeyStreamLen; ++i) {
BitReorganization();//比特重组
pKeyStream[i] = F() ^ BRC_X[3] & 0x00000000ffffffffL;
LFSRWithWorkMode();
}
}
/**
* 加密方法
* @param M 明文
* @param keys 密钥
* @return C 密文
*/
public static long[] Encry(String M,long[]keys)
{
int length = M.length();
long[] C = new long[length];
char[] chars = M.toCharArray();
System.out.println("--------------------------");
System.out.println("ZUC加密结果:");
for (int i = 0; i < length; i++) {
C[i] = chars[i] ^ keys[i];
System.out.print(Integer.toHexString((int)C[i])+" ");
}
return C;
}
/**
* 解密方法
* @param C 密文
* @param keys 密钥
* @return M 明文
*/
public static char[] Decry(long[] C,long[]keys)
{
int length = C.length;
char[] M = new char[length];
System.out.println("--------------------------");
System.out.println("解密结果:");
for (int i = 0; i < length; i++) {
M[i] = (char) (C[i] ^ keys[i]);
System.out.print(M[i]);
}
return M;
}
}