@MD5码以及相关衍生
在写易卖网项目的时候,要求我把用户密码加密,在网上查阅资料,再次进行笔记记录,代码皆非本人敲写,只是统合自己所需要的记录翻阅学习,满怀感激
**
1.JAVA中包java.security是java提供的加密API——MessageDigest
**
- 作用:提供MD5,SHA-1,SHA-256等的加密算法。可接受任意长度的输入,并产生固定长度的输出。输出一般可称为摘要或散列
- 我们一般可以使用MessageDigest进行一些常规的加密操作,具体的使用如下:
//实例化一个MessageDigest对象,通过提供的静态的getInstance方法。方法中参数指的是加密的算法,大小写无所谓。
MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
//输入待加密的字符串
messageDigest.update("待加密的字符串");
//加密之后生成的密文的字节数组
byte[] value = messageDigest.digest();
//一般不会直接使用生成的字节数组,而是转化成16进行字符串,长度一般可以设定
//下来将提供字节数组转化为16进制字符串的方法``
private static String tranform16Str(byte[] md, int needLen){
char[] hexDigits = { '0','1','2','3','4','5','6','7','8','9',
'a','b','c','d','e','f'};
try {
int j = md.length;
char buf[] = new char[needLen];
int k = 0;
for (int i = 0; i < needLen/2; i++) {
byte byte0 = md[i];
buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
buf[k++] = hexDigits[byte0 & 0xf];
}
return new String(buf);
} catch (Exception e) {
log.error("加密后的密文转化为16进制字符串过程中出现异常,",e);
}
return null;
}
简单示例:
//为了避免用户密码直接裸露存储在数据库中,应对用户密码进行加密,有些数据库就有加密函数,比如oracle,有个示例如下,进行对密码字段加密:
CREATE OR REPLACE function md5(input_string VARCHAR2) return varchar2
IS
raw_input RAW(128) := UTL_RAW.CAST_TO_RAW(input_string);
decrypted_raw RAW(2048);
error_in_input_buffer_length EXCEPTION;
BEGIN
--dbms_output.put_line(sysdate || '> 加密前的数据:' || input_string);
sys.dbms_obfuscation_toolkit.MD5(input => raw_input,
checksum => decrypted_raw);
--dbms_output.put_line(sysdate || '> 加密后的数据:' || rawtohex(decrypted_raw));
return lower(rawtohex(decrypted_raw));
END;
而这个方法也存在缺陷性,只适合某一特定的数据库,一旦数据库换了(不是所有数据库都有加密解密存储函数),这个加密方法就不能用了。
这里要讨论的是用java方法对数据进行MD5码的加密,相对也是比较简单,因为java有现成的类java.security,MessageDigest帮我们生成MD5码。
思路是把密码字符窜进行MD5码的转换存储在数据库中,用户登入的时候把登入输入的密码字符窜进行MD5码的转换,再与数据库中存储的密码MD5码进行比较;如用户修改密码就拿新的密码的MD5码进行替换。
生成MD5码的函数如下:
public String getMD5String(String str){
try{
byte psw[] = str.getBytes();
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(psw);
return this.toHex(md.digest());
} catch (IllegalStateException e) {
return null;
} catch (NoSuchAlgorithmException e) {
return null;
}
}
public String toHex(byte buffer[]) {
StringBuffer sb = new StringBuffer();
String s = null;
for (int i = 0; i < buffer.length; i++) {
s = Integer.toHexString((int) buffer[i] & 0xff);
if (s.length() < 2) {
sb.append('0');
}
sb.append(s);
}
return sb.toString();
}
上述原文
上述原文
衍生
- 续接(1)第一点
给一个文件进行加密怎么办。文件那么长又该如何加密?
//上面有这么一句
messageDigest.update("待加密的字符串");
//1.首先先把文件读取到一个字节数组里面
File file = new File(filePath);
InputStream in = new FileInputStream(file);
byte[] allData = readInputStream(in);//获取到文件的内容
//方式1:一段一段往里面塞
int len = allData.length;
int i = 0;
while(true){
try{
int arrLen = (len - i * 4096) > 4096 ? 4096 : (len - i * 4096);
byte[] content = new byte[arrLen];
System.arraycopy(getData, i * 4096, content, 0, arrLen);
messageDigest.update(content);
i++;
}catch (Exception e){
log.info("字节数组拷贝出现异常,表示完成 i ={}", i);
break;
}
}
byte[] transform = messageDigest.digest();
//说明,MessageDigest调用digest()方法之后 输入的摘要将被重置,意思就是之后需要再加密的话 可以直接使用之前已有的对象
String miwen = tranform16Str(transform, transform.length);
//方式2:一次性全部往里面塞
messageDigest.update(allData);
byte[] second = messageDigest.digest();
//之后再进行16进制的转换操作。
#####################################################################
public static byte[] readInputStream(InputStream inputStream) throws IOException {
byte[] buffer = new byte[1024];
int len = 0;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while ((len = inputStream.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
bos.close();
return bos.toByteArray();
}
buffer[i] & 0xff
代码中加密的时候 用到bytes[i] & 0xFF
MessageDigest md5 = MessageDigest.getInstance(“MD5”);
bytes = md5.digest(basestring.toString().getBytes("UTF-8"));
String hex = Integer.toHexString(bytes[i] & 0xFF);
首先你得清楚几个个概念
1.byte的取值范围
byte java 中一个字节 8位 即 -2^7—2^7-1 范围是 -128——127 (*对这个有疑问吗?在2 中解答)
2.计算机中负数的存储的值是它的补码
补码计算方法 负数取值是它的绝对值,的二进制数,取反,加1,那么对于 -128——-1的存储 就知道了吧
举个例子 -12 绝对值------> 12 就是 0000 1100 取反------> 1111 0011 加 1 1111 0100 (晓得不)
那么-128——127 就可以理解了
-128 是 绝对值 128 二进制--------> 1000 0000 取反 0111 1111(127) 加1 ,1000 0000 (128)
-1 的绝对值 是 1 0000 0001 取反 1111 1110 加1 1111 1111 (255)
计算机 中 -128——-1 对应存储的是 128-255
再看 2^7-1 也就是127 刚好 是0111 1111 为啥减1 因为 128 已经被占了 所以
-128——127 在数据库中真实存的是 128——255 外加 0——127 也就是 byte真正在计算机中存储范围是
0——255 只不过我们说的是它的取值范围 是 -128——127
3.byte 转 int 16进制 (int -2^31——2^31-1)
bytes[i] & 0xFF
byte和int运算,计算机会自动将 byte转化为32位,然后再运算,也就是8位转32位,那么计算机会自动将高位补1
所以-12 在计算机种就是 1111 0011--------> 1111 1111 1111 1111 1111 1111 1111 0100
然后 & 0xFF
0x 代表16进制 每一位取值范围是 0 —— 15
但是 10——15之间是两位数字了,会和10进制分部开 所以 10——15就对应了 A——F
那么0xFF 就是 0000 0000 0000 0000 0000 0000 1111 1111
&运算 得到 0000 0000 0000 0000 0000 0000 1111 0100
有没有发现什么?低八位 1111 0011 没有改变 ,那么为什么 & 0xFF而不是别的呢?
原因就是 byte 转int 前24位都被计算机强制 变成了1, 1111 1111 1111 1111 1111 1111 1111 0100 转化成int已经不是
-12在计算机中存储的值了,& 0xFF 0000 0000 0000 0000 0000 0000 1111 1111 恰好前24位都是 0,进行&运算,只会保留
低八位,也就是byte中原来在计算机中存储的值 1111 0100。
上述原文
integer.tohexstring
Integer.toHexString这个方法是把字节(转换成了int)以16进制的方式显示。
别人的相关笔记
位操作符
位操作符
- C语言中六种位运算符:
- “&” 按位与
- “|” 按位或
- “^” 按位异或
- “~”取反
- “<<”左移
- “>>”右移
因为项目内容,我主要只记录了&:按位与
按位与运算符"&"是双目运算符。 其功能是参与运算的两数各对应的二进位相与。只有对应的两个二进位均为1时,结果位才为1 ,否则为0。参与运算的数以补码方式出现。
例如:9&5可写算式如下: 00001001 (9的二进制补码)&00000101 (5的二进制补码) 00000001 (1的二进制补码)可见9&5=1。 按位与运算通常用来对某些位清0或保留某些位。例如把a 的高八位清 0 , 保留低八位, 可作 a&255 运算 ( 255 的二进制数为11111111)。
十进制转化为二进制
进制转换
十进制整数转换为二进制整数十进制整数转换为二进制整数采用"除2取余,逆序排列"法。具体做法是:用2整除十进制整数,可以得到一个商和余数;再用2去除商,又会得到一个商和余数,如此进行,直到商为小于1时为止,然后把先得到的余数作为二进制数的低位有效位,后得到的余数作为二进制数的高位有效位,依次排列起来。
十进制整数转二进制
如:
255=(11111111)B
255/2=127=余1
127/2=63余1
63/2=31=余1
31/2=15=余1
15/2=7余1
7/2=3=余1
3/2=1=余1
1/2=0===余1
我发现,上面“9”的二进制00001001 (9的二进制补码)和我根据算法算出来的二进制1001不一样,那么千米那那么多“0”是什么
补码(二进制补码)
计算机中的有符号数有三种表示方法,即原码、反码和补码。三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位,三种表示方法各不相同 。在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理 。
所以前面的“1001”就是数值位,因而,前面的一串“0”就是符号位
在介绍补码概念之前,先介绍一下“模”的概念:“模”是指一个计量系统的计数范围,如过去计量粮食用的斗、时钟等。计算机也可以看成一个计量机器,因为计算机的字长是定长的,即存储和处理的位数是有限的,因此它也有一个计量范围,即都存在一个“模”。如:时钟的计量范围是0~11,模=12。表示n位的计算机计量范围是 ,模= .“模”实质上是计量器产生“溢出”的量,它的值在计量器上表示不出来,计量器上只能表示出模的余数。任何有模的计量器,均可化减法为加法运算 。
就是取反后加1。
假设当前时针指向8点,而准确时间是6点,调整时间可有以下两种拨法:一种是倒拨2小时,即8-2=6;另一种是顺拨10小时,8+10=12+6=6,即8-2=8+10=8+12-2(mod 12).在12为模的系统里,加10和减2效果是一样的,因此凡是减2运算,都可以用加10来代替。若用一般公式可表示为:a-b=a-b+mod=a+mod-b。对“模”而言,2和10互为补数。实际上,以12为模的系统中,11和1,8和4,9和3,7和5,6和6都有这个特性,共同的特点是两者相加等于模。对于计算机,其概念和方法完全一样。n位计算机,设n=8,所能表示的最大数是11111111,若再加1成100000000(9位),但因只有8位,最高位1自然丢失。又回到了 00000000,所以8位二进制系统的模为 。在这样的系统中减法问题也可以化成加法问题,只需把减数用相应的补数表示就可以了。把补数用到计算机对数的处理上,就是补码 。
例:+9的补码是00001001。(备注:这个+9的补码是用8位2进制来表示的,补码表示方式很多,还有16位二进制补码表示形式,以及32位二进制补码表示形式,64位进制补码表示形式等。每一种补码表示形式都只能表示有限的数字。)