前言
最近在学习java编程,在学习到编码一块时,想着系统的学习并整理一下java和密码学这块的知识点。
密码学大概像中国的历史发展一样,可分为古典密码学、近代密码学、现代密码学。
古典密码学
古典密码主要是替换密码和移位密码。核心原理也就是替换法、移位法。
替换法
替换法也有两种,单表替换和多表替换。
单表替换即原文和密文用的同一张表
如表:j替换u,a替换q,v替换为f
所以明文java替换后变密文uqfq。
也有多表替换,即原文和密文用的是多张表
如:表 abcdef---swtrpv、abcdef---chfhkd、abcdef---jftoud
秘钥为 312
表示分别替换第三张表、第一张表、第二张表
则明文的abc替换后为密文sht。
移位法
移位法就是将原文所有字母在字母表上按照一个固定数组进行前后移位,得出密文。
如:明文abcde变为密文cdefg,就是在字母表里每个字母分别往后移动两位。
典型的移位法应用有"恺撒加密"。
java 代码实现"恺撒加密"
package javaweb.com.demo;
public class Encryption {
public static void main(String[] args) throws Exception {
String a = "cseroad";
int key = 3;
char[] chars = a.toCharArray();
//字符串变成字节数组
StringBuilder sb = new StringBuilder();
for (char c : chars) {
int ascii = c;
ascii = ascii + key;
//向右移动三位后的ascii
char newa = (char)ascii;
//System.out.println(newa);
sb.append(newa);
}
System.out.println("密文:"+sb.toString());
//转化为String对象
}
}
近代密码学
恩尼格玛机,本质上使用的还是替换和移位。后被图灵破解。
现代密码学
密码学基础
ASCII编码
我们知道在计算机中,存储数据都是用二进制表示的(即0和1)。那么像我们平时用到的字母、数字也是需要用二进制表示。于是有了ASCII编码表来统一进行换算。
java实现求字符对应的ASCII编码
package javaweb.com.demo;
public class Encryption {
public static void main(String[] args) throws Exception {
char a = 'a';
int b = a;
System.out.println(b);
}
}
即char字符类型自动转化为int类型。
字符a的ASCII码为97,存储到计算机的二进制为0110 0001。
Unicode编码
那么汉字、日文、韩文又是怎么在计算机中存储的呢?
这就有了Unicode编码表,也被称为万国码。为每种语言每个字符设置了统一并唯一的二进制编码。包含ASCII码,从0到127的字符。如上面的a的ASCII码为97,十六进制为61,则unicode编码为0x0061。该编码一般用十六进制表示。
package javaweb.com.demo;
public class Encryption {
public static void main(String[] args) throws Exception {
char a = '经';
int b = a;
System.out.println(b);
}
}

汉字"经"的十六进制为0x7ecf,转化十进制就是32463,即输出值。
但是char只能存储单一的字符。如果遇到字符串呢?
java实现求字符串对应的ASCII编码
package javaweb.com.demo;
public class Encryption {
public static void main(String[] args) throws Exception {
String a = "cseroad";
char[] chars = a.toCharArray();
//字符串转化为字节数组,然后遍历字符
for (char c : chars) {
int ascii = c;
System.out.println(ascii);
}
}
}
需要补充的是,UTF-8是Unicode实现的方式之一。两者之间也是通过程序进行转换的。
转换规则参考 字符编码笔记:ASCII,Unicode 和 UTF-8
二进制位
不管是什么编码方式,运行在计算机里只能是二进制数。
java 实现求某字符串二进制数
package javaweb.com.demo;
public class Encryption {
public static void main(String[] args) throws Exception {
String a = "经";
byte[] bytes = a.getBytes("UTF-8");
for (byte c : bytes) {
int ascii = c;
String s = Integer.toBinaryString(ascii);
System.out.println("二进制值:"+s);
}
}
}

