- ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。。
- 实例代码:
public static void main(String[] args) {
char a = 'A';
int code = a;
System.out.println(code);
String s = "frank";
char[] chars = s.toCharArray();
for (char c : chars) {
int num = c;
System.out.println(num);
}
}
-
恺撒密码(Caesar cipher)是一种相传尤利乌斯·恺撒曾使用过的密码。恺撒于公元前100年左右诞生于古罗马,是一位著名的军事统帅。
-
恺撤密码是通过将明文中所使用的字母表按照一定的字数“平移”来进行加密的
-
凯撒密码加解密公式
-
示例代码如下:
public static void main(String[] args) {
String s = "frank";
int key = 3;
String encriptData = KaiserEncrypt(s, key);
System.out.println("encriptData="+encriptData);
String decriptData = KaiserDecrypt(encriptData, key);
System.out.println("decriptData="+decriptData);
/**
* result as below:
* encriptData=iudqn
* decriptData=frank
*/
}
/**
*
* @param orignal:原文
* @param key:秘钥
* @return:返回值
*/
private static String KaiserEncrypt(String orignal, int key) {
char[] chars = orignal.toCharArray();
StringBuilder sb = new StringBuilder();
for (char aChar : chars) {
// 获取字符的ASCII编码
int asciiCode = aChar;
// 偏移数据
asciiCode += key;
// 将偏移后的数据转为字符
char result = (char) asciiCode;
// 拼接数据
sb.append(result);
}
return sb.toString();
}
/**
*
* @param encryptedData 密文
* @param key 秘钥
* @return
*/
private static String KaiserDecrypt(String encryptedData, int key) {
char[] chars = encryptedData.toCharArray();
StringBuilder sb = new StringBuilder();
for (char aChar : chars) {
int asciiCode = aChar;
asciiCode -= key;
char result = (char) asciiCode;
sb.append(result);
}
return sb.toString();
}
- 将明文字母的出现频率与密文字母的频率相比较的过程
- 通过分析每个符号出现的频率而轻易地破译代换式密码
- 在每种语言中,冗长的文章中的字母表现出一种可对之进行分辨的频率。
- e是英语中最常用的字母,其出现频率为八分之一
- 由于破解太简单,在此不贴代码了,有兴趣的自己试试
-
对称加密采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。
-
示例
- 我们现在有一个原文3要发送给B
- 设置密钥为108, 3 * 108 = 324, 将324作为密文发送给B
- B拿到密文324后, 使用324/108 = 3 得到原文
-
常见对称加密算法
- DES : Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。
- AES : Advanced Encryption Standard, 高级加密标准 .在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。
-
特点
- 加密速度快, 可以加密大文件
- 密文可逆, 一旦密钥文件泄漏, 就会导致数据暴露
- 加密后编码表找不到对应字符, 出现乱码
- 一般结合Base64使用
-
分组密码的模式
-
按位异或
-
第一步需要将数据转换为二进制
-
按位异或操作符: ^
-
两个标志位进行按位异或操作:
- 相同为0, 不同为1
-
举例:
1 0 0 0 ----> 8 1 0 1 1 ----> 11 -----------------------按位异或一次 0 0 1 1 ----> 3 1 0 1 1 ----> 11 -----------------------按位异或两侧 1 0 0 0 -----> 8 ================================= a = 8 b = 11 a 和 b按位异或1次 ==> 加密 得到的结果再次和 b 按位异或 ===> 解密
-
-
ECB - Electronic Code Book, 电子密码本模式
- 特点: 简单, 效率高, 密文有规律, 容易被破解
- 最后一个明文分组必须要填充
- des/3des -> 最后一个分组填充满8字节
- aes -> 最后一个分组填充满16字节
- 不需要初始化向量
-
CBC - Cipher Block Chaining, 密码块链模式
- 特点: 密文没有规律, 经常使用的加密方式
- 最后一个明文分组需要填充
- des/3des -> 最后一个分组填充满8字节
- aes -> 最后一个分组填充满16字节
- 需要一个初始化向量 - 一个数组
- 数组的长度: 与明文分组相等
- 数据来源: 负责加密的人的提供的
- 加解密使用的初始化向量值必须相同
-
CFB - Cipher FeedBack, 密文反馈模式
- 特点: 密文没有规律, 明文分组是和一个数据流进行的按位异或操作, 最终生成了密文
- 需要一个初始化向量 - 一个数组
- 数组的长度: 与明文分组相等
- 数据来源: 负责加密的人的提供的
- 加解密使用的初始化向量值必须相同
- 不需要填充
-
OFB - Output-Feedback, 输出反馈模式
- 特点: 密文没有规律, 明文分组是和一个数据流进行的按位异或操作, 最终生成了密文
- 需要一个初始化向量 - 一个数组
- 数组的长度: 与明文分组相等
- 数据来源: 负责加密的人的提供的
- 加解密使用的初始化向量值必须相同
- 不需要填充
-
CTR - CounTeR, 计数器模式
- 特点: 密文没有规律, 明文分组是和一个数据流进行的按位异或操作, 最终生成了密文
- 不需要初始化向量
- 不需要填充
-
最后一个明文分组的填充
- 使用cbc, ecb需要填充
- 要求:
- 明文分组中进行了填充, 然后加密
- 解密密文得到明文, 需要把填充的字节删除
- 要求:
- 使用 ofb, cfb, ctr不需要填充
- 使用cbc, ecb需要填充
-
初始化向量 - IV
- ecb, ctr模式不需要初始化向量
- cbc, ofc, cfb需要初始化向量
- 初始化向量的长度
- des/3des -> 8字节
- aes -> 16字节
- 加解密使用的初始化向量相同
- 初始化向量的长度
-
-
DES加密示例代码如下:
- DES加密,key的大小必须是8个字节
- 如果没有指定分组密码模式和填充模式,ECB/PKCS5Padding就是默认值
- 如果指定分组密码模式为CBC,必须指定初始向量,初始向量中密钥的长度必须是8个字节
- NoPadding模式,原文的长度必须是8个字节的整倍数
public class DESDemo {
// DES加密算法,key的大小必须是8个字节
public static void main(String[] args) throws Exception {
String input = "frank";
String key = "12345678";
// 指定获取Cipher的算法,如果没有指定分组密码模式和填充模式,ECB/PKCS5Padding就是默认值
// CBC模式,必须指定初始向量,初始向量中密钥的长度必须是8个字节
// NoPadding模式,原文的长度必须是8个字节的整倍数
// String transformation = "DES";
String transformation = "DES/CBC/PKCS5Padding";
// 指定获取密钥的算法
String algorithm = "DES";
String encryptDES = DESEncrypt(input, key, transformation, algorithm);
System.out.println("加密:" + encryptDES);
String s = DESDecrypt(encryptDES, key, transformation, algorithm);
System.out.println("解密:" + s);
}
/**
*
* @param input 明文
* @param key 密钥(DES,密钥的长度必须是8个字节)
* @param transformation 获取Cipher对象的算法
* @param algorithm 获取密钥的算法
* @return 返回密文
* @throws Exception
*/
private static String DESEncrypt(String input, String key, String transformation, String algorithm) throws Exception {
// 1,获取Cipher对象
Cipher cipher = Cipher.getInstance(transformation);
// 指定密钥规则
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
// 2.初始化向量的秘钥长度需要根据算法而定,des 8个字节长度 aes 16个字节长度
//这里为了方便,统一使用传来的秘钥
IvParameterSpec iv = new IvParameterSpec(key.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, sks, iv);
// cipher.init(Cipher.ENCRYPT_MODE, sks);
// 3. 加密
byte[] bytes = cipher.doFinal(input.getBytes());
// 对数据进行Base64编码
String encode = Base64.encode(bytes);
return encode;
}
private static String DESDecrypt(String input, String key, String transformation, String algorithm) throws Exception {
Cipher cipher = Cipher.getInstance(transformation);
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
IvParameterSpec iv = new IvParameterSpec(key.getBytes());
cipher.init(Cipher.DECRYPT_MODE, sks, iv);
// cipher.init(Cipher.DECRYPT_MODE, sks);
byte[] bytes = cipher.doFinal(Base64.decode(input));
return new String(bytes);
}
}
- AES加密示例代码如下(AES和DES加密高度类似)
- AES加密,key的大小必须是16个字节
- 如果没有指定分组密码模式和填充模式,ECB/PKCS5Padding就是默认值
- 如果没有指定分组密码模式为CBC,必须指定初始向量,初始向量中密钥的长度必须是16个字节
- NoPadding模式,原文的长度必须是16个字节的整倍数
public class AESDemo {
// AES加密算法,key的大小必须是16个字节
public static void main(String[] args) throws Exception {
String input = "frank";
String key = "1234567887654321";
// 指定获取Cipher的算法,如果没有指定分组密码模式和填充模式,ECB/PKCS5Padding就是默认值
// CBC模式,必须指定初始向量,初始向量中密钥的长度必须是16个字节
// NoPadding模式,原文的长度必须是16个字节的整倍数
// String transformation = "AES";
String transformation = "AES/CBC/PKCS5Padding";
// 指定获取密钥的算法
String algorithm = "AES";
String encryptAES = AESEncrypt(input, key, transformation, algorithm);
System.out.println("加密:" + encryptAES);
String s = AESDecrypt(encryptAES, key, transformation, algorithm);
System.out.println("解密:" + s);
}
/**
*
* @param input 明文
* @param key 密钥(AES,密钥的长度必须是16个字节)
* @param transformation 获取Cipher对象的算法
* @param algorithm 获取密钥的算法
* @return 返回密文
* @throws Exception
*/
private static String AESEncrypt(String input, String key, String transformation, String algorithm) throws Exception {
// 1,获取Cipher对象
Cipher cipher = Cipher.getInstance(transformation);
// 指定密钥规则
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
// 2.初始化向量的秘钥长度需要根据算法而定,des 8个字节长度 aes 16个字节长度
//这里为了方便,统一使用传来的秘钥
IvParameterSpec iv = new IvParameterSpec(key.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, sks, iv);
// cipher.init(Cipher.ENCRYPT_MODE, sks);
// 3. 加密
byte[] bytes = cipher.doFinal(input.getBytes());
// 对数据进行Base64编码
String encode = Base64.encode(bytes);
return encode;
}
private static String AESDecrypt(String input, String key, String transformation, String algorithm) throws Exception {
Cipher cipher = Cipher.getInstance(transformation);
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
IvParameterSpec iv = new IvParameterSpec(key.getBytes());
cipher.init(Cipher.DECRYPT_MODE, sks, iv);
// cipher.init(Cipher.DECRYPT_MODE, sks);
byte[] bytes = cipher.doFinal(Base64.decode(input));
return new String(bytes);
}
}
-
秘钥分发困难
-
可以通过非对称加密完成秘钥的分发
https
Alice 和 Bob通信, Alice给bob发送数据, 使用对称加密的方式
- 生成一个非对称的秘钥对, bob生成
- bob将公钥发送给alice
- alice生成一个用于对称加密的秘钥
- alice使用bob的公钥就对称加密的秘钥进行加密, 并且发送给bob
- bob使用私钥就数据解密, 得到对称加密的秘钥
- 通信的双方使用写好的秘钥进行对称加密数据加密
- 不存在秘钥分发困难的问题
数据对谁更重要, 谁就拿私钥
- 直观上看: 私钥比公钥长
- 使用第三方工具生成密钥对: 公钥文件xxx.pub xxx
通信流程, 信息加密 (A写数据, 发送给B, 信息只允许B读)
A: 公钥
B: 私钥
登录认证 (客户端要登录, 连接服务器, 向服务器请求个人数据)
客户端: 私钥
服务器: 公钥
数字签名(表明信息没有受到伪造,确实是信息拥有者发出来的,附在信息原文的后面)
- 发送信息的人: 私钥
- 收到信息的人: 公钥
网银U盾
- 个人: 私钥
- 银行拿公钥
要求: Alice 给 bob发送数据, 保证数据信息只有bob能看到
- x509证书规范、pem、base64
- pem编码规范 - 数据加密
- base64 - 对数据编码, 可逆
- 不管原始数据是什么, 将原始数据使用64个字符来替代
- a-z A-Z 0-9 + /
- 不管原始数据是什么, 将原始数据使用64个字符来替代
- ASN.1抽象语法标记
- PKCS1标准
RSA
ECC(java需要借助第三方库实现加密解密,我将在另外一个项目中用go实现)
###6 RSA示例代码如下
-
注意:经测试在后端使用Cipher.getInstance(“RSA”)加密,在移动端获取解密的Cipher类时要使用Cipher.getInstance(“RSA/ECB/PKCS1Padding”)
-
移动端核心代码
//后端用私钥加密过的数据
String eText = "ETLPedgx7vR9E9JGNIj4pLhvurcOM26oo4RgJhmeF5RvDXVdl3qQ+5H6hmUx3dV2K8jPxi5aKSVs1xnjuMgSfK32fjrqzYBULFaBCmnN1HbpcwYFNMA3enWiVwT3TAWFKA9ReJ2DWh0lkCzaHruOmcWCY3f2tjuEE9X9L0DN7m5R9iy2qgEEDPgfzImYYl8wltYdudryz2fQ7UNGdIUPc75EMqdvHEUrxIi5A7cM0BDGQI2aXD+39ijQCOVBtai/9ohF7YXtGmbsocPBKarhe8qpVIcvXza6fBbOWxBC6Z68uRGcljTVkhvNjWrEmRuu5pc3C41bx4OK9FD8kPgITg==";
//后端给的公钥
String pkey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmpz9g5IX3ElRtXFo2+9nwD4+amqhEH4Rz1FI2cXeSiQeLPfdCoSsflLovdJ21NxvcKGw9IvmjWkLESCVU/pxDeP2UkVXFAjC2KhZvoQO4v0x4Yn3/55bAAQ9O3qoGatjPlDbzr1CEAi+ZA7NY1Oz2TtOSq8Odc7wc3Sq6U1gZBf87w5jq0GwQwgLrQjaVf5oTgKmavyf6g8Uq8U0QnktXCJpJUsSSZdeWTwAhtKk+MDkd5VRHIynLklOgeAhjG7xzEAad/Q32qLGcCwY+ySiZWLZ5q5uZAys4rj98LiwV6zLyk8CYYclUDUtBPLLXDRN8DUEe4uKAucFC4IlkrXQ0wIDAQAB";
//获取公钥对象
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
X509EncodedKeySpec spec = new X509EncodedKeySpec(Base64.decode(pkey, Base64.DEFAULT));
PublicKey publicKey = keyFactory.generatePublic(spec);
//指定解密algorithm = "RSA/ECB/PKCS1Padding"; Cipher cipher = Cipher.getInstance(algorithm);
algorithm = "RSA/ECB/PKCS1Padding";
String decrypt = RSADemo.RSADecrypt(algorithm, publicKey, eText, 256);
System.out.println("decript###" + decrypt + "###");
- 后端代码
public class RSADemo {
private static int MAX_ENCRIPT_SIZE = 200;
public static void main(String[] args) throws Exception {
String algorithm = "RSA";
String input = "frank";
// generateKeys(algorithm, "test.pri", "test.pub");
PrivateKey privateKey = getPrivateKey("test.pri", algorithm);
PublicKey publicKey = getPublicKey("test.pub", algorithm);
String encryptData = RSAEncrypt(algorithm, privateKey, input, MAX_ENCRIPT_SIZE);
System.out.println("encryptData=" + encryptData);
String decryptData = RSADecrypt(algorithm, publicKey, encryptData, 256);
System.out.println("decryptData=" + decryptData);
}
public static PrivateKey getPrivateKey(String priPath, String algorithm) throws Exception {
String privateKeyString = FileUtils.readFileToString(new File(priPath), Charset.defaultCharset());
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Base64.decode(privateKeyString));
return keyFactory.generatePrivate(spec);
}
public static PublicKey getPublicKey(String pubPath, String algorithm) throws Exception {
String publicKeyString = FileUtils.readFileToString(new File(pubPath), Charset.defaultCharset());
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
X509EncodedKeySpec spec = new X509EncodedKeySpec(Base64.decode(publicKeyString));
return keyFactory.generatePublic(spec);
}
public static void generateKeys(String algorithm, String priPath, String pubPath) throws Exception {
// 获取密钥对生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
// 获取密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 获取公私钥
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
// 获取公私钥的字节数组
byte[] privateKeyEncoded = privateKey.getEncoded();
byte[] publicKeyEncoded = publicKey.getEncoded();
// 对公私钥进行Base64的编码
String privateKeyString = Base64.encode(privateKeyEncoded);
String publicKeyString = Base64.encode(publicKeyEncoded);
FileUtils.writeStringToFile(new File(priPath), privateKeyString, Charset.defaultCharset());
FileUtils.writeStringToFile(new File(pubPath), publicKeyString, Charset.defaultCharset());
}
public static String RSAEncrypt(String algorithm, Key key, String input, int maxEncryptSize) throws Exception {
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] data = input.getBytes();
int total = data.length;
int offset = 0;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] bytes;
while (total - offset > 0) {
if (total - offset >= maxEncryptSize) {
bytes = cipher.doFinal(data, offset, maxEncryptSize);
offset += maxEncryptSize;
} else {
bytes = cipher.doFinal(data, offset, total - offset);
offset = total;
}
baos.write(bytes);
}
return Base64.encode(baos.toByteArray());
}
public static String RSADecrypt(String algorithm, Key key, String input, int maxEncryptSize) throws Exception {
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] data = Base64.decode(input);
int total = data.length;
int offset = 0;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] bytes;
while (total - offset > 0) {
if (total - offset >= maxEncryptSize) {
bytes = cipher.doFinal(data, offset, maxEncryptSize);
offset += maxEncryptSize;
} else {
bytes = cipher.doFinal(data, offset, total - offset);
offset = total;
}
baos.write(bytes);
}
return baos.toString();
}
}
由于篇幅有点长,我将在另外一篇介绍哈希函数和数字签名