网络安全,信息安全、区块链学科的基础
古代密码学 替换法例如:bee,b替换为w,e替换为p,单词就变为了wpp
单表替换:原文和密文使用的是同一张表
多表替换:多张表,原文和密文对比
例子:
表1:abcde - swtrp,表2:abcde-chfhk,表3:abcde-jftou
原文:bee
密钥:312
密文:b从表3获取,第二个e从表1获取,第三个3e从表2获取,即fpk
移位法恺撒加密
例如:移动3位,原文:abc,密文:def
频率分析破解英文 频率从高到低 e> t > a
近代密码学恩尼格玛密码机 核心:使用移位法和替换法
被图灵破解了
电影《模仿游戏》
现代密码学 散列函数 MD5MD5信息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的 密码散列函数,可以产生出一个128位(16字节)的散列值(hash value)可以被加以破解,对于需要高度安全性的数据,专家一般建议改用其他算法,如SHA-2。MD5算法无法防止碰撞(collision),因此不适用于安全性认证,如SSL公开密钥认证或是数字签名等
public static void main(String[] args) throws Exception {
MessageDigest md5 = MessageDigest.getInstance("MD5");
// 可以使用 DigestUtils.md5Digest("abc".getBytes());
byte[] digest = md5.digest("123".getBytes());
System.out.println(digest.length);
}
SHA1
SHA-1(英语:Secure Hash Algorithm 1,中文名:安全散列算法1)是一种密码散列函数,美国国家安全局设计,并由美国国家标准技术研究所(NIST)发布为联邦数据处理标准(FIPS)。SHA-1可以生成一个被称为消息摘要的160位(20字节)散列值,散列值通常的呈现形式为40个 十六进制数
正式名称为 SHA 的家族第一个成员发布于 1993年。然而人们给它取了一个非正式的名称 SHA-0 以避免与它的后继者混淆。两年之后, SHA-1,第一个 SHA 的后继者发布了。 另外还有四种变体,曾经发布以提升输出的范围和变更一些细微设计: SHA-224, SHA-256, SHA-384 和 SHA-512 (这些有时候也被称做 SHA-2):
public static void main(String[] args) Exception {
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
byte[] digest = sha1.digest("123".getBytes());
System.out.println(digest.length);
}
SHA256
256位
public static void main(String[] args) throws Exception {
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
byte[] digest = sha256.digest("123".getBytes());
System.out.println(digest.length);
}
SHA512
512位
public static void main(String[] args) throws Exception {
MessageDigest sha512 = MessageDigest.getInstance("SHA-512");
byte[] digest = sha512.digest("123".getBytes());
System.out.println(digest.length);
}
安全性更高的加密方式
HMACMAC message authentication code,
HMAC 是 MAC值 + hash值,
下载的软件的md5和官网的md5对比,如果一样,则是安全的,即没有被修改
String input = "琉衣"; String algorithm = "MD5"; MessageDigest md5 = MessageDigest.getInstance(algorithm); byte[] digest = md5.digest(); // 因为ascii是0-127,但是此数组里有负数,解析不出来 String encode = base64.encode(digest); System.out.println(encode); // 1B2M2Y8AsgTpgAmY7PhCfg== // 发现和 网上的在线md5加密,得出的结果不一样,那是因为消息摘要不是使用base64进行编码的,所以我们需要把值转成16进制转成16进制
要注意,要有补0操作,原因在Integer.toHexString(b)这个语句,如果字节是00001111只得到了F
String s = Integer.toHexString(0x00001111); System.out.println(s); // 1111
0xff,表示十六进制的两个f,一个f即1111,所以也就是八位,一个字节
toHexString方法传入的参数是int类型32位,此处传入的是byte类型8位,所以需要在前面补24个0,然后 &ff,与上11111111就是把前面的24个0去掉,只要最后8位,int是由4组byte组成的,并且java中本身就以byte读取
String input = "琉衣";
String algorithm = "MD5";
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
byte[] digest = messageDigest.digest(input.getBytes());
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
String s = Integer.toHexString(b & 0xff);
if (s.length() == 1){
s = "0" + s;
}
sb.append(s);
}
System.out.println(sb.toString());
获取文件信息摘要
import com.sun.org.apache.xml.internal.security.utils.base64;
public static void main(String[] args) throws Exception {
String sha1 = getDigestFile("D:\study.txt", "SHA1");
// String sha1 = getDigestFile("D:\study.txt", "SHA-256");
System.out.println(sha1);
}
private static String getDigestFile(String filePath, String algorithm) throws Exception {
FileInputStream fis = new FileInputStream(filePath);
int len;
byte[] bytes = new byte[1024];
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ((len = fis.read(bytes)) != -1) {
baos.write(bytes, 0, len);
}
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
byte[] digest = messageDigest.digest(baos.toByteArray());
System.out.println("密文长度: " + digest.length);
return toHex(digest);
}
private static String toHex(byte[] digest) {
StringBuilder stringBuilder = new StringBuilder();
for (byte b : digest) {
String s = Integer.toHexString(b & 0xff);
if (s.length() == 1) {
s = "0" + s;
}
stringBuilder.append(s);
}
return stringBuilder.toString();
}
消息认证码
typora ctrl + . 即可中文符号输出
- 首先A 将加解密的密钥k1和生成消息验证码的密钥k2 都发送给B(安全的方式:比如 非对称加密)
- A将明文通过k1加密得到密文s,然后将密文和k2通过hash得到 消息认证码m
- A将s和m一起发送给B,然后B将s和k2通过hash得到m',m和m'进行比对,一样则,则用k1进行解密
将A发送给B的消息认证码和密文被X截取,但是不做任何修改,而是重新发送,并且是多次。比如A向B借100,但是X发10次给B
预防重放攻击- 加随机数
该方法优点是认证双方不需要时间同步,双方记住使用过的随机数,如发现报文中有以前使用过的随机数,就认为是重放攻击。缺点是需要额外保存使用过的随机数,若记录的时间段较长,则保存和查询的开销较大 - 加时间戳
该方法优点是不用额外保存其他信息。缺点是认证双方需要准确的时间同步,同步越好,受攻击的可能性就越小。但当系统很庞大,跨越的区域较广时,要做到精确的时间同步并不是很容易。 - 加流水号
就是双方在报文中添加一个逐步递增的整数,只要接收到一个不连续的流水号报文(太大或太小),就认定有重放威胁。该方法优点是不需要时间同步,保存的信息量比随机数方式小。缺点是一旦攻击者对报文解密成功,就可以获得流水号,从而每次将流水号递增欺骗认证端
在实际中,常将方法(1)和方法(2)组合使用,这样就只需保存某个很短时间段内的所有随机数,而且时间戳的同步也不需要太精确。对付重放攻击除了使用本以上方法外,还可以使用挑战一应答机制和一次性口令机制,而且似乎后面两种方法在实际中使用得更广泛
挑战一应答机制 一次性口令机制 DDoS我开了一家有五十个座位的重庆火锅店,由于用料上等,童叟无欺。平时门庭若市,生意特别红火,而对面二狗家的火锅店却无人问津。二狗为了对付我,想了一个办法,叫了五十个人来我的火锅店坐着却不点菜,让别的客人无法吃饭。
上面这个例子讲的就是典型的 DDoS 攻击,全称是 Distributed Denial of Service,翻译成中文就是分布式拒绝服务。一般来说是指攻击者利用“肉鸡”对目标网站在较短的时间内发起大量请求,大规模消耗目标网站的主机资源,让它无法正常服务。在线游戏、互联网金融等领域是 DDoS 攻击的高发行业
对称加密加密和解密的方式,使用同一把密钥
假设A手握一把密钥 key1,那么A需要克隆一把相同的密钥 key1’
在第一次通信中,A将报文连同 key1’一起发送给B
例如:需要发送原文3
- 设置密钥为108,则发送 3 * 108 = 324,发送给对方
- 对方收到之后,使用304 / 108 = 3得到原文
特点:
- 机密速度快,可以加密大文件
- 密文可逆,一旦密钥泄露,会导致数据泄露
- 加密后编码表找不到对应的字符,出现乱码
- 一般结合base64使用
核心原理:
- 流加密:对每一个字母或比特加密,然后组成一起
- 块加密:对信息流分块,不够可以补位,然后再对每一块分别加密,最后拼到一起
高级加密标准
以64位为分组对数据加密,它的密钥长度64位,实际参与运算56位,加密解密用同一算法
数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准,并授权在非密级政府通信中使用
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import java.util.Arrays;
import java.util.base64;
public class DES {
// 算法秘钥,Wrong Key Size,密钥的长度64位
private static final String DES_KEY = "12345678";
private static final String ALGORITHM_NAME = "DES";
public static byte[] encryptDes(byte[] data) {
try {
// DES算法要求有一个可信任的随机数源
DESKeySpec desKey = new DESKeySpec(DES_KEY.getBytes());
// 创建一个密钥工厂,然后用它把DESKeySpec转换成一个SecretKey对象
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM_NAME);
SecretKey key = keyFactory.generateSecret(desKey);
// 加密对象
Cipher cipher = Cipher.getInstance(ALGORITHM_NAME);
// 第一个参数是模式:加密,解密
cipher.init(Cipher.ENCRYPT_MODE, key);
// 加密
return cipher.doFinal(data);
} catch (Exception e) {
throw new RuntimeException("加密错误,错误信息:", e);
}
}
public static byte[] decryptbasedDes(byte[] cryptData) {
try {
DESKeySpec desKey = new DESKeySpec(DES_KEY.getBytes());
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM_NAME);
SecretKey key = keyFactory.generateSecret(desKey);
Cipher cipher = Cipher.getInstance(ALGORITHM_NAME);
cipher.init(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(cryptData);
} catch (Exception e) {
throw new RuntimeException("解密错误,错误信息:", e);
}
}
public static void main(String[] args) {
String data = "琉衣";
byte[] result = encryptDes(data.getBytes());
// 直接打印密文,会出现乱码
System.out.println(result);
System.out.println("加密后:" + Arrays.toString(base64.getEncoder().encode(result)));
byte[] source = decryptbasedDes(result);
System.err.println("解密后:" + new String(source));
}
}
AES
密码学中的高级加密标准(Advanced Encryption Standard,AES),又称Rijndael加密法,是美国联邦政府 采用的一种区块加密标准
密钥128位
import com.sun.org.apache.xml.internal.security.utils.base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class T {
public static void main(String[] args) throws Exception {
String input = "琉衣";
String key = "0123456789123456";
String transformation = "AES";
// 等同于
// String transformation = "AES/ECB/PKCS5Padding";
String algorithm = "AES";
String encryptAES = encryptAES(input, key, transformation, algorithm);
System.out.println("加密:" + encryptAES);
String s = decryptAES(encryptAES, key, transformation, algorithm);
System.out.println("解密:" + s);
}
private static String encryptAES(String input, String key, String transformation, String algorithm) throws Exception {
Cipher cipher = Cipher.getInstance(transformation);
// 加密规则
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
cipher.init(Cipher.ENCRYPT_MODE, sks);
byte[] bytes = cipher.doFinal(input.getBytes());
return base64.encode(bytes);
}
private static String decryptAES(String encryptAES, String key, String transformation, String algorithm) throws Exception {
Cipher cipher = Cipher.getInstance(transformation);
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
cipher.init(Cipher.DECRYPT_MODE, sks);
byte[] bytes = cipher.doFinal(base64.decode(encryptAES));
return new String(bytes);
}
}
使用CBC加密模式
import com.sun.org.apache.xml.internal.security.utils.base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class T {
public static void main(String[] args) throws Exception {
String input = "琉衣";
String key = "0123456789123456";
String transformation = "AES/CBC/PKCS5Padding";
String algorithm = "AES";
String encryptAES = encryptAES(input, key, transformation, algorithm);
System.out.println("加密:" + encryptAES);
String s = decryptAES(encryptAES, key, transformation, algorithm);
System.out.println("解密:" + s);
}
private static String encryptAES(String input, String key, String transformation, String algorithm) throws Exception {
Cipher cipher = Cipher.getInstance(transformation);
SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
// 创建iv向量,iv向量,是使用到CBC加密模式
IvParameterSpec iv = new IvParameterSpec(key.getBytes());
cipher.init(Cipher.ENCRYPT_MODE,sks,iv);
byte[] bytes = cipher.doFinal(input.getBytes());
return base64.encode(bytes);
}
private static String decryptAES(String encryptAES, String key, String transformation, String algorithm) throws Exception{
Cipher cipher = Cipher.getInstance(transformation);
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(),algorithm);
// 创建iv向量 //CBC加密模式需要有IV向量
IvParameterSpec iv = new IvParameterSpec(key.getBytes());
cipher.init(Cipher.DECRYPT_MODE,secretKeySpec,iv);
byte[] bytes = cipher.doFinal(base64.decode(encryptAES));
return new String(bytes);
}
}
加密:CCEsmebPjdSoUoZ+hMFLJg==
解密:琉衣
加密模式
ECB
Electronic codebook,电子密码本,按照块密码的块大小被分为数个块,并对每个块进行独立加密
- 优点: 可以并行处理数据
- 缺点:同样的原文生成同样的密文,不能很好保护数据
Cipher-block chaining,密码块链接,每个明文块先与前一个密文块进行异或后,在进行加密,第一个明文和IV异或再加密
- 优点:同样的原文生成的密文不一样
- 缺点:串行处理数据
…
CFB…
OFB…
填充模式当需要按块处理的数据,数据长度不合符块处理需求时,按照一定的方法填充满块长 的规则
NoPadding- 不填充
- 在DES加密算法下, 要求原文长度必须是8byte的整数倍
- 在AES加密算法下, 要求原文长度必须是16byte的整数倍
数据块的大小为8位,不足就补位
base64-
base64是一种用64个字符来表示任意二进制数据的方法,完成了数据在HTTP协议上的传输
-
它是一种编码方式,而非加密方式
-
数据编码之后,数据量会变大,变大1/3左右
base64一般用于在HTTP协议下传输二进制数据,由于HTTP协议是文本协议,所以在HTTP协议下传输二进制数据需要将二进制数据转换为字符数据。然而直接转换是不行的。因为网络传输只能传输可打印字符
编码规则
-
首先将待编码的内容转换成8位二进制,每3个字符为一组
-
如果编码前的长度是3n+1,编码后的内容最后面补上2个 =,如果编码前的长度是3n+2,内容最后面补上1个 =
-
再将每一组的二进制内容拆分成6位的二进制,不足6位的后面补足0
-
每个6进制的数字前面补足0,保证变成8位二进制
-
将补足后的内容根据base64编码表转换成base64内容输出
(不足四个字符的时候会用 =来补足,下面会说明)
编码前 “hb”
**1.**根据ascii码转换成8位二进制,3个为一组:
01101000,01100010
**2.**编码前长度是3n+2,所以后面补1个 ‘=’:
01101000,01100010,=
**3.**拆分成6位二进制,不足6位的在后面补足0,0010补足变成001000:
011010,000110,001000,=
**4.**每个6进制的数字前面补足0:
0011010,00000110,00000010,=
**5.**根据base64编码表输出:
aGI=
The base64 Alphabet
| 索引 | 对应字符 | 索引 | 对应字符 | 索引 | 对应字符 | 索引 | 对应字符 |
|---|---|---|---|---|---|---|---|
| 0 | A | 17 | R | 34 | i | 51 | z |
| 1 | B | 18 | S | 35 | j | 52 | 0 |
| 2 | C | 19 | T | 36 | k | 53 | 1 |
| 3 | D | 20 | U | 37 | l | 54 | 2 |
| 4 | E | 21 | V | 38 | m | 55 | 3 |
| 5 | F | 22 | W | 39 | n | 56 | 4 |
| 6 | G | 23 | X | 40 | o | 57 | 5 |
| 7 | H | 24 | Y | 41 | p | 58 | 6 |
| 8 | I | 25 | Z | 42 | q | 59 | 7 |
| 9 | J | 26 | a | 43 | r | 60 | 8 |
| 10 | K | 27 | b | 44 | s | 61 | 9 |
| 11 | L | 28 | c | 45 | t | 62 | + |
| 12 | M | 29 | d | 46 | u | 63 | / |
| 13 | N | 30 | e | 47 | v | ||
| 14 | O | 31 | f | 48 | w | ||
| 15 | P | 32 | g | 49 | x | ||
| 16 | Q | 33 | h | 50 | y |
public class base64Test {
public static void main(String[] args) {
// 获取编码器
base64.Encoder encoder = base64.getEncoder();
byte[] encode = encoder.encode("abc".getBytes());
System.out.println(new String(encode));
// 获取解码器
base64.Decoder decoder = base64.getDecoder();
byte[] decode = decoder.decode(encode);
System.out.println(new String(decode));
// 或者使用
byte[] encode2 = baseUtils.encode("abc".getBytes());
}
}
base58 一般用在比特币里面的一种编码方式,没有数字0,字母O,大写I,小写i,+,/
非对称加密假设A手握公钥 key2 和私钥 key2’
首先,A将报文主体连同 key2 一起发送给B
此后,B向A发送的报文都将使用 key2 加密
而被 key2 上锁的报文 只有A手上的 key2’ 才能解密
同理:当A想要回复B时,正好相反
此时A就使用B的公钥对数据进行加密
而B就用自己的私钥进行解密
非对称加密可以解决密钥交换问题
也称公钥加密。使用公钥加密的文本只能用私钥解密,使用私钥加密的文本也可以使用公钥解密
公钥不需要具有安全性,因为公钥需要在网络间传输,非对称加密可以解决密钥交换的问题
非对称加密算法,常见的比如RSA、ECC、DH,DSA等
-
RSA加密算法:RSA加密算法是一种非对称加密算法。在公开密钥加密和电子商业中RSA被广泛使用。RSA是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的
-
RSA是目前最有影响力的公钥加密算法,该算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥,即公钥,而两个大素数组合成私钥。公钥是可发布的供任何人使用,私钥则为自己所有,供解密之用
-
解密者拥有私钥,并且将由私钥计算生成的公钥发布给加密者。加密都使用公钥进行加密,并将密文发送到解密者,解密者用私钥解密将密文解码为明文。
public class T {
public static void main(String[] args) throws Exception {
String input = "琉衣";
String algorithm = "RSA";
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] bytes = cipher.doFinal(input.getBytes());
String encode = base64.encode(bytes);
System.out.println("密文: " + encode);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] bytes1 = cipher.doFinal(bytes);
System.out.println("原文: " + new String(bytes1));
}
}
保存共钥、私钥 读取公钥、私钥
package com.example.demo;
import com.sun.org.apache.xml.internal.security.utils.base64;
import org.apache.commons.io.FileUtils;
import javax.crypto.Cipher;
import java.io.File;
import java.nio.charset.Charset;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Objects;
public class T {
private static final String PUBLIC_KEY = "publicKey";
private static final String PRIVATE_KEY = "privateKey";
private static final String RSA = "RSA";
public static void main(String[] args) throws Exception {
String input = "琉衣";
String algorithm = "RSA";
generateKeyToFile(algorithm, "D:/a.pub", "D:/a.pri");
Key key = getKey("D:/a.pub", RSA, PUBLIC_KEY);
System.out.println(base64.encode(key.getEncoded()));
Key key1 = getKey("D:/a.pri", RSA, PRIVATE_KEY);
System.out.println(base64.encode(key1.getEncoded()));
}
private static void generateKeyToFile(String algorithm, String publicPath, String privatePath) 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();
String encode = base64.encode(privateKeyEncoded);
String encode1 = base64.encode(publicKeyEncoded);
// 保存文件
FileUtils.writeStringToFile(new File(publicPath), encode1, Charset.defaultCharset());
FileUtils.writeStringToFile(new File(privatePath), encode, Charset.defaultCharset());
}
// 读取公钥/私钥
private static Key getKey(String keyPath, String algorithm, String keyType) throws Exception {
String s = FileUtils.readFileToString(new File(keyPath), Charset.defaultCharset());
// 密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
if (Objects.equals(keyType, PUBLIC_KEY)) {
// 密钥规范
// 此类表示根据 ASN.1 类型 SubjectPublicKeyInfo 进行编码的公用密钥的 ASN.1 编码。X.509 标准中定义的 SubjectPublicKeyInfo 语法
X509EncodedKeySpec specification = new X509EncodedKeySpec(base64.decode(s));
return keyFactory.generatePublic(specification);
}
// 该类代表私有密钥的ASN.1编码,根据ASN.1类型PrivateKeyInfo进行编码。PrivateKeyInfo语法在PKCS#8标准中
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(base64.decode(s));
return keyFactory.generatePrivate(pkcs8EncodedKeySpec);
}
private static String encryptRSA(String algorithm, Key key, String input) throws Exception {
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] bytes = cipher.doFinal(input.getBytes());
return base64.encode(bytes);
}
private static String decryptRSA(String algorithm, Key key, String encode) throws Exception {
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decode = cipher.doFinal(base64.decode(encode));
return new String(decode);
}
}
混合加密
TLS是使用对称加密和非对称加密的混合加密方式来实现的
完整性通过信息摘要算法实现
在交换密钥的环节使用非对称加密,之后的通信则使用对称加密
例如:A手握一对非对称密钥(公钥key3、私钥key3’),B手握一对对称密钥(key4、key4’)
阶段1:交换密钥-使用非对称加密
1、A将公钥 key3 发送给B
2、B将下一阶段加密/解密用的 key4’ 放在报文中,并使用公钥 key3 对报文进行加密
3、A使用私钥 key3’ 解密报文,得到 key4’
阶段2:数据通信-使用对称加密
A和B分别使用 key4’ 和 key4对接收/发送的报文进行解密/加密
一方面,第一阶段的非对称加密 保证了对称密钥的安全性
另一方面,第二阶段的对称加密 可以提高加密/解密处理的速度,提高数据传输的效率
加密和完整性都搞定之后就差认证了
认证认证中心 CA(Certificate Authority) 是认证中心,全世界具有认证的CA就几家,分别颁布了DV,OV,EV,区别在于可信度,DV是最低的,只是域名级别的可信,EV是最高的,经过了法律和审计的严格核查,可以证明网络拥有者的身份(在浏览器地址会显示出公司的名字,比如Apple,Github的网站)。不同的信任等级的机构一起形成了层级关系
形成 证书链,最高的是RCA(root CA),RSA还会相互给对方签名
可以访问https的网站查看证书的路径,就能看到层级关系
发布认证过的公钥,才能解决公钥的信任问题
-
根据RSA算法生成公钥和私钥,然后将文件进行hash,得到哈希值.然后将哈希值和私钥解密运算->得到数字签名
-
公钥,文件,数字签名一起发送给另外一个人,然后使用公钥根据RSA对数字签名通过加密算法 得到哈希值,
-
然后再对文件哈希运算得到哈希值,看是否和上面的哈希值一样
-
如果被冒充了,
-
那么该如何确定就是小明发送的呢?这就涉及到了数字证书,小明将公钥和个人身份发送给权威公证的证书颁发机构CA,核实了小明的身份之后,将颁发一个数字证书,该证书包含了小明的身份信息和公钥,就可以保证了
-
但如何保证数字证书不会被伪造呢?因为CA机构自己也生成一套公钥和私钥,使用私钥 对小明的身份和公钥 生成数字签名,然后将该数字签名放到小明的数字证书中。每个人的电脑是有安装默认的根证书,根证书记录了可以信赖的CA机构信息及其公钥
通过以上方式就可以确保某一文件被谁签署并且没有被篡改
运行中输入certlm.msc ,打开证书管理器
数字签名代码实现public class T {
private static final String PUBLIC_KEY = "publicKey";
private static final String PRIVATE_KEY = "privateKey";
private static final String RSA = "RSA";
public static void main(String[] args) throws Exception {
String input = "琉衣";
String algorithm = "RSA";
generateKeyToFile(algorithm, "D:/a.pub", "D:/a.pri");
Key key = getKey("D:/a.pub", RSA, PUBLIC_KEY);
System.out.println(base64.encode(key.getEncoded()));
Key key1 = getKey("D:/a.pri", RSA, PRIVATE_KEY);
System.out.println(base64.encode(key1.getEncoded()));
String signaturedData = generateSignature(input, "sha256withrsa", (PrivateKey) key1);
boolean isTrue = verifySignature(input, "sha256withrsa", (PublicKey)key, signaturedData);
System.out.println(isTrue);
}
private static void generateKeyToFile(String algorithm, String publicPath, String privatePath) 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();
String encode = base64.encode(privateKeyEncoded);
String encode1 = base64.encode(publicKeyEncoded);
// 保存文件
FileUtils.writeStringToFile(new File(publicPath), encode1, Charset.defaultCharset());
FileUtils.writeStringToFile(new File(privatePath), encode, Charset.defaultCharset());
}
// 读取公钥/私钥
private static Key getKey(String keyPath, String algorithm, String keyType) throws Exception {
String s = FileUtils.readFileToString(new File(keyPath), Charset.defaultCharset());
// 密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
if (Objects.equals(keyType, PUBLIC_KEY)) {
// 密钥规范
X509EncodedKeySpec specification = new X509EncodedKeySpec(base64.decode(s));
return keyFactory.generatePublic(specification);
}
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(base64.decode(s));
return keyFactory.generatePrivate(pkcs8EncodedKeySpec);
}
// RSA加密
private static String encryptRSA(String algorithm, Key key, String input) throws Exception {
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] bytes = cipher.doFinal(input.getBytes());
return base64.encode(bytes);
}
// RSA解密
private static String decryptRSA(String algorithm, Key key, String encode) throws Exception {
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decode = cipher.doFinal(base64.decode(encode));
return new String(decode);
}
// 生成签名
private static String generateSignature(String input, String algorithm, PrivateKey privateKey) throws Exception {
Signature signature = Signature.getInstance(algorithm);
// 初始化签名
signature.initSign(privateKey);
// 传入原文
signature.update(input.getBytes());
// 生成签名
byte[] sign = signature.sign();
return base64.encode(sign);
}
// 校验签名
private static boolean verifySignature(String input, String algorithm, PublicKey publicKey, String signaturedData) throws Exception {
// 获取签名对象
Signature signature = Signature.getInstance(algorithm);
// 初始化签名
signature.initVerify(publicKey);
// 传入原文
signature.update(input.getBytes());
// 校验数据,验证自己活得的签名和传入的签名是否匹配
return signature.verify(base64.decode(signaturedData));
}
}
Keytool的使用
撞库
撞库是黑客通过收集互联网已泄露的用户和密码信息,生成对于的字典表,尝试批量登录其他网站后,得到一系列可以登录的用户。因为很多用户在不同网站使用的账号密码大多是相同的,因此黑客可以通过获取用户在A网站的账户从而尝试登录B网站
不要每个网站的密码都设置成一样的
URLENCODE为什么使用
- url发送给服务器,不允许出现空格和特殊字符
- url转义其实也只是为了符合url的规范而已。因为在标准的url规范中中文和很多的字符是不允许出现在url中的
除了 杠-,下划线_,点 . ,数字,字母 之外字符都将被替换成%后跟两位十六进制数
需要转换:
- ASCII中,0-31、128这33个字符属于控制字符,32-127这95个字符属于可打印字符,网络传输只能传输这95个字符
- 一些非ASCII字符
- 一些保留字符 比如 &
- 不安全的字符,比如 空格,会变为+加号
public class UrlEncode {
public static void main(String[] args) throws UnsupportedEncodingException {
String encode = URLEncoder.encode("http://xx.cn/中文 , . ? + ", String.valueOf(StandardCharsets.UTF_8));
System.out.println(encode);
String decode = URLDecoder.decode(encode, "UTF-8");
System.out.println(decode);
}
}