UTF-8编码格式下,一个中文等于3个字节,一个字节等于8个二进制位。
GBK 编码格式下,一个中文等于2个字节,一个字节等于8个二进制位。
char和byte区别
char
- 字符数据类型,占两个字节,无符号
- 范围在0-65535之间
- 不可以表示负数,但是可以表示中文
byte
- 字节数据类型,有符号
- 范围在-128-127之间
- 可以和 ASCII 码相互转换,不可以表示中文
- 一字节等于8个二进制位
编码方式
URL编码
url编码指的是浏览器发送到服务器时使用的编码,用来便于浏览器和服务器进行通信。
url编码就是一个字符ascii码的十六进制,每个字节前面添加%。
java 实现url编码
package javaweb.com.demo;
import java.net.URLDecoder;
import java.net.URLEncoder;
public class Encryption {
public static void main(String[] args) throws Exception {
String old = "url 编码";
String encode = URLEncoder.encode(old,"UTF-8");
System.out.println(encode);
String decode = URLDecoder.decode(encode,"UTF-8");
System.out.println(decode);
}
}
Base64编码
base64编码是一种基于64个字符把二进制数据用文本表示的编码算法,方便传输,提高可读性。
分别是A-Z、a-z、0-9、两个字符+和/,正好64个字符。
原理
每3个8位二进制转化为4个6位二进制数,然后在每个6位二进制数都填两个高位0,得到4个8位二进制数,如果缺位则补0。再将其分别转化为十进制,最后通过base64编码表获取编码后字符。
如:
字符串"中国" 的utf-8编码对应的十六进制为e4b8ad、e59bbd
转化二进制为111001001011100010101101、111001011001101110111101
然后每6个二进制位为1组。
111001
001011
100010
101101
111001
011001
101110
111101
再分别填补两个高位0。
00111001
00001011
00100010
00101101
00111001
00011001
00101110
00111101
再转化十进制为57、11、34、45、57、25、46、61
按照base64编码表转为5Lit5Zu9

java 实现base64编码
package javaweb.com.demo;
import java.util.Base64;
public class Encryption {
public static void main(String[] args) throws Exception {
String old = "中国";
//encode
String bs64 = Base64.getEncoder().encodeToString(old.getBytes("UTF-8"));
System.out.println(bs64);
//decode
String ori = new String(Base64.getDecoder().decode(bs64),"UTF-8");
System.out.println(ori);
}
}

消息摘要算法
消息摘要算法也叫散列算法、哈希函数、单向函数。是一种将任意长度输入转换为固定长度输出的算法。
主要作用不是用于加密与解密,而是用于验证信息的完整性。常用于密码加密、文件校验。
消息摘要算法主要分为三类,MD(消息摘要算法)、SHA(安全散列算法)、MAC(消息认证码算法)
常见的消息摘要算法有MD5、SHA-1、SHA-256、SHA-512等等。
MD5
md5的长度默认为128个二进制位,这样表达是很不友好的,所以将二进制转成了16进制,每4个二进制位表示一个16进制,所以就变为32位了。
package javaweb.com.demo;
import java.security.MessageDigest;
public class Encryption {
public static void main(String[] args) throws Exception {
String input = "cseroad";
//明文
String algorithm = "MD5";
//加密类型
MessageDigest md = MessageDigest.getInstance(algorithm);
//初始化
md.update(input.getBytes("UTF-8"));
//更新摘要
byte[] r = md.digest();
//获得密文完成哈希计算
StringBuilder sb = new StringBuilder();
for (byte b : r) {
String s = Integer.toHexString(b & 0xff);
//密文10进制转16进制
if (s.length() == 1) {
s = "0"+s;
}
//判断如果密文长度为1,高位需要补0
sb.append(s);
}
System.out.println(sb.toString());
}
}

要点
使用MessageDigest类用于为应用程序提供信息摘要算法的功能。要实现SHA-1、SHA-256、SHA-512 替换algorithm变量即可。
也可以通过BigInteger类处理任意精度的整数运算,这样代码比较简洁。
java 实现MD5加密
package javaweb.com.demo;
import java.math.BigInteger;
import java.security.MessageDigest;
public class Encryption {
public static void main(String[] args) throws Exception {
String input = "cseroad";
String algorithm = "MD5";
MessageDigest md = MessageDigest.getInstance(algorithm);
md.update(input.getBytes("UTF-8"));
byte[] r = md.digest();
BigInteger a = new BigInteger(1,r);
//转化为16进制
System.out.println(String.format("%032x",a));
}
}
SHA-1
java实现SHA-1加密
package javaweb.com.demo;
import java.math.BigInteger;
import java.security.MessageDigest;
public class Encryption {
public static void main(String[] args) throws Exception {
String input = "cseroad";
String algorithm = "SHA-1";
MessageDigest md = MessageDigest.getInstance(algorithm);
md.update(input.getBytes("UTF-8"));
byte[] r = md.digest();
BigInteger a = new BigInteger(1,r);
//转化为16进制
System.out.println(String.format("%040x",a));
}
}

SHA-1的返回值时160位二进制位,同样和MD5一样,每四个二进制位换算为一个16进制,所以结果为40位。
盐salt
在密码学中,是指通过在任意固定位置插入特定的字符串,让散列后的结果和使用原始密码的散列结果不相符,这种过程称之为"加盐"。
注意加盐的目的是为了增加攻击者破解的难度,所以盐值不能太短,也不能使用固定盐值。
参考资料
https://www.bilibili.com/video/BV134411T7rq
https://www.bilibili.com/video/BV1ga4y1v7jn
《java 加密与解密的艺术》
网友评论