加密算法
MD5/SHA(160),256
由于Sha算法原理和md5是样的,故明白md5算法原理就明白了sha系列算法原理。
Message Digest Algorithm MD5为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护.。是计算机广泛使用的杂凑算法之一(又译摘要算法、哈希算法,主流编程语言普遍已有MD5实现。将数据(如汉字)运算为另一固定长度值,是杂凑算法的基础原理,MD5的前身有MD2、MD3和MD4。
- 广泛用于加密和解密技术,常用于文件校验。校验?不管文件多大,经过MD5后都能生成唯一的MD5值。好比现在的ISO校验,都 是MD5校验。怎么用?当然是把ISO经过MD5后产生MD5的值。一般下载linux-ISO的朋友都见过下载链接旁边放着MD5的串。就是用来验证文 件是否一致的.
- MD5加密算法为现在应用最广泛的哈希算法之一,该算法广泛应用于互联网网站的用户文件加密,能够将用户密码加密为128位的长整数。数据库并不明文存储用户密码,而是在用户登录时将输入密码字符串进行MD5加密,与数据库中所存储的MD5值匹配,从而降低密码数据库被盗取后用户损失的风险.
用途
-
防止被篡改:
- 比如发送一个电子文档,发送前,我先得到MD5的输出结果a。然后在对方收到电子文档后,对方也得到一个MD5的输出结果b。如果a与b一样就代表中途未被篡改。
- 比如我提供文件下载,为了防止不法分子在安装程序中添加木马,我可以在网站上公布由安装文件得到的MD5输出结果。
-
防止直接看到明文: 现在很多网站在数据库存储用户的密码的时候都是存储用户密码的MD5值。这样就算不法分子得到数据库的用户密码的MD5值,也无法知道用户的密码。(比如在UNIX系统中用户的密码就是以MD5(或其它类似的算法)经加密后存储在文件系统中。当用户登录的时候,系统把用户输入的密码计算成MD5值,然后再去和保存在文件系统中的MD5值进行比较,进而确定输入的密码是否正确。通过这样的步骤,系统在并不知道用户密码的明码的情况下就可以确定用户登录系统的合法性。这不但可以避免用户的密码被具有系统管理员权限的用户知道,而且还在一定程度上增加了密码被破解的难度.)
-
防止抵赖(数字签名): 这需要一个第三方认证机构。例如A写了一个文件,认证机构对此文件用MD5算法产生摘要信息并做好记录。若以后A说这文件不是他写的,权威机构只需对此文件重新产生摘要信息,然后跟记录在册的摘要信息进行比对,相同的话,就证明是A写的了。这就是所谓的“数字签名”.
安全性
普遍认为MD5是很安全,因为暴力破解的时间是一般人无法接受的。实际上如果把用户的密码MD5处理后再存储到数据库,其实是很不安全的。因为用户的密码是比较短的,而且很多用户的密码都使用生日,手机号码,身份证号码,电话号码等等。或者使用常用的一些吉利的数字,或者某个英文单词。如果我把常用的密码先MD5处理,把数据存储起来,然后再跟你的MD5结果匹配,这时我就有可能得到明文。比如某个MD5破解网站http://www.cmd5.com/default.aspx,所以现在大多数网站密码的策略是强制要求用户使用数字大小写字母的组合的方式提高用户密码的安全度。
算法步骤
-
填充:如果输入信息的长度(bit)对512求余的结果不等于448,就需要填充使得对512求余的结果等于448。填充的方法是填充一个1和n个0。填充完后,信息的长度就为N*512+448(bit);
-
记录信息长度:用64位来存储填充前信息长度。这64位加在第一步结果的后面,这样信息长度就变为N*512+448+64=(N+1)*512位。
-
装入标准的幻数(四个整数):标准的幻数(物理顺序)是(A=(01234567)16,B=(89ABCDEF)16,C=(FEDCBA98)16,D=(76543210)16)。如果在程序中定义应该是: (A=0X67452301L,B=0XEFCDAB89L,C=0X98BADCFEL,D=0X10325476L)。有点晕哈,其实想一想就明白了。
-
四轮循环运算:循环的次数是分组的个数(N+1)
1)将每一512字节细分成16个小组,每个小组64位(8个字节)
2)先认识四个线性函数(&是与,|是或,~是非,^是异或)
F(X,Y,Z)=(X&Y)|((~X)&Z) G(X,Y,Z)=(X&Z)|(Y&(~Z)) H(X,Y,Z)=X^Y^Z I(X,Y,Z)=Y^(X|(~Z))1234
3)设Mj表示消息的第j个子分组(从0到15),<<
FF(a,b,c,d,Mj,s,ti)表示a=b+((a+F(b,c,d)+Mj+ti)<<<s) GG(a,b,c,d,Mj,s,ti)表示a=b+((a+G(b,c,d)+Mj+ti)<<<s) HH(a,b,c,d,Mj,s,ti)表示a=b+((a+H(b,c,d)+Mj+ti)<<<s) II(a,b,c,d,Mj,s,ti)表示a=b+((a+I(b,c,d)+Mj+ti)<<<s)1234
4)四轮运算
第一轮 a=FF(a,b,c,d,M0,7,0xd76aa478) b=FF(d,a,b,c,M1,12,0xe8c7b756) c=FF(c,d,a,b,M2,17,0x242070db) d=FF(b,c,d,a,M3,22,0xc1bdceee) a=FF(a,b,c,d,M4,7,0xf57c0faf) b=FF(d,a,b,c,M5,12,0x4787c62a) c=FF(c,d,a,b,M6,17,0xa8304613) d=FF(b,c,d,a,M7,22,0xfd469501) a=FF(a,b,c,d,M8,7,0x698098d8) b=FF(d,a,b,c,M9,12,0x8b44f7af) c=FF(c,d,a,b,M10,17,0xffff5bb1) d=FF(b,c,d,a,M11,22,0x895cd7be) a=FF(a,b,c,d,M12,7,0x6b901122) b=FF(d,a,b,c,M13,12,0xfd987193) c=FF(c,d,a,b,M14,17,0xa679438e) d=FF(b,c,d,a,M15,22,0x49b40821) 第二轮 a=GG(a,b,c,d,M1,5,0xf61e2562) b=GG(d,a,b,c,M6,9,0xc040b340) c=GG(c,d,a,b,M11,14,0x265e5a51) d=GG(b,c,d,a,M0,20,0xe9b6c7aa) a=GG(a,b,c,d,M5,5,0xd62f105d) b=GG(d,a,b,c,M10,9,0x02441453) c=GG(c,d,a,b,M15,14,0xd8a1e681) d=GG(b,c,d,a,M4,20,0xe7d3fbc8) a=GG(a,b,c,d,M9,5,0x21e1cde6) b=GG(d,a,b,c,M14,9,0xc33707d6) c=GG(c,d,a,b,M3,14,0xf4d50d87) d=GG(b,c,d,a,M8,20,0x455a14ed) a=GG(a,b,c,d,M13,5,0xa9e3e905) b=GG(d,a,b,c,M2,9,0xfcefa3f8) c=GG(c,d,a,b,M7,14,0x676f02d9) d=GG(b,c,d,a,M12,20,0x8d2a4c8a) 第三轮 a=HH(a,b,c,d,M5,4,0xfffa3942) b=HH(d,a,b,c,M8,11,0x8771f681) c=HH(c,d,a,b,M11,16,0x6d9d6122) d=HH(b,c,d,a,M14,23,0xfde5380c) a=HH(a,b,c,d,M1,4,0xa4beea44) b=HH(d,a,b,c,M4,11,0x4bdecfa9) c=HH(c,d,a,b,M7,16,0xf6bb4b60) d=HH(b,c,d,a,M10,23,0xbebfbc70) a=HH(a,b,c,d,M13,4,0x289b7ec6) b=HH(d,a,b,c,M0,11,0xeaa127fa) c=HH(c,d,a,b,M3,16,0xd4ef3085) d=HH(b,c,d,a,M6,23,0x04881d05) a=HH(a,b,c,d,M9,4,0xd9d4d039) b=HH(d,a,b,c,M12,11,0xe6db99e5) c=HH(c,d,a,b,M15,16,0x1fa27cf8) d=HH(b,c,d,a,M2,23,0xc4ac5665) 第四轮 a=II(a,b,c,d,M0,6,0xf4292244) b=II(d,a,b,c,M7,10,0x432aff97) c=II(c,d,a,b,M14,15,0xab9423a7) d=II(b,c,d,a,M5,21,0xfc93a039) a=II(a,b,c,d,M12,6,0x655b59c3) b=II(d,a,b,c,M3,10,0x8f0ccc92) c=II(c,d,a,b,M10,15,0xffeff47d) d=II(b,c,d,a,M1,21,0x85845dd1) a=II(a,b,c,d,M8,6,0x6fa87e4f) b=II(d,a,b,c,M15,10,0xfe2ce6e0) c=II(c,d,a,b,M6,15,0xa3014314) d=II(b,c,d,a,M13,21,0x4e0811a1) a=II(a,b,c,d,M4,6,0xf7537e82) b=II(d,a,b,c,M11,10,0xbd3af235) c=II(c,d,a,b,M2,15,0x2ad7d2bb) d=II(b,c,d,a,M9,21,0xeb86d391)1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
5)每轮循环后,将A,B,C,D分别加上a,b,c,d,然后进入下一循环,一共N+1轮循环。
具体代码,这里注意下,第一组的4个幻数就是初始值:result[0]=a,result[1]=b,result[2]=c,result[3]=d
处理完第一组后:
result[0] += a;
result[1] += b;
result[2] += c;
result[3] += d;
result[0]=result[0]&0xFFFFFFFFL;
result[1]=result[1]&0xFFFFFFFFL;
result[2]=result[2]&0xFFFFFFFFL;
result[3]=result[3]&0xFFFFFFFFL;
然后继续作为下一组的输入,然后继续处理完后加和,截断,这样得到进行完一大轮。一共进行4×16=64小轮,大轮的个数是N+1.即补位后512的倍数。这样最后的结果是4个16进制数,共16×4×4=128位。
下面是Java版使用实例,并不去真的实现这个算法,这个算法的实现参见上面的具体代码。
import org.springframework.util.DigestUtils;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* @author pinker on 2018/8/6
*/
public class md5Util {
static char[] hex = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
public static void main(String[] args) {
md5method1();
byte[] result = DigestUtils.md5Digest("pinker".getBytes());
StringBuilder resu = DigestUtils.appendMd5DigestAsHex("pinker".getBytes(), new StringBuilder());
System.out.println(byte2str(result));
System.out.println(resu.toString());
}
/**
* 对于给定数量的更新数据,digest 方法只能被调用一次。
* digest 被调用后,MessageDigest 对象被重新设置成其初始状态。
*/
private static void md5method1() {
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
String word = "pinker";
digest.update(word.getBytes());
System.out.println(byte2str(digest.digest()));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
private static String byte2str(byte[] bytes) {
int len = bytes.length;
StringBuffer result = new StringBuffer();
for (int i = 0; i < len; i++) {
byte byte0 = bytes[i];
//16的倍数
int index = byte0 >>> 4 & 0xf;
result.append(hex[index]);
//16的余数
int index1 = byte0 & 0xf;
result.append(hex[index1]);
}
return result.toString();
}
}
上面的算法用三种方法实现了一下,其实底层调用的都是method1,method1,method2的转16进制是自己实现的,可以看一下结果:
D46B65D8CCB25C5BA37B93437B543F6F
D46B65D8CCB25C5BA37B93437B543F6F
d46b65d8ccb25c5ba37b93437b543f6f
BASE64
private static final String word = "i am pinker";
public static void main(String[] args) throws IOException {
springutilmethod();
jdkmethod();
bouncycastlemethod();
}
private static void bouncycastlemethod() {
System.out.println("----------------bouncycastle-----------------------");
byte[] result = Base64.encode(word.getBytes());
System.out.println(new String(result));
System.out.println(new String(Base64.decode(result)));
}
private static void jdkmethod() throws IOException {
System.out.println("-----------------jdk------------------");
BASE64Encoder encoder = new BASE64Encoder();
String result = encoder.encode(word.getBytes());
System.out.println(result);
System.out.println(new String(new BASE64Decoder().decodeBuffer(result)));
}
private static void springutilmethod() {
System.out.println("------------------springutil----------------------");
byte[] result = Base64Utils.encode(word.getBytes());
System.out.println(new String(result));
System.out.println(new String(Base64Utils.decode(result)));
}
Base64
为什么会有Base64编码呢?因为有些网络传送渠道并不支持所有的字节,例如传统的邮件只支持可见字符的传送,像ASCII码的控制字符就 不能通过邮件传送。这样用途就受到了很大的限制,比如图片二进制流的每个字节不可能全部是可见字符,所以就传送不了。最好的方法就是在不改变传统协议的情 况下,做一种扩展方案来支持二进制文件的传送。把不可打印的字符也能用可打印字符来表示,问题就解决了。Base64编码应运而生,Base64就是一种 基于64个可打印字符来表示二进制数据的表示方法。
算法步骤
- 把要编码的字符转换成相应的ascii码
- 每3个字符对应的24位划分为4个6位(2^6=64),每个6位可以对应上面的一个字符,然后对应下来。
- 如果最后的字符不足3个字符,则补位==
如
字符串 a b c
ASCII 97 98 99
8bit 01100001 01100010 01100011
6bit 011000 010110 001001 100011
十进制 24 22 9 35
对应编码 Y W J j
public class Base64Util {
private static final String word = "i am pinker";
public static void main(String[] args) throws IOException {
springutilmethod();
jdkmethod();
bouncycastlemethod();
System.out.println("测试原理是否正确!");
byte[] result = Base64Utils.encode("abc".getBytes());
System.out.println(new String(result));
result = Base64Utils.encode("A".getBytes());
System.out.println(new String(result));
result = Base64Utils.encode("BC".getBytes());
System.out.println(new String(result));
}
private static void bouncycastlemethod() {
System.out.println("----------------bouncycastle-----------------------");
byte[] result = Base64.encode(word.getBytes());
System.out.println(new String(result));
System.out.println(new String(Base64.decode(result)));
}
private static void jdkmethod() throws IOException {
System.out.println("-----------------jdk------------------");
BASE64Encoder encoder = new BASE64Encoder();
String result = encoder.encode(word.getBytes());
System.out.println(result);
System.out.println(new String(new BASE64Decoder().decodeBuffer(result)));
}
private static void springutilmethod() {
System.out.println("------------------springutil----------------------");
byte[] result = Base64Utils.encode(word.getBytes());
System.out.println(new String(result));
System.out.println(new String(Base64Utils.decode(result)));
}
}
DES/DESede/AES
public class DESUtil {
private static final String word = "****";
private static final String src = "i am pinker";
static byte[] keys;
static {
SecretKey secretKey = null;
try {
secretKey = KeyGenerator.getInstance("AES").generateKey();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
keys = secretKey.getEncoded();
}
public static void main(String[] args) {
desmethod();
desedemethod();
aesmethod();
aesmethod();
aesmethod();
}
private static void aesmethod() {
try {
System.out.println(Base64Utils.encodeToString(keys));
Key key = new SecretKeySpec(keys, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] result = cipher.doFinal(src.getBytes());
String save = Base64Utils.encodeToString(result);
//解密
cipher.init(Cipher.DECRYPT_MODE, key);
result = cipher.doFinal(Base64Utils.decode(save.getBytes()));
System.out.println(new String(result));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
}
private static void desedemethod() {
try {
Key key = SecretKeyFactory.getInstance("DESede").generateSecret(new DESedeKeySpec(word.getBytes()));
Cipher cipher = Cipher.getInstance("DESede");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] result = cipher.doFinal(src.getBytes());
String save = Base64Utils.encodeToString(result);
System.out.println(save);
//解密
cipher.init(Cipher.DECRYPT_MODE, key);
result = cipher.doFinal(Base64Utils.decode(save.getBytes()));
System.out.println(new String(result));
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
}
private static void desmethod() {
try {
Cipher cipher = Cipher.getInstance("DES");
DESKeySpec keySpec = new DESKeySpec(word.getBytes());
//用密钥工厂来加密解密
Key key = SecretKeyFactory.getInstance("DES").generateSecret(keySpec);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] result = cipher.doFinal(src.getBytes());
String save = Base64Utils.encodeToString(result);
System.out.println(save);
//解密
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] srcs = cipher.doFinal(Base64Utils.decode(save.getBytes()));
System.out.println(new String(srcs));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
}
}
2g++YDyEGbz3Mvdjg0Sd9g==
i am pinker
gS8BqL5Sr/i0odO+npYHUA==
i am pinker
oKvX3yJRpzIRnFpe59lfzg==
i am pinker
oKvX3yJRpzIRnFpe59lfzg==
i am pinker
oKvX3yJRpzIRnFpe59lfzg==
i am pinker
建议用keygenerator来生成密钥。