信息安全实验报告(终)

更新时间:2023-04-14 20:36:01 阅读量: 实用文档 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

院系:计算机科学学院专业:信息与计算科学

年级: 11级

课程名称:信息安全

班级:信息3班

姓名:胡志桥

学号: 11151196

指导教师:孟博

2013年月日

年级 11级

班号信息3班学号11151196

专业信息与计算科学姓名胡志桥

实验名称 Engima密码算法

实验

类型

设计型综合型创新型

实验

目的或要求通过使用Engima密码模拟器,加深对古典密码体制的了解,为深入学习现代密码学奠定基础。

实验原理(算法流程)

ENIGMA看起来是一个装满了复杂而精致的元件的盒子。不过要是我们把它打开来,就可以看到它可以被分解成相当简单的几部分。下面的图是它的最基本部分的示意图,我们可以看见它的三个部分:键盘、转子和显示器。

在上面ENIGMA的照片上,我们看见水平面板的下面部分就是键盘,一共有26个键,键盘排列接近我们现在使用的计算机键盘。为了使消息尽量地短和更难以破译,空格和标点符号都被省略。在示意图中我们只画了六个键。实物照片中,键盘上方就是显示器,它由标示了同样字母的26个小灯组成,当键盘上的某个键被按下时,和此字母被加密后的密文相对应的小灯就在显示器上亮起来。同样地,在示意图上我们只画了六个小灯。在显示器的上方是三个转子,它们的主要部分隐藏在面板之下,在示意图中我们暂时只画了一个转子。

键盘、转子和显示器由电线相连,转子本身也集成了6条线路(在实物中是26条),把键盘的信号对应到显示器不同的小灯上去。在示意图中我们可以看到,如果按下a 键,那么灯B就会亮,这意味着a被加密成了B。同样地我们看到,b被加密成了A,c被加密成了D,d被加密成了F,e被加密成了E,f被加密成了C。于是如果我们在键盘上依次键入cafe(咖啡),显示器上就会依次显示DBCE。这是最简单的加密方法之一,把每一个字母都按一一对应的方法替换为另一个字母,这样的加密方式叫做“简单替换密码”。

简单替换密码在历史上很早就出现了。著名的“凯撒法”就是一种简单替换法,它把每个字母和它在字母表中后若干个位置中的那个字母相对应。比如说我们取后三个位置,那么字母的一一对应就如下表所示:

明码字母表:abcdefghijklmnopqrstuvwxyz

密码字母表:DEFGHIJKLMNOPQRSTUVWXYZABC

于是我们就可以从明文得到密文:(veni, vidi, vici,“我来,我见,我征服”是儒勒·凯撒征服本都王法那西斯后向罗马元老院宣告的名言)

明文:veni, vidi, vici

密文:YHAL, YLGL, YLFL

很明显,这种简单的方法只有26种可能性,不足以实际应用。一般上是规定一个比较随意的一一对应,比如

明码字母表:abcdefghijklmnopqrstuvwxyz

密码字母表:JQKLZNDOWECPAHRBSMYITUGVXF

甚至可以自己定义一个密码字母图形而不采用拉丁字母。但是用这种方法所得到的密文还是相当容易被破解的。至迟在公元九世纪,阿拉伯的密码破译专家就已经娴熟地掌握了用统计字母出现频率的方法来击破简单替换密码。破解的原理很简单:在每种拼音文字语言中,每个字母出现的频率并不相同,比如说在英语中,e出现的次数就要大大高于其他字母。所以如果取得了足够多的密文,通过统计每个字母出现的频率,我们就可以猜出密码中的一个字母对应于明码中哪个字母(当然还要通过揣摩上下文等基本密码破译手段)。柯南·道尔在他著名的福尔摩斯探案集中《跳舞的人》里详细叙述了福尔摩斯使用频率统计法破译跳舞人形密码的过程。

所以如果转子的作用仅仅是把一个字母换成另一个字母,那就没有太大的意思了。但是大家可能已经猜出来了,所谓的“转子”,它会转动!这就是谢尔比乌斯关于ENIGMA 的最重要的设计——当键盘上一个键被按下时,相应的密文在显示器上显示,然后转子的方向就自动地转动一个字母的位置(在示意图中就是转动1/6圈,而在实际中转动1/26圈)。下面的示意图表示了连续键入3个b的情况:

