中南大学
现 代 密 码 学 实 验 报 告
学生姓名 张振宇 学 号 0906130221 专业班级 信息安全1302班 指导教师 段桂华 学 院 信息科学与工程学院 完成时间 2015年5月
目 录
实验一 密码算法实验 ............................................... - 2 -
[实验目的] . ................................................................................................ - 2 -
[实验预备] . ................................................................................................ - 2 -
[实验内容] . ................................................................................................ - 2 -
[实验步骤] . ................................................................................................ - 2 -
1. AES 算法 .......................................................................................................... - 3 - 2. RC4算法 ........................................................................................................ - 11 -
[问题讨论] . .............................................................................................. - 15 - 实验二 SKEY 协议设计实验 ...................................... - 16 -
[实验目的] . .............................................................................................. - 16 -
[实验预备] . .............................................................................................. - 16 -
[实验内容] . .............................................................................................. - 16 -
[实验步骤] . .............................................................................................. - 16 -
1. 协议分析 ........................................................................................................ - 17 -
2. 协议实现 ........................................................................................................ - 17 -
3. 运行结果 ........................................................................................................ - 21 -
[问题讨论] . .............................................................................................. - 22 -
实验一 密码算法实验
[实验目的]
1.掌握密码学中经典的对称密码算法AES、RC4的算法原理。
2.掌握AES、RC4的算法流程和实现方法。
[实验预备]
1.AES 算法的基本原理和特点。
2.流密码RC4的密钥流生成以及S 盒初始化过程。
[实验内容]
1. 分析AES、RC4的实现过程。2. 用程序设计语言将算法过程编程实现。
3. 完成字符串数据的加密运算和解密运算
输入十六进制明文:[***********]00AABBCCDDEEFF
输入十六进制密钥:13579BDF02468ACE1234567890ABCDEF
[实验步骤]
1. 预习AES、RC4算法。
2. 写出算法流程,用程序设计语言将算法过程编程实现。
3. 输入指定的明文、密钥进行实验,验证结果。
4. 自己选择不同的输入,记录输出结果。
1.
AES 算法
算法分析
Rijndael (State , ExpandedKey)
{
AddRoundKey (State , ExpandedKey);
for (i =1; i
Round (State , ExpandedKey+Nb * i);
FinalRound (State , ExpandedKey+Nb *Nr )
}
Round (State , RoundKey)
{
ByteSub (State );
ShiftRow (State );
MixColumn (State );
AddRoundKey (State , RoundKey)
}
FinalRound (State , RoundKey)
{
ByteSub (State );
ShiftRow (State );
AddRoundKey (State , RoundKey)
}
编程实现
Subbyte (字节替换)
非线性的字节替代,单独处理每个字节:
求该字节在有限域GF(28) 上的乘法逆,"0" 被映射为自身,
即对于α∈GF(28) ,求β∈GF(28) ,使得α·β=β·α=1mod(x8+x4+x2+x+1)。 对上一步求得的乘法逆作仿射变换
yi=xi + x(i+4)mod8 + x(i+6)mod8 + x(i+7)mod8 + ci
为了数据处理的方便,我在这里使用的是置换表的方法。
public
byte [][] subbyte(byte [][] sub, int r){
byte row, col ;
byte [][] temp=new byte [4][4];
for (int i=0; i
for (int j=0; j
col=(byte )(sub [i ][j ]&0xf );
row=(byte )((sub [i ][j ]>>4)&0xf );
temp[i ][j ]=rsbox [row ][col ];
}
return temp;
}
ShiftRows (行移位变换)
行移位变换完成基于行的循环位移操作,变换方法:
即行移位变换作用于行上,第0行不变,第1行循环左移1个字节,第2行循环左移2个字节,第3行循环左移3个字节。
public byte [][] shift(byte [][]
sub
, int mode){
byte temp;
temp=sub [3][0];
sub[3][0]=sub [3][1];sub [3][1]=sub [3][2];
sub[3][2]=sub [3][3];sub [3][3]=temp ;
temp=sub [2][0];
sub[2][0]=sub [2][2];sub [2][2]=temp ;
temp=sub [2][1];
sub[2][1]=sub [2][3];sub [2][3]=temp ;
temp=sub [1][0];
sub[1][0]=sub [1][3];sub [1][3]=sub [1][2];
sub[1][2]=sub [1][1];sub [1][1]=temp ;
return sub;
}
MixColumns (列混合变换)
逐列混合,方法:
b(x) = (03·x3 + 01·x2 + 01·x + 02) · a(x) mod(x4 + 1)
矩阵表示形式:
public byte [][] mix(byte [][] sub, int mode){
byte count=0;
byte [][] temp=new byte [4][4];
for (int i=0; i
for (int j=0; j
while (count
temp[i ][j ]=(byte )(
temp [i ][j ]^mu (rmut [i ][count ],
sub [count ][j ]));
count++;
}
count=0;
}
}
return temp;
}
public byte
mu(byte b, byte c){
byte ret=0 , count1=0, count2=0;
byte [] barray=new byte [8];
byte [] carray=new byte [8];
byte [] pro=new byte [15];
if (b ==1|c ==0) return c;
if (c ==1) return b;
for (int i=0; i
barray[i ]=(byte )(b &1);
b=(byte )(b >>1);
count1++;
}
for (int i=0; i
carray[i ]=(byte )(c &1);
c=(byte )(c >>1);
count2++;
}
for (int i=0; i
for (int j=0; j
if (barray [i ]>0&carray [j ]>0)
pro[i +j ]=(byte )((pro [i +j ]+1)%2);
}
for (int m=0; m
if (pro [m ]>0)
ret=(byte )((by [m ])^(ret ));
}
return ret;
}
AddRoundKey (轮密钥加密)
简单来说就是逐字节相加,有限域GF(2
8) 上的加法是模2加法,即异或。
public byte [][] add(byte sub[][],byte [][] roundkey){
for (int i=0; i
for (int j=0; j
sub[i ][j ]=(byte )(
sub[i ][j ]^roundkey [i ][j ]);
}
return sub;
}
KeyExpansion (密钥扩展)
将输入的密钥扩展为11组128位密钥组,其中第0组为输入密钥本身 其后第n 组第i 列 为 第n-1组第i 列 与 第n 组第i-1列之和
对于每一组第一列有特殊的处理
将前一列即第n-1组第3列的4个字节循环左移1个字节,并对每个字节进行字节替代变换SubBytes 将第一行与轮常量相加,最后再与前一组该列相加
public byte [][][] key(byte [][] okey){
byte [][][] retarray=new byte [11][4
][4];
for (int i=0; i
for (int j=0; j
retarray[0][j ][i ]=okey [i ][j ];
}
for (int i=1; i
retarray[i ]=tkey (retarray [i -1],r [i ]);
}
return retarray;
}
public byte [][] tkey(byte [][] okey, int ri){
byte [][] temp=new byte [4][4];
byte col, row ;
col=(byte )(okey [1][3]&0xf );
row=(byte )((okey [1][3]>>4)&0xf );
temp[0][0]=(byte )(ri ^sbox [row ][col ]^okey [0][0]);
col=(byte )(okey [2][3]&0xf );
row=(byte )((okey [2][3]>>4)&0xf );
temp[1][0]=(byte )(sbox [row ][col ]^okey [1][0]);
col=(byte )(okey [3][3]&0xf );
row=(byte )((okey [3][3]>>4)&0xf );
temp[2][0]=(byte )(sbox [row ][col ]^okey [2][0]);
col=(byte )(okey [0][3]&0xf );
row=(byte )((okey [0][3]>>4)&0xf );
temp[3][0]=(byte )(sbox [row ][col ]^okey [3][0]);
for (int i=1; i
temp[0][i ]=(byte )(temp [0][i -1]^okey [0][i ]);
temp[1][i ]=(byte )(temp [1][i -1]^okey [1][i ]);
temp[2][i ]=(byte )(temp [2][i -1]^okey [2][i ]);
temp[3][i ]=(byte )(temp [3][i -1]^okey [3][i ]);
}
return temp;
}
数据加密: case 1:
try {
Encrypt en=new Encrypt();
System. out . println (" 请输入明文" );
content=in . readLine ();
System. out . println (" 请输入密钥" );
key=in . readLine ();
out=en . textEncrypt (content , key);
System. out . println (out );
while (true ){
System. out . println ("coyp 一下结果? y/n"); ch=in . readLine ();
if (ch . length ()==1){
if (ch . charAt (0)=='y' |
ch . charAt (0)=='Y' )
{save =out ; break ;}
if (ch . charAt (0)=='n' |
ch . charAt (0)=='N' )
break ;
}
}
}catch (IOException e){
e. printStackTrace ();}
break ;
数据解密:
case 2:
try {
if (save !="" &save !=null ){
while (true ){
System . out . println (" 用一下刚刚copy 的结果? y/n" ch =in . readLine ();
if (ch . length ()==1){
if (ch . charAt (0)=='y' |
ch . charAt (0)=='Y' )
{content =save ; break ;}
);
if (ch . charAt
(
)=='n' |
ch . charAt (0)=='N' )
break ;
}
}
}
else {
System. out . println (" 请输入密文" );
content=in . readLine ();
}
System. out . println (" 请输入密钥" );
key=in . readLine ();
Decrypt de=new Decrypt();
out=de . textDecrypt (content , key);
System. out . print (out );
}catch (IOException e){
e. printStackTrace ();System . out . println (" 未知错误);} break ;
运行结果
2. RC4算法
算法分析
RC4算法的原理包括初始化算法和伪随机子密码生成算法两大部分。
假设S-box 长度和密钥长度均为n 。
算法的初始化部分 for (i =0; i
{
s[i ]=i ;
}
j =0;
for (i =0; i
{
j=(j +s [i ]+k [i ])%n ;
swap(s [i ], s
[j ]);
}
在初始化的过程中,密钥的主要功能是将S-box 搅乱,i 确保S-box 的每个元素都得到处理,j 保证S-box 的搅乱是随机的。而不同的S-box 在经过伪随机子密码生成算法的处理后可以得到不同的子密钥序列。
i =j =0;
while (明文未结束)
{
++i %=n ;
j=(j +s )%n ;
swap(s , s[j ]);
sub_k=s ((s +s [j ])%n );
}
得到的子密码sub_k用以和明文进行异或运算,得到密文,解密过程也完全相同。
编程实现 package RC4;
import java.util.Scanner;
public class RC4 {
public static String decry_RC4(byte [] data, String key) {
if (data == null || key == null ) {
return null ;
}
return asString(RC4Base (data , key));
}
public static String decry_RC4(String data, String key) {
if (data == null || key == null ) {
return null ;
}
return new String(RC4Base (HexString2Bytes (data ), key));
}
public static byte [] encry_RC4_byte(String data, String key) { if (data == null || key == null ) {
return null ;
}
byte b_data[] = data. getBytes ();
return RC4Base(b_data, key);
}
public static String encry_RC4_string(String data, String key) { if (data == null || key == null ) {
return null ;
}
return toHexString(asString (encry_RC4_byte(data , key))); }
private static String asString(byte [] buf) {
StringBuffer strbuf = new StringBuffer(buf . length );
for (int i = 0; i
strbuf. append ((char ) buf[i ]);
}
return strbuf. toString ();
}
private static byte [] initKey(String aKey) {
byte [] b_key = aKey. getBytes ();
byte state[] = new byte [256];
for (int i = 0; i
state[i ] = (byte ) i;
}
int index1 = 0;
int index2 = 0;
if (b_key == null || b_key. length == 0) {
return null ;
}
for (int i = 0; i
index2 = ((b_key[index1] & 0xff )
+ (state [i ] & 0xff ) + index2) & 0xff ;
byte tmp = state[i ];
state[i ] = state[index2];
state[index2] = tmp;
index1 = (index1 + 1) % b_key. length ;
}
return state;
}
private static String toHexString(String s) {
String str = "" ;
for (int i = 0; i
int ch = (int ) s. charAt (i );
String s4 = Integer. toHexString (ch & 0xFF );
if (s4. length () == 1) {
s4 = '0' + s4;
}
str = str + s4;
}
return str; // 0x表示十六进制
}
private static byte [] HexString2Bytes(String src) {
int size = src. length ();
byte [] ret = new byte [size / 2];
byte [] tmp = src. getBytes ();
for (int i = 0; i
ret[i ] = uniteBytes(tmp [i * 2], tmp[i * 2 + 1]);
}
return ret;
}
private static byte uniteBytes(byte src0, byte src1) {
char _b0 = (char ) Byte. decode (
"0x" + new String(new byte [] { src0 })).byteValue ();
_b0 = (char ) (_b0
char _b1 = (char ) Byte. decode (
"0x" + new String(new byte [] { src1 })).byteValue ();
byte ret = (byte ) (_b0 ^ _b1);
return ret;
}
private static byte [] RC4Base(byte [] input, String mKkey) { int x = 0, y = 0;
byte key[] = initKey(mKkey );
int xorIndex;
byte [] result = new byte [input . length ];
for (int i = 0; i
x = (x + 1) & 0xff ;
y = ((key [x ] & 0xff ) + y) & 0xff ;
byte tmp = key[x ];
key[x ] = key[y ];
key[y ] = tmp;
xorIndex = ((key [x ] & 0xff ) + (key [y ] & 0xff )) & 0xff ; result[i ] = (byte ) (input [i ] ^ key[xorIndex ]);
}
return result;
}
public static void main(String [] args) {
System. out . println (" 请输入明文:" );
Scanner in=new Scanner(System . in );
String inputStr, keyStr;
inputStr=in . nextLine ();
System. out . println (" 请输入密钥:" );
keyStr = in. nextLine ();
String str = encry_RC4_string(inputStr , keyStr);
System. out . println (" 加密后的密文:" +str );
System. out . print (" 解密后的明文:" );
System. out . println (decry_RC4(str , keyStr));
}
}
运行结果
测试数据1
测试数据2
[问题讨论]
1. 改变明文或密钥中的一个比特值可能影响AES 值中的多少比特?
答:明文和密钥在S 盒置换时,不同的字节会替换出不同的结果。算法过程
中一共进行了10轮加密,所以改变一个比特值可能影响AES 值中的80
比特(8 x 10)。
2. 在RC4的密钥流生成中,改变初始密钥的一个比特值可能影响输
出中的多少比特?
答:初始密钥的不同会导致S 盒的不同,所以可能影响输出中的256比特。
3. 分析实验中在编辑编译运行等各环节中所出现的问题及解决方法。
答:在AES 算法实现过程中,在对输入数据的处理上遇到了很多的问题,刚
开始的时候习惯性地使用string 的形式保存S 盒等数据,但是由于AES
算法用到了很多的异或和移位操作,使用string 和char 形式数据的弊
端就慢慢地暴露了出来。然后在网上查阅了很多资料,最后选择了使用
byte 类型的数据,在整个算法的实现过程中,进一步加深了我对AES 加
密算法和RC4算法的理解。
实验二 SKEY 协议设计实验
[实验目的]
1.掌握身份认证协议的原理和基本思想。
2.掌握SKEY 协议的机制和实现方法。
[实验预备]
1.SKEY 协议的作用。
2.SKEY 协议的安全性分析。
3.SKEY 协议的实现过程。
[实验内容]
1. 分析SKEY 协议的实现过程。
2. 用程序设计语言将算法过程编程实现。
3. 演示SKEY 协议的身份鉴别过程。
[实验步骤]
1. 预习SKEY 协议的机制。
2. 选择和实现相应的摘要算法MD5或SHA。
3. 写出算法流程,用程序设计语言将协议过程编程实现。
4. 验证SKEY 协议的身份鉴别过程。
1. 协议分析
2. 协议实现
为了便于实现和演示,我采用了一个比较简单的方式进行。要模拟客户端保存数据比较不容易实现,对服务器生成的hash 值,保存在本地文件中, 这个hash 的值是动态变化的,每次hash 都会加上相应的登陆次数(这个hash 方式和一般的skey 协议有些不同),
这样可以动态的获取。通过文件读写的方式获得。对于
生成的
hash
值从100到0次访问的问题也是使用一个文件来储存,每次验证完成后,都会把前一次存的数据减1然后重新的写入文件中。
本次实验使用到了Java 中的文件读写,这是用到的两个辅助文件:
其中int.txt 存储登陆后修改的数据
(写进去的格式不是10进值的,所以在这里不能看出明文)
md5.txt 为存储hash 值的文件:
(共100条密钥)
MD5实现文件: package SKEY;
import
java. security . MessageDigest ;
import java. security . NoSuchAlgorithmException ;
public class MD5String {
private String string ;
// 全局数组
private final static String[] strDigits = {
"0" , "1" , "2" , "3" , "4" , "5" , "6" , "7" ,
public MD5String(String string ) {
this . string =string ;
}
private String byteToArrayString(byte bByte) {
int iRet = bByte;
if (iRet
iRet += 256; "8" , "9" , "A" , "B" , "C" , "D" , "E" , "F" };
}
int iD1 = iRet / 16;
int iD2 = iRet % 16;
return strDigits[iD1] + strDigits[iD2];
}
// 转换字节数组为16进制字串
private String byteToString(byte [] bByte) {
StringBuffer sBuffer = new StringBuffer();
for (int i = 0; i
sBuffer. append (byteToArrayString (bByte [i ])); }
return sBuffer. toString ();
}
public String GetMD5Code() {
String resultString = null ;
try {
resultString = new String(string );
MessageDigest. getInstance ("MD5" );
MessageDigest md = MessageDigest. getInstance ("MD5" ); //该函数返回值为存放返回结果的byte 数组
md. digest (string . getBytes ());
byte [] bytes = md. digest (string . getBytes ()); resultString = byteToString(bytes );
} catch (NoSuchAlgorithmException ex) {
}
return resultString;
}
}
Skey 协议实现文件:
package SKEY;
import java. io . BufferedReader ;
import java. io . BufferedWriter ;
import java. io . File ;
import java. io . FileInputStream ;
import java. io . FileOutputStream ;
import java. io . FileReader ;
import java. io . FileWriter ;
import java. io . IOException ;
public class Skey {
String intsString =
"C://Users//zzy//workspace//Cryptology//int.txt";
String md5sString =
"C://Users//zzy//workspace//Cryptology//md5.txt";
File file_int = new File(intsString );
File file_md5 = new File(md5sString );
//初始化
public void GUI(String string) {
try {
FileOutputStream fos = new FileOutputStream(file_int); FileWriter writer = new FileWriter(file_md5);
BufferedWriter bw = new BufferedWriter(writer );
fos. write (1);
for (int a=1; a
MD5String md5 = new MD5String(string +a );
System. out . println (md5. GetMD5Code ());
bw. write (md5. GetMD5Code ().toString ());
bw. write ("\r\n");
}
bw. close ();
writer. close ();
FileInputStream fis = new FileInputStream(file_int); System. out . print (fis . read ());
fos. close ();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public int compare(String string, String keys) {
int flag =0;
try {
FileInputStream fis = new FileInputStream(file_int); int a = fis. read ();
FileReader fileReader = new FileReader(file_md5);
BufferedReader bReader = new BufferedReader(fileReader ); //从文件读出来MD5值
String string2 = (String ) readLine (a , bReader);
if (string2. equals (keys )){
flag = 1;
- 20 -
}
FileOutputStream fos = new
FileOutputStream
(file_int); fos. write (a +1);
} catch (Exception e ) {
// TODO Auto-generated catch block
e. printStackTrace ();
}
return flag;
}
private String readLine(int lineNumber,
BufferedReader reader) throws Exception{
String line="" ;
int i=0;
while (i
line=reader . readLine ();
i++;
}
return line;
}
}
3. 运行结果
(初始化)
(认证登录)
- 21 -
(验证成功)
(如果再使用第一次的密钥或者其他密钥登陆)
(验证失败)
[问题讨论]
1. 分析SKEY 的安全性
答:1.每个数只使用一次,对数据库攻击用处不大
2.可防止重放攻击
2. 分析实验中在编辑编译运行等各环节中所出现的问题及解决方法
答:刚开始的时候对skey 协议的原理不太熟悉,导致在实现的初期中觉得
无从下手,后来通过查阅资料完全理解了skey 协议的流程以后,又在文件读写方面遇到了一些问题。包括后面MD5数字摘要算法的应用。通过这个实验,不只是学到了很多密码学方面是知识,更是对我个人编程
能力的一个提高。
- 22 -
中南大学
现 代 密 码 学 实 验 报 告
学生姓名 张振宇 学 号 0906130221 专业班级 信息安全1302班 指导教师 段桂华 学 院 信息科学与工程学院 完成时间 2015年5月
目 录
实验一 密码算法实验 ............................................... - 2 -
[实验目的] . ................................................................................................ - 2 -
[实验预备] . ................................................................................................ - 2 -
[实验内容] . ................................................................................................ - 2 -
[实验步骤] . ................................................................................................ - 2 -
1. AES 算法 .......................................................................................................... - 3 - 2. RC4算法 ........................................................................................................ - 11 -
[问题讨论] . .............................................................................................. - 15 - 实验二 SKEY 协议设计实验 ...................................... - 16 -
[实验目的] . .............................................................................................. - 16 -
[实验预备] . .............................................................................................. - 16 -
[实验内容] . .............................................................................................. - 16 -
[实验步骤] . .............................................................................................. - 16 -
1. 协议分析 ........................................................................................................ - 17 -
2. 协议实现 ........................................................................................................ - 17 -
3. 运行结果 ........................................................................................................ - 21 -
[问题讨论] . .............................................................................................. - 22 -
实验一 密码算法实验
[实验目的]
1.掌握密码学中经典的对称密码算法AES、RC4的算法原理。
2.掌握AES、RC4的算法流程和实现方法。
[实验预备]
1.AES 算法的基本原理和特点。
2.流密码RC4的密钥流生成以及S 盒初始化过程。
[实验内容]
1. 分析AES、RC4的实现过程。2. 用程序设计语言将算法过程编程实现。
3. 完成字符串数据的加密运算和解密运算
输入十六进制明文:[***********]00AABBCCDDEEFF
输入十六进制密钥:13579BDF02468ACE1234567890ABCDEF
[实验步骤]
1. 预习AES、RC4算法。
2. 写出算法流程,用程序设计语言将算法过程编程实现。
3. 输入指定的明文、密钥进行实验,验证结果。
4. 自己选择不同的输入,记录输出结果。
1.
AES 算法
算法分析
Rijndael (State , ExpandedKey)
{
AddRoundKey (State , ExpandedKey);
for (i =1; i
Round (State , ExpandedKey+Nb * i);
FinalRound (State , ExpandedKey+Nb *Nr )
}
Round (State , RoundKey)
{
ByteSub (State );
ShiftRow (State );
MixColumn (State );
AddRoundKey (State , RoundKey)
}
FinalRound (State , RoundKey)
{
ByteSub (State );
ShiftRow (State );
AddRoundKey (State , RoundKey)
}
编程实现
Subbyte (字节替换)
非线性的字节替代,单独处理每个字节:
求该字节在有限域GF(28) 上的乘法逆,"0" 被映射为自身,
即对于α∈GF(28) ,求β∈GF(28) ,使得α·β=β·α=1mod(x8+x4+x2+x+1)。 对上一步求得的乘法逆作仿射变换
yi=xi + x(i+4)mod8 + x(i+6)mod8 + x(i+7)mod8 + ci
为了数据处理的方便,我在这里使用的是置换表的方法。
public
byte [][] subbyte(byte [][] sub, int r){
byte row, col ;
byte [][] temp=new byte [4][4];
for (int i=0; i
for (int j=0; j
col=(byte )(sub [i ][j ]&0xf );
row=(byte )((sub [i ][j ]>>4)&0xf );
temp[i ][j ]=rsbox [row ][col ];
}
return temp;
}
ShiftRows (行移位变换)
行移位变换完成基于行的循环位移操作,变换方法:
即行移位变换作用于行上,第0行不变,第1行循环左移1个字节,第2行循环左移2个字节,第3行循环左移3个字节。
public byte [][] shift(byte [][]
sub
, int mode){
byte temp;
temp=sub [3][0];
sub[3][0]=sub [3][1];sub [3][1]=sub [3][2];
sub[3][2]=sub [3][3];sub [3][3]=temp ;
temp=sub [2][0];
sub[2][0]=sub [2][2];sub [2][2]=temp ;
temp=sub [2][1];
sub[2][1]=sub [2][3];sub [2][3]=temp ;
temp=sub [1][0];
sub[1][0]=sub [1][3];sub [1][3]=sub [1][2];
sub[1][2]=sub [1][1];sub [1][1]=temp ;
return sub;
}
MixColumns (列混合变换)
逐列混合,方法:
b(x) = (03·x3 + 01·x2 + 01·x + 02) · a(x) mod(x4 + 1)
矩阵表示形式:
public byte [][] mix(byte [][] sub, int mode){
byte count=0;
byte [][] temp=new byte [4][4];
for (int i=0; i
for (int j=0; j
while (count
temp[i ][j ]=(byte )(
temp [i ][j ]^mu (rmut [i ][count ],
sub [count ][j ]));
count++;
}
count=0;
}
}
return temp;
}
public byte
mu(byte b, byte c){
byte ret=0 , count1=0, count2=0;
byte [] barray=new byte [8];
byte [] carray=new byte [8];
byte [] pro=new byte [15];
if (b ==1|c ==0) return c;
if (c ==1) return b;
for (int i=0; i
barray[i ]=(byte )(b &1);
b=(byte )(b >>1);
count1++;
}
for (int i=0; i
carray[i ]=(byte )(c &1);
c=(byte )(c >>1);
count2++;
}
for (int i=0; i
for (int j=0; j
if (barray [i ]>0&carray [j ]>0)
pro[i +j ]=(byte )((pro [i +j ]+1)%2);
}
for (int m=0; m
if (pro [m ]>0)
ret=(byte )((by [m ])^(ret ));
}
return ret;
}
AddRoundKey (轮密钥加密)
简单来说就是逐字节相加,有限域GF(2
8) 上的加法是模2加法,即异或。
public byte [][] add(byte sub[][],byte [][] roundkey){
for (int i=0; i
for (int j=0; j
sub[i ][j ]=(byte )(
sub[i ][j ]^roundkey [i ][j ]);
}
return sub;
}
KeyExpansion (密钥扩展)
将输入的密钥扩展为11组128位密钥组,其中第0组为输入密钥本身 其后第n 组第i 列 为 第n-1组第i 列 与 第n 组第i-1列之和
对于每一组第一列有特殊的处理
将前一列即第n-1组第3列的4个字节循环左移1个字节,并对每个字节进行字节替代变换SubBytes 将第一行与轮常量相加,最后再与前一组该列相加
public byte [][][] key(byte [][] okey){
byte [][][] retarray=new byte [11][4
][4];
for (int i=0; i
for (int j=0; j
retarray[0][j ][i ]=okey [i ][j ];
}
for (int i=1; i
retarray[i ]=tkey (retarray [i -1],r [i ]);
}
return retarray;
}
public byte [][] tkey(byte [][] okey, int ri){
byte [][] temp=new byte [4][4];
byte col, row ;
col=(byte )(okey [1][3]&0xf );
row=(byte )((okey [1][3]>>4)&0xf );
temp[0][0]=(byte )(ri ^sbox [row ][col ]^okey [0][0]);
col=(byte )(okey [2][3]&0xf );
row=(byte )((okey [2][3]>>4)&0xf );
temp[1][0]=(byte )(sbox [row ][col ]^okey [1][0]);
col=(byte )(okey [3][3]&0xf );
row=(byte )((okey [3][3]>>4)&0xf );
temp[2][0]=(byte )(sbox [row ][col ]^okey [2][0]);
col=(byte )(okey [0][3]&0xf );
row=(byte )((okey [0][3]>>4)&0xf );
temp[3][0]=(byte )(sbox [row ][col ]^okey [3][0]);
for (int i=1; i
temp[0][i ]=(byte )(temp [0][i -1]^okey [0][i ]);
temp[1][i ]=(byte )(temp [1][i -1]^okey [1][i ]);
temp[2][i ]=(byte )(temp [2][i -1]^okey [2][i ]);
temp[3][i ]=(byte )(temp [3][i -1]^okey [3][i ]);
}
return temp;
}
数据加密: case 1:
try {
Encrypt en=new Encrypt();
System. out . println (" 请输入明文" );
content=in . readLine ();
System. out . println (" 请输入密钥" );
key=in . readLine ();
out=en . textEncrypt (content , key);
System. out . println (out );
while (true ){
System. out . println ("coyp 一下结果? y/n"); ch=in . readLine ();
if (ch . length ()==1){
if (ch . charAt (0)=='y' |
ch . charAt (0)=='Y' )
{save =out ; break ;}
if (ch . charAt (0)=='n' |
ch . charAt (0)=='N' )
break ;
}
}
}catch (IOException e){
e. printStackTrace ();}
break ;
数据解密:
case 2:
try {
if (save !="" &save !=null ){
while (true ){
System . out . println (" 用一下刚刚copy 的结果? y/n" ch =in . readLine ();
if (ch . length ()==1){
if (ch . charAt (0)=='y' |
ch . charAt (0)=='Y' )
{content =save ; break ;}
);
if (ch . charAt
(
)=='n' |
ch . charAt (0)=='N' )
break ;
}
}
}
else {
System. out . println (" 请输入密文" );
content=in . readLine ();
}
System. out . println (" 请输入密钥" );
key=in . readLine ();
Decrypt de=new Decrypt();
out=de . textDecrypt (content , key);
System. out . print (out );
}catch (IOException e){
e. printStackTrace ();System . out . println (" 未知错误);} break ;
运行结果
2. RC4算法
算法分析
RC4算法的原理包括初始化算法和伪随机子密码生成算法两大部分。
假设S-box 长度和密钥长度均为n 。
算法的初始化部分 for (i =0; i
{
s[i ]=i ;
}
j =0;
for (i =0; i
{
j=(j +s [i ]+k [i ])%n ;
swap(s [i ], s
[j ]);
}
在初始化的过程中,密钥的主要功能是将S-box 搅乱,i 确保S-box 的每个元素都得到处理,j 保证S-box 的搅乱是随机的。而不同的S-box 在经过伪随机子密码生成算法的处理后可以得到不同的子密钥序列。
i =j =0;
while (明文未结束)
{
++i %=n ;
j=(j +s )%n ;
swap(s , s[j ]);
sub_k=s ((s +s [j ])%n );
}
得到的子密码sub_k用以和明文进行异或运算,得到密文,解密过程也完全相同。
编程实现 package RC4;
import java.util.Scanner;
public class RC4 {
public static String decry_RC4(byte [] data, String key) {
if (data == null || key == null ) {
return null ;
}
return asString(RC4Base (data , key));
}
public static String decry_RC4(String data, String key) {
if (data == null || key == null ) {
return null ;
}
return new String(RC4Base (HexString2Bytes (data ), key));
}
public static byte [] encry_RC4_byte(String data, String key) { if (data == null || key == null ) {
return null ;
}
byte b_data[] = data. getBytes ();
return RC4Base(b_data, key);
}
public static String encry_RC4_string(String data, String key) { if (data == null || key == null ) {
return null ;
}
return toHexString(asString (encry_RC4_byte(data , key))); }
private static String asString(byte [] buf) {
StringBuffer strbuf = new StringBuffer(buf . length );
for (int i = 0; i
strbuf. append ((char ) buf[i ]);
}
return strbuf. toString ();
}
private static byte [] initKey(String aKey) {
byte [] b_key = aKey. getBytes ();
byte state[] = new byte [256];
for (int i = 0; i
state[i ] = (byte ) i;
}
int index1 = 0;
int index2 = 0;
if (b_key == null || b_key. length == 0) {
return null ;
}
for (int i = 0; i
index2 = ((b_key[index1] & 0xff )
+ (state [i ] & 0xff ) + index2) & 0xff ;
byte tmp = state[i ];
state[i ] = state[index2];
state[index2] = tmp;
index1 = (index1 + 1) % b_key. length ;
}
return state;
}
private static String toHexString(String s) {
String str = "" ;
for (int i = 0; i
int ch = (int ) s. charAt (i );
String s4 = Integer. toHexString (ch & 0xFF );
if (s4. length () == 1) {
s4 = '0' + s4;
}
str = str + s4;
}
return str; // 0x表示十六进制
}
private static byte [] HexString2Bytes(String src) {
int size = src. length ();
byte [] ret = new byte [size / 2];
byte [] tmp = src. getBytes ();
for (int i = 0; i
ret[i ] = uniteBytes(tmp [i * 2], tmp[i * 2 + 1]);
}
return ret;
}
private static byte uniteBytes(byte src0, byte src1) {
char _b0 = (char ) Byte. decode (
"0x" + new String(new byte [] { src0 })).byteValue ();
_b0 = (char ) (_b0
char _b1 = (char ) Byte. decode (
"0x" + new String(new byte [] { src1 })).byteValue ();
byte ret = (byte ) (_b0 ^ _b1);
return ret;
}
private static byte [] RC4Base(byte [] input, String mKkey) { int x = 0, y = 0;
byte key[] = initKey(mKkey );
int xorIndex;
byte [] result = new byte [input . length ];
for (int i = 0; i
x = (x + 1) & 0xff ;
y = ((key [x ] & 0xff ) + y) & 0xff ;
byte tmp = key[x ];
key[x ] = key[y ];
key[y ] = tmp;
xorIndex = ((key [x ] & 0xff ) + (key [y ] & 0xff )) & 0xff ; result[i ] = (byte ) (input [i ] ^ key[xorIndex ]);
}
return result;
}
public static void main(String [] args) {
System. out . println (" 请输入明文:" );
Scanner in=new Scanner(System . in );
String inputStr, keyStr;
inputStr=in . nextLine ();
System. out . println (" 请输入密钥:" );
keyStr = in. nextLine ();
String str = encry_RC4_string(inputStr , keyStr);
System. out . println (" 加密后的密文:" +str );
System. out . print (" 解密后的明文:" );
System. out . println (decry_RC4(str , keyStr));
}
}
运行结果
测试数据1
测试数据2
[问题讨论]
1. 改变明文或密钥中的一个比特值可能影响AES 值中的多少比特?
答:明文和密钥在S 盒置换时,不同的字节会替换出不同的结果。算法过程
中一共进行了10轮加密,所以改变一个比特值可能影响AES 值中的80
比特(8 x 10)。
2. 在RC4的密钥流生成中,改变初始密钥的一个比特值可能影响输
出中的多少比特?
答:初始密钥的不同会导致S 盒的不同,所以可能影响输出中的256比特。
3. 分析实验中在编辑编译运行等各环节中所出现的问题及解决方法。
答:在AES 算法实现过程中,在对输入数据的处理上遇到了很多的问题,刚
开始的时候习惯性地使用string 的形式保存S 盒等数据,但是由于AES
算法用到了很多的异或和移位操作,使用string 和char 形式数据的弊
端就慢慢地暴露了出来。然后在网上查阅了很多资料,最后选择了使用
byte 类型的数据,在整个算法的实现过程中,进一步加深了我对AES 加
密算法和RC4算法的理解。
实验二 SKEY 协议设计实验
[实验目的]
1.掌握身份认证协议的原理和基本思想。
2.掌握SKEY 协议的机制和实现方法。
[实验预备]
1.SKEY 协议的作用。
2.SKEY 协议的安全性分析。
3.SKEY 协议的实现过程。
[实验内容]
1. 分析SKEY 协议的实现过程。
2. 用程序设计语言将算法过程编程实现。
3. 演示SKEY 协议的身份鉴别过程。
[实验步骤]
1. 预习SKEY 协议的机制。
2. 选择和实现相应的摘要算法MD5或SHA。
3. 写出算法流程,用程序设计语言将协议过程编程实现。
4. 验证SKEY 协议的身份鉴别过程。
1. 协议分析
2. 协议实现
为了便于实现和演示,我采用了一个比较简单的方式进行。要模拟客户端保存数据比较不容易实现,对服务器生成的hash 值,保存在本地文件中, 这个hash 的值是动态变化的,每次hash 都会加上相应的登陆次数(这个hash 方式和一般的skey 协议有些不同),
这样可以动态的获取。通过文件读写的方式获得。对于
生成的
hash
值从100到0次访问的问题也是使用一个文件来储存,每次验证完成后,都会把前一次存的数据减1然后重新的写入文件中。
本次实验使用到了Java 中的文件读写,这是用到的两个辅助文件:
其中int.txt 存储登陆后修改的数据
(写进去的格式不是10进值的,所以在这里不能看出明文)
md5.txt 为存储hash 值的文件:
(共100条密钥)
MD5实现文件: package SKEY;
import
java. security . MessageDigest ;
import java. security . NoSuchAlgorithmException ;
public class MD5String {
private String string ;
// 全局数组
private final static String[] strDigits = {
"0" , "1" , "2" , "3" , "4" , "5" , "6" , "7" ,
public MD5String(String string ) {
this . string =string ;
}
private String byteToArrayString(byte bByte) {
int iRet = bByte;
if (iRet
iRet += 256; "8" , "9" , "A" , "B" , "C" , "D" , "E" , "F" };
}
int iD1 = iRet / 16;
int iD2 = iRet % 16;
return strDigits[iD1] + strDigits[iD2];
}
// 转换字节数组为16进制字串
private String byteToString(byte [] bByte) {
StringBuffer sBuffer = new StringBuffer();
for (int i = 0; i
sBuffer. append (byteToArrayString (bByte [i ])); }
return sBuffer. toString ();
}
public String GetMD5Code() {
String resultString = null ;
try {
resultString = new String(string );
MessageDigest. getInstance ("MD5" );
MessageDigest md = MessageDigest. getInstance ("MD5" ); //该函数返回值为存放返回结果的byte 数组
md. digest (string . getBytes ());
byte [] bytes = md. digest (string . getBytes ()); resultString = byteToString(bytes );
} catch (NoSuchAlgorithmException ex) {
}
return resultString;
}
}
Skey 协议实现文件:
package SKEY;
import java. io . BufferedReader ;
import java. io . BufferedWriter ;
import java. io . File ;
import java. io . FileInputStream ;
import java. io . FileOutputStream ;
import java. io . FileReader ;
import java. io . FileWriter ;
import java. io . IOException ;
public class Skey {
String intsString =
"C://Users//zzy//workspace//Cryptology//int.txt";
String md5sString =
"C://Users//zzy//workspace//Cryptology//md5.txt";
File file_int = new File(intsString );
File file_md5 = new File(md5sString );
//初始化
public void GUI(String string) {
try {
FileOutputStream fos = new FileOutputStream(file_int); FileWriter writer = new FileWriter(file_md5);
BufferedWriter bw = new BufferedWriter(writer );
fos. write (1);
for (int a=1; a
MD5String md5 = new MD5String(string +a );
System. out . println (md5. GetMD5Code ());
bw. write (md5. GetMD5Code ().toString ());
bw. write ("\r\n");
}
bw. close ();
writer. close ();
FileInputStream fis = new FileInputStream(file_int); System. out . print (fis . read ());
fos. close ();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public int compare(String string, String keys) {
int flag =0;
try {
FileInputStream fis = new FileInputStream(file_int); int a = fis. read ();
FileReader fileReader = new FileReader(file_md5);
BufferedReader bReader = new BufferedReader(fileReader ); //从文件读出来MD5值
String string2 = (String ) readLine (a , bReader);
if (string2. equals (keys )){
flag = 1;
- 20 -
}
FileOutputStream fos = new
FileOutputStream
(file_int); fos. write (a +1);
} catch (Exception e ) {
// TODO Auto-generated catch block
e. printStackTrace ();
}
return flag;
}
private String readLine(int lineNumber,
BufferedReader reader) throws Exception{
String line="" ;
int i=0;
while (i
line=reader . readLine ();
i++;
}
return line;
}
}
3. 运行结果
(初始化)
(认证登录)
- 21 -
(验证成功)
(如果再使用第一次的密钥或者其他密钥登陆)
(验证失败)
[问题讨论]
1. 分析SKEY 的安全性
答:1.每个数只使用一次,对数据库攻击用处不大
2.可防止重放攻击
2. 分析实验中在编辑编译运行等各环节中所出现的问题及解决方法
答:刚开始的时候对skey 协议的原理不太熟悉,导致在实现的初期中觉得
无从下手,后来通过查阅资料完全理解了skey 协议的流程以后,又在文件读写方面遇到了一些问题。包括后面MD5数字摘要算法的应用。通过这个实验,不只是学到了很多密码学方面是知识,更是对我个人编程
能力的一个提高。
- 22 -