当第一次键入b时,信号通过转子中的连线,灯A亮起来,放开键后,转子转动一格,各字母所对应的密码就改变了;第二次键入b时,它所对应的字母就变成了C;同样地,第三次键入b时,灯E闪亮。

这不是一种简单替换密码。同一个字母b在明文的不同位置时,可以被不同的字母替换,而密文中不同位置的同一个字母,可以代表明文中的不同字母,频率分析法在这里就没有用武之地了。这种加密方式被称为“复式替换密码”。

但是我们看到,如果连续键入6个字母(实物中26个字母),转子就会整整转一圈,回到原始的方向上,这时编码就和最初重复了。而在加密过程中,重复的现象是很危险的,这可以使试图破译密码的人看见规律性的东西。于是谢尔比乌斯在机器上又加了一个转子。当第一个转子转动整整一圈以后,它上面有一个齿拨动第二个转子,使得它的方向转动一个字母的位置。看下面的示意图(为了简单起见,现在我们将它表示为平面形式):

这里(a)图中我们假设第一个转子(左边的那个)已经整整转了一圈,按b键时显示器上D灯亮;当放开b键时第一个转子上的齿也带动第二个转子同时转动一格,于是(b)图中第二次键入b时,加密的字母为F;而再次放开键b时,就只有第一个转子转动了,于是(c)图中第三次键入b时,与b相对应的就是字母B。

(写不完时,可另加附页。)

加密

成绩

评定教师签名:

年月日

备注:源代码附后,源代码要求有注释说明

年级11级

班号信息3班学号11151196

专业信息与计算科学姓名胡志桥

实验名称基于RSA的数字签名实现

实验

类型

设计型综合型创新型

实验

目的或要求

掌握RSA算法的基本原理,通过用RSA算法对实际数据进行加密和解密来深刻了解RSA的运行原理。对数字签名有所了解,理解和掌握MD5算法,以及如何利用RSA和MD5算法来实现数字签名。

实验原理(算法流程)

公钥密码体制中,解密和加密密钥不同,解密和加密可分离,通信双方无须事先交换密钥就可建立起保密通信,较好地解决了传统密码体制在网络通信中出现的问题.另外,随着电子商务的发展,网络上资金的电子交换日益频繁,如何防止信息的伪造和欺骗也成为非常重要的问题.数字签名可以起到身份认证,核准数据完整性的作用.目前关于数字签名的研究主要集中基于公钥密码体制的数字签名.

公钥密码体制的特点是:为每个用户产生一对密钥(PK和SK);PK公开,SK保密;从PK推出SK是很困难的;A,B双方通信时,A通过任何途径取得B的公钥,用B的公钥加密信息.加密后的信息可通过任何不安全信道发送.B收到密文信息后,用自己私钥解密恢复出明文.

.RSA公钥密码体制到目前为止还是一种认可为安全的体制. RSA算法和用RSA算法实现数字签名的理论,以及它们在实际应用中的实现.

1、RSA算法

RSA算法是一种非对称密码算法,所谓非对称,就是指该算法需要一对密钥,使用其中一个加密,则需要用另一个才能解密。

RSA的算法涉及三个参数,n、e1、e2。

其中,n是两个大质数p、q的积,n的二进制表示时所占用的位数,就是所谓的密钥长度。

e1和e2是一对相关的值,e1可以任意取,但要求e1与(p-1)*(q-1)互质;再选择e2,要求(e2*e1)mod((p-1)*(q-1))=1。

(n及e1),(n及e2)就是密钥对。

RSA加解密的算法完全相同,设A为明文,B为密文,则:A=B^e1 mod n;B=A^e2 mod n;

e1和e2可以互换使用,即:

A=B^e2 mod n;B=A^e1 mod n;

2、RSA数字签名算法的理论描述

RSA数字签名算法的过程为:A对明文m用解密变换作: s Dk (m)=md mod n,其中d,n为A的私人密钥,只有A才知道它;B收到A的签名后,用A的公钥和加密变换得到明文,因: Ek(s)= Ek(Dk (m))= (md)e mod n,又de1 mod (n)即de=l(n)+1,根据欧拉定理m(n)=1 mod n,所以Ek(s)=ml(n)+1=[m(n)]em=m mod n.若明文m和签名s一起送给用户B,B可以确信信息确实是A发送的.同时A也不能否认送给这个信息,因为除了A本人外,其他任何人都无法由明文m产生s.因此RSA数字签名方案是可行的.

但是RSA数字签名算法存在着因计算方法本身同构造成签名易被伪造和计算时间长的弱点,因此实际对文件签名前,需要对消息做MD5变换.MD5函数是一种单向散列函数,它将任意长度的消息压缩成128位的消息摘要.应用MD5的单向性(即给定散列值,计算消息很难)和抗碰撞性(即给定消息M,要找到另一消息M' 并满足两者的散列值很难),可以实现信息的完整性检验.另外该函数的设计不基于任何假设和密码体制而直接构造,执行的速度快,是一种被广泛认可的单向散列算法.

3、MD5算法的实现

算法描述:对MD5算法简要的叙述可以为:MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。

在MD5算法中,首先需要对信息进行填充,使其字节长度对512求余的结果等于448。因此,信息的字节长度(Bits Length)将被扩展至N*512+448,即N*64+56个字节(Bytes),N为一个正整数。填充的方法如下,在信息的后面填充一个1和无数个0,直到满足上面的条件时才停止用0对信息的填充。然后,在在这个结果后面附加一个以64位二进制表示的填充前信息长度。经过这两步的处理,现在的信息字节长度=N*512+448+64=(N+1)*512,即长度恰好是512的整数倍。这样做的原因是为满足后面处理中对信息长度的要求。

MD5中有四个32位被称作链接变量(Chaining Variable)的整数参数,他们分别为:A=0x01234567,B=0x89abcdef,C=0xfedcba98,D=0x76543210。

当设置好这四个链接变量后,就开始进入算法的四轮循环运算。循环的次数是信息中512位信息分组的数目。

将上面四个链接变量复制到另外四个变量中:A到a,B到b,C到c,D到d。

主循环有四轮(MD4只有三轮),每轮循环都很相似。第一轮进行16次操作。每次操作对a、b、c和d中的其中三个作一次非线性函数运算,然后将所得结果加上第四个变量,文本的一个子分组和一个常数。再将所得结果向右环移一个不定的数,并加上a、b、c或d中之一。最后用该结果取代a、b、c或d中之一。

MD5的安全性

MD5相对MD4所作的改进:

1. 增加了第四轮;

2. 每一步均有唯一的加法常数;

3. 为减弱第二轮中函数G的对称性从(X&Y)|(X&Z)|(Y&Z)变为

(X&Z)|(Y&(~Z));

4. 第一步加上了上一步的结果,这将引起更快的雪崩效应;

5. 改变了第二轮和第三轮中访问消息子分组的次序,使其更不相似;

6. 近似优化了每一轮中的循环左移位移量以实现更快的雪崩效应。各轮的位移量互不相同。

组内分工(可选)

实验结果分析RSA加密解密MD5加密

教师签名:

年月日备注:源代码附后,源代码要求有注释说明

//RSA加密解密

package chp16;

import java.security.*;

import java.security.interfaces.*;

import java.math.*;

import java.io.*;

public class Password_Test {

//用FileInputStream获取公钥

//用RSAPublicKey类中的方法获取公钥的参数(e,n)

//用“BigInteger m = new BigInteger(ptext);”来获得明文整数

//执行计算

public static void main(String[] args) {

try {

new Password_Test();

Encryption_RSA();

} catch (Exception e) {

e.printStackTrace();

}

}

public Password_Test() throws Exception {// 构造方法创建公钥和私钥

KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");//生成实现RSA算法的KeyPairGenerator对象。

kpg.initialize(1024);// 初始化确定密钥的大小

KeyPair kp = kpg.genKeyPair();// 生成密钥对

PublicKey pbkey = kp.getPublic();// 创建公钥

PrivateKey prkey = kp.getPrivate();// 创建私钥

// 保存公钥

FileOutputStream file1 = new FileOutputStream("Skey_RSA_pub.dat");

ObjectOutputStream ob1 = new ObjectOutputStream(file1);//创建ObjectOutputStream 对象

ob1.writeObject(pbkey); //将指定的对象写入ObjectOutputStream。

// 保存私钥

FileOutputStream file2 = new FileOutputStream("Skey_RSA_priv.dat");

ObjectOutputStream ob2 = new ObjectOutputStream(file2);

ob2.writeObject(prkey);

}

public static void Encryption_RSA() throws Exception {

System.out.println("根据公钥生成密文:"+"\n");

String string = "I am a student";

// 获取公钥及参数e,n

FileInputStream f_in = new FileInputStream("Skey_RSA_pub.dat");

ObjectInputStream o_in = new ObjectInputStream(f_in);

RSAPublicKey pbk = (RSAPublicKey) o_in.readObject();

BigInteger e = pbk.getPublicExponent();//返回此公钥的指数

BigInteger n = pbk.getModulus();//返回此公钥的模

System.out.println("公钥的指数e= " + e);

System.out.println("公钥的模n= " + n);

// 明文bit

byte bt[] = string.getBytes("UTF8");

BigInteger bit = new BigInteger(bt);

// 计算密文c,打印

BigInteger mi = bit.modPow(e, n);//生成密文

System.out.println("生成密文为:" + mi+"\n\n");//打印密文

// 保存密文

String save = mi.toString();

BufferedWriter out = new BufferedWriter(new OutputStreamWriter(

new FileOutputStream("Enc_RSA.dat")));

out.write(save, 0, save.length());

out.close();

Decrypt_RSA();

}

public static void Decrypt_RSA() throws Exception {

System.out.println("根据私钥破解密文:"+"\n");

// 读取密文

BufferedReader in = new BufferedReader(new InputStreamReader(

new FileInputStream("Enc_RSA.dat")));

String ctext = in.readLine();

BigInteger mi = new BigInteger(ctext);

// 读取私钥

FileInputStream f = new FileInputStream("Skey_RSA_priv.dat");

ObjectInputStream b = new ObjectInputStream(f);

RSAPrivateKey prk = (RSAPrivateKey) b.readObject();

BigInteger d = prk.getPrivateExponent();//返回此私钥的指数

BigInteger n = prk.getModulus();//返回此私钥的模

System.out.println("私钥的指数d= " + d);

System.out.println("私钥的模n= " + n);

BigInteger jie = mi.modPow(d, n);//进行解密操作

System.out.println("m= " + jie); // 显示解密结果

byte[] mt = jie.toByteArray();

System.out.println("解密后的文本内容为:");

for (int i = 0; i < mt.length; i++) {

System.out.print((char) mt[i]);

}

}

}

MD5加密

package md5;

import 7b04c30c3169a4517723a386ng.reflect.*;

/**

* MD5 算法的Java Bean MD5 类实现了RSA Data Security, Inc.在提交给IETF 的RFC1321中的MD5

* message-digest 算法。

*/

public class MD5 {

// 下面这些S11-S44实际上是一个4*4的矩阵,在原始的C实现中是用#define 实现的,这里把它们实现成为static

// final是表示了只读,切能在同一个进程空间内的多个Instance间共享

static final int S11 = 7;

static final int S12 = 12;

static final int S13 = 17;

static final int S14 = 22;

static final int S21 = 5;

static final int S22 = 9;

static final int S23 = 14;

static final int S24 = 20;

static final int S31 = 4;

static final int S32 = 11;

static final int S33 = 16;

static final int S34 = 23;

static final int S41 = 6;

static final int S42 = 10;

static final int S43 = 15;

static final int S44 = 21;

static final byte[] PADDING = { -128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0,

0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 0, 0, 0 };

// 下面的三个成员是MD5计算过程中用到的3个核心数据,在原始的C实现中被定义到

MD5_CTX结构中

private long[] state = new long[4]; // state (ABCD)

private long[] count = new long[2]; // number of bits, modulo 2^64 (lsb

// first)

private byte[] buffer = new byte[64]; // input buffer

// digestHexStr是MD5的唯一一个公共成员,是最新一次计算结果的16进制ASCII表示.

public String digestHexStr;

// digest,是最新一次计算结果的2进制内部表示,表示128bit的MD5值.

private byte[] digest = new byte[16];

// getMD5ofStr是类MD5最主要的公共方法,入口参数是你想要进行MD5变换的字符串返回的是变换完的结果,这个结果是从公共成员digestHexStr取得的.

public String getMD5ofStr(String inbuf) {

md5Init();

md5Update(inbuf.getBytes(), inbuf.length());

md5Final();

digestHexStr = "";

for (int i = 0; i < 16; i++) {

digestHexStr += byteHEX(digest[i]);

}

return digestHexStr;

}

// 这是MD5这个类的标准构造函数,JavaBean要求有一个public的并且没有参数的构造函数

public MD5() {

md5Init();

return;

}

// md5Init是一个初始化函数,初始化核心变量,装入标准的幻数

private void md5Init() {

count[0] = 0L;

count[1] = 0L;

// Load magic initialization constants.

state[0] = 0x67452301L;

state[1] = 0xefcdab89L;

state[2] = 0x98badcfeL;

state[3] = 0x10325476L;

return;

}

/*

* F, G, H ,I是4个基本的MD5函数,在原始的MD5的C实现中,由于它们是简单的位运算,可能出于效率的考虑把它们实现成了宏,在java中,

* 我们把它们实现成了private方法,名字保持了原来C中的。

*/

private long F(long x, long y, long z) {

return (x & y) | ((~x) & z);

}

private long G(long x, long y, long z) {

return (x & z) | (y & (~z));

}

private long H(long x, long y, long z) {

return x ^ y ^ z;

}

private long I(long x, long y, long z) {

return y ^ (x | (~z));

}

/*

* FF,GG,HH和II将调用F,G,H,I进行近一步变换FF, GG, HH, and II transformations for rounds

* 1, 2, 3, and 4. Rotation is separate from addition to prevent * recomputation.

*/

private long FF(long a, long b, long c, long d, long x, long s, long ac) {

a += F(b, c, d) + x + ac;

a = ((int) a << s) | ((int) a >>> (32 - s));

a += b;

return a;

}

private long GG(long a, long b, long c, long d, long x, long s, long ac) {

a += G(b, c, d) + x + ac;

a = ((int) a << s) | ((int) a >>> (32 - s));

a += b;

return a;

}

private long HH(long a, long b, long c, long d, long x, long s, long ac) {

a += H(b, c, d) + x + ac;

a = ((int) a << s) | ((int) a >>> (32 - s));

a += b;

return a;

}

private long II(long a, long b, long c, long d, long x, long s, long ac) {

a += I(b, c, d) + x + ac;

a = ((int) a << s) | ((int) a >>> (32 - s));

a += b;

return a;

}

// md5Update是MD5的主计算过程,inbuf是要变换的字节串,inputlen是长度,这个函数由getMD5ofStr调用,调用之前需要调用md5init,因此把它设计成private的private void md5Update(byte[] inbuf, int inputLen) {

int i, index, partLen;

byte[] block = new byte[64];

index = (int) (count[0] >>> 3) & 0x3F;

// Update number of bits

if ((count[0] += (inputLen << 3)) < (inputLen << 3)) {

count[1]++;

}

count[1] += (inputLen >>> 29);

partLen = 64 - index;

// Transform as many times as possible.

if (inputLen >= partLen) {

md5Memcpy(buffer, inbuf, index, 0, partLen);

md5Transform(buffer);

for (i = partLen; i + 63 < inputLen; i += 64) {

md5Memcpy(block, inbuf, 0, i, 64);

md5Transform(block);

}

index = 0;

} else {

i = 0;

}

// Buffer remaining input

md5Memcpy(buffer, inbuf, index, i, inputLen - i);

}

// md5Final整理和填写输出结果

private void md5Final() {

byte[] bits = new byte[8];

int index, padLen;

// Save number of bits

Encode(bits, count, 8);

// Pad out to 56 mod 64.

index = (int) (count[0] >>> 3) & 0x3f;

padLen = (index < 56) ? (56 - index) : (120 - index);

md5Update(PADDING, padLen);

// Append length (before padding)

md5Update(bits, 8);

// Store state in digest

Encode(digest, state, 16);

}

// md5Memcpy是一个内部使用的byte数组的块拷贝函数,从input的inpos开始把len 长度的字节拷贝到output的outpos位置开始

private void md5Memcpy(byte[] output, byte[] input, int outpos, int inpos,

int len) {

int i;

for (i = 0; i < len; i++) {

output[outpos + i] = input[inpos + i];

}

}

// md5Transform是MD5核心变换程序,有md5Update调用,block是分块的原始字节private void md5Transform(byte block[]) {

long a = state[0], b = state[1], c = state[2], d = state[3];

long[] x = new long[16];

Decode(x, block, 64);

/* Round 1 */

a = FF(a, b, c, d, x[0], S11, 0xd76aa478L); /* 1 */

d = FF(d, a, b, c, x[1], S12, 0xe8c7b756L); /* 2 */

c = FF(c, d, a, b, x[2], S13, 0x242070dbL); /* 3 */

b = FF(b, c, d, a, x[3], S14, 0xc1bdceeeL); /* 4 */

a = FF(a, b, c, d, x[4], S11, 0xf57c0fafL); /* 5 */

d = FF(d, a, b, c, x[5], S12, 0x4787c62aL); /* 6 */

c = FF(c, d, a, b, x[6], S13, 0xa8304613L); /* 7 */

b = FF(b, c, d, a, x[7], S14, 0xfd469501L); /* 8 */

a = FF(a, b, c, d, x[8], S11, 0x698098d8L); /* 9 */

d = FF(d, a, b, c, x[9], S12, 0x8b44f7afL); /* 10 */

c = FF(c, d, a, b, x[10], S13, 0xffff5bb1L); /* 11 */

b = FF(b, c, d, a, x[11], S14, 0x895cd7beL); /* 12 */

a = FF(a, b, c, d, x[12], S11, 0x6b901122L); /* 13 */

d = FF(d, a, b, c, x[13], S12, 0xfd987193L); /* 14 */

c = FF(c, d, a, b, x[14], S13, 0xa679438eL); /* 15 */

b = FF(b, c, d, a, x[15], S14, 0x49b40821L); /* 16 */

/* Round 2 */

a = GG(a, b, c, d, x[1], S21, 0xf61e2562L); /* 17 */

d = GG(d, a, b, c, x[6], S22, 0xc040b340L); /* 18 */

c = GG(c, d, a, b, x[11], S23, 0x265e5a51L); /* 19 */

b = GG(b, c, d, a, x[0], S24, 0xe9b6c7aaL); /* 20 */

a = GG(a, b, c, d, x[5], S21, 0xd62f105dL); /* 21 */

d = GG(d, a, b, c, x[10], S22, 0x2441453L); /* 22 */

c = GG(c, d, a, b, x[15], S23, 0xd8a1e681L); /* 23 */

b = GG(b, c, d, a, x[4], S24, 0xe7d3fbc8L); /* 24 */

a = GG(a, b, c, d, x[9], S21, 0x21e1cde6L); /* 25 */

d = GG(d, a, b, c, x[14], S22, 0xc33707d6L); /* 26 */

c = GG(c, d, a, b, x[3], S23, 0xf4d50d87L); /* 27 */

b = GG(b, c, d, a, x[8], S24, 0x455a14edL); /* 28 */

a = GG(a, b, c, d, x[13], S21, 0xa9e3e905L); /* 29 */

d = GG(d, a, b, c, x[2], S22, 0xfcefa3f8L); /* 30 */

c = GG(c, d, a, b, x[7], S23, 0x676f02d9L); /* 31 */

b = GG(b, c, d, a, x[12], S24, 0x8d2a4c8aL); /* 32 */

/* Round 3 */

a = HH(a, b, c, d, x[5], S31, 0xfffa3942L); /* 33 */

d = HH(d, a, b, c, x[8], S32, 0x8771f681L); /* 34 */

c = HH(c, d, a, b, x[11], S33, 0x6d9d6122L); /* 35 */

b = HH(b, c, d, a, x[14], S34, 0xfde5380cL); /* 36 */

a = HH(a, b, c, d, x[1], S31, 0xa4beea44L); /* 37 */

d = HH(d, a, b, c, x[4], S32, 0x4bdecfa9L); /* 38 */

c = HH(c, d, a, b, x[7], S33, 0xf6bb4b60L); /* 39 */

b = HH(b, c, d, a, x[10], S34, 0xbebfbc70L); /* 40 */

a = HH(a, b, c, d, x[13], S31, 0x289b7ec6L); /* 41 */

d = HH(d, a, b, c, x[0], S32, 0xeaa127faL); /* 42 */

c = HH(c, d, a, b, x[3], S33, 0xd4ef3085L); /* 43 */

b = HH(b, c, d, a, x[6], S34, 0x4881d05L); /* 44 */

a = HH(a, b, c, d, x[9], S31, 0xd9d4d039L); /* 45 */

d = HH(d, a, b, c, x[12], S32, 0xe6db99e5L); /* 46 */

c = HH(c, d, a, b, x[15], S33, 0x1fa27cf8L); /* 47 */

b = HH(b, c, d, a, x[2], S34, 0xc4ac5665L); /* 48 */

/* Round 4 */

a = II(a, b, c, d, x[0], S41, 0xf4292244L); /* 49 */

d = II(d, a, b, c, x[7], S42, 0x432aff97L); /* 50 */

c = II(c, d, a, b, x[14], S43, 0xab9423a7L); /* 51 */

b = II(b, c, d, a, x[5], S44, 0xfc93a039L); /* 52 */

a = II(a, b, c, d, x[12], S41, 0x655b59c3L); /* 53 */

d = II(d, a, b, c, x[3], S42, 0x8f0ccc92L); /* 54 */

c = II(c, d, a, b, x[10], S43, 0xffeff47dL); /* 55 */

b = II(b, c, d, a, x[1], S44, 0x85845dd1L); /* 56 */

a = II(a, b, c, d, x[8], S41, 0x6fa87e4fL); /* 57 */

d = II(d, a, b, c, x[15], S42, 0xfe2ce6e0L); /* 58 */

c = II(c, d, a, b, x[6], S43, 0xa3014314L); /* 59 */

b = II(b, c, d, a, x[13], S44, 0x4e0811a1L); /* 60 */

a = II(a, b, c, d, x[4], S41, 0xf7537e82L); /* 61 */

d = II(d, a, b, c, x[11], S42, 0xbd3af235L); /* 62 */

c = II(c, d, a, b, x[2], S43, 0x2ad7d2bbL); /* 63 */

b = II(b, c, d, a, x[9], S44, 0xeb86d391L); /* 64 */

state[0] += a;

state[1] += b;

state[2] += c;

state[3] += d;

}

// Encode把long数组按顺序拆成byte数组,因为java的long类型是64bit的,只拆低32bit,以适应原始C实现的用途

private void Encode(byte[] output, long[] input, int len) {

int i, j;

for (i = 0, j = 0; j < len; i++, j += 4) {

output[j] = (byte) (input[i] & 0xffL);

output[j + 1] = (byte) ((input[i] >>> 8) & 0xffL);

output[j + 2] = (byte) ((input[i] >>> 16) & 0xffL);

output[j + 3] = (byte) ((input[i] >>> 24) & 0xffL);

}

}

// Decode把byte数组按顺序合成成long数组,因为java的long类型是64bit的,只合成低32bit,高32bit清零,以适应原始C实现的用途

private void Decode(long[] output, byte[] input, int len) { int i, j;

for (i = 0, j = 0; j < len; i++, j += 4) {

output[i] = b2iu(input[j]) | (b2iu(input[j + 1]) << 8)

| (b2iu(input[j + 2]) << 16) | (b2iu(input[j + 3]) << 24);

}

return;

}

// b2iu是一个把byte按照不考虑正负号的原则的"升位"程序,因为java没有unsigned运算

public static long b2iu(byte b) {

return b < 0 ? b & 0x7F + 128 : b;

}

// byteHEX(),用来把一个byte类型的数转换成十六进制的ASCII表示,因为java中的byte的toString无法实现这一点,我们又没有C语言中的sprintf(outbuf,"%02X",ib) public static String byteHEX(byte ib) {

char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A',

'B', 'C', 'D', 'E', 'F' };

char[] ob = new char[2];

ob[0] = Digit[(ib >>> 4) & 0X0F];

ob[1] = Digit[ib & 0X0F];

String s = new String(ob);

return s;

}

public static void main(String args[]) {

MD5 m = new MD5();

本文来源:https://www.bwwdw.com/article/zc9q.html

Top