一、什么是HTTP协议
HTTP规范主要内容
1.请求方式 request method
最常见的就是GET和POST,还有其他请求方式
类型 | 描述 |
---|---|
GET | 请求从服务器获取资源 |
HEAD | 类似于GET请求,只不过不会返回实体数据,只获取报头 |
POST | 向服务器提交数据 |
PUT | 替换服务器的内容 |
DELETE | 请求服务器删除指定的资源 |
TRACE | 对链路进行测试或者诊断 |
…… | …… |
而且HTTP协议是可扩展的
2.请求和响应格式
HTTP请求request格式
HTTP响应response格式
3.响应状态码
类型 | 描述 |
---|---|
1xx | 信息,服务器收到请求,需要请求者继续执行操作 |
2xx | 成功,操作被成功接收并处理 |
2xx | 重定向,需要进一步的操作以完成请求 |
4xx | 客户端错误,请求包含语法错误或无法完成请求 |
5xx | 服务端错误,服务器在处理请求的过程中发生了错误 |
4.通信特点
1.请求应答模式
2.灵活可扩展
3.可靠传输——基于传输层的TCP协议(3次握手,4次挥手)
4.无状态——stateless,这一次访问和下一次访问没有任何关联----》cookie和session的诞生
二、为什么HTTP不安全
1.泄漏——破坏机密性
数据明文传输
2.篡改——破坏完整性
3.冒充——破坏真实性
泄漏的问题怎么解决?
数据加密,发送密文
1975年IBM公布了DES(Data Encryption Standard,标准加密算法,一种对称加密算法)
对称加密:共享密钥加密
比如:
密钥–12345678
encrypt(“bones 666”,“12345678”)----->密文
decrypt(“9jI4vE1h+frU4I/ZVAIEhQ==”,“12345678”) ----->明文
这个过程是可逆的
下面Java代码用来实现DES加密算法:(JDK版本8,太高无法运行,因为有一些包是1.8的)
public class DESEncrypt {
public static final String ALGORITHM ="DES";
public static void main(String[] args) throws Exception {
// 定义密钥
String key = "12345678";
// 明文
String text = "zyy 好爽";
// 加密
String cipher = encryptDesCipher(text, key);
System.out.println("DES加密后密文:"+cipher);
// 解密
text = decryptDesCipher(cipher, key);
System.out.println("DES解密后明文:"+text);
}
// DES加密算法
private static String encryptDesCipher(String text, String origKey) throws Exception{
Key key = new SecretKeySpec(origKey.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, key);
return Base64.encode(cipher.doFinal(text.getBytes()));
}
// DES解密算法
private static String decryptDesCipher(String text, String origKey) throws Exception{
Key key = new SecretKeySpec(origKey.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.DECRYPT_MODE, key);
return new String(cipher.doFinal(Base64.decode(text)));
}
}
运行结果:
当然还有改进版本的DES,这里就先不提。现在应该要关注的就是这个对称加密算法用在通信中,是如何工作的?
(1)服务端和客户端约定一个密钥
(2)客户端向服务端发送加密好之后的信息,服务端解密
难点1:密钥传输问题,密钥如何约定?总不能物理传输吧,hhh
难点2:访问人数一多,就要生成很多密钥,维护以及查找非常消耗性能和资源。
总结:对称加密的特点
不足:
1.密钥的私密性问题
2.密钥的存储、管理问题
优点:
计算简单,速度快
三、对称加密和非对称加密
对称加密已经分析在上面了,下面说说非对称加密。
非对称加密(公开密钥加密)
1.生成密钥对key pair(算法生成的)
{publicKey、privateKey}
2.公钥加密
encrypt(“bones 666”,publicKey);—>密文
3.私钥解密
decrypt(“”,privateKey);---->明文
举一个生活中的智慧来理解:
代码实现(1977年RSA,世界上第一个非对称加密的算法):(JDK1.8运行)
public class RSAEncrypt {
private final static int KEY_SIZE = 1024;
public static final String ALGORITHM ="RSA";
public static final String CHARSET ="UTF-8";
private static Map<Integer, String> keyMap = new HashMap<Integer, String>();
public static void main(String[] args) throws Exception {
// 生成密钥对
genKeyPair();
String publicKey = keyMap.get(0);
String privateKey = keyMap.get(1);
System.out.println("公钥:" + publicKey);
System.out.println("私钥:" + privateKey +"\n");
System.out.println("----------------------------");
String message = "bones 666";
// 用公钥加密,得到密文
String cipher = encrypt(message, publicKey);
System.out.println("用公钥加密后的密文:" + cipher);
// 用私钥解密,得到明文
String messageDe = decrypt(cipher, privateKey);
System.out.println("用匹配的私钥解密后的明文:" + messageDe);
}
/**
* 随机生成密钥对
*/
public static void genKeyPair() throws NoSuchAlgorithmException {
// KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(ALGORITHM);
// 初始化密钥对生成器
keyPairGen.initialize(KEY_SIZE, new SecureRandom());
// 生成一个密钥对,保存在keyPair中
KeyPair keyPair = keyPairGen.generateKeyPair();
// 得到私钥
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
// 得到公钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
String publicKeyString = Base64.getEncoder().encodeToString(publicKey.getEncoded());
// 得到私钥字符串
String privateKeyString = Base64.getEncoder().encodeToString(privateKey.getEncoded());
// 将公钥和私钥保存到Map
//0表示公钥
keyMap.put(0, publicKeyString);
//1表示私钥
keyMap.put(1, privateKeyString);
}
/**
* RSA公钥加密
* @param str 加密字符串
* @param publicKey 公钥
* @return 密文
* @throws Exception 加密过程中的异常信息
*/
public static String encrypt(String str, String publicKey) throws Exception {
// base64编码的公钥
byte[] decoded = Base64.getDecoder().decode(publicKey);
RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance(ALGORITHM).generatePublic(new X509EncodedKeySpec(decoded));
// RSA加密
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
String outStr = Base64.getEncoder().encodeToString(cipher.doFinal(str.getBytes(CHARSET)));
return outStr;
}
/**
* RSA私钥解密
* @param str 加密字符串
* @param privateKey 私钥
* @return 明文
* @throws Exception 解密过程中的异常信息
*/
public static String decrypt(String str, String privateKey) throws Exception {
// 64位解码加密后的字符串
byte[] inputByte = Base64.getDecoder().decode(str);
// base64编码的私钥
byte[] decoded = Base64.getDecoder().decode(privateKey);
RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(decoded));
// RSA解密
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, priKey);
String outStr = new String(cipher.doFinal(inputByte));
return outStr;
}
}
main方法运行结果:
非对称加密算法用在通信中,是如何工作的?
非对称加密算法流程
1.消息接收方生成密钥对,公开公钥
2.消息发送方用公钥加密消息
3.消息接收方用私钥解密消息
(1)浏览器获得服务器的公钥
注意:私钥自己保存
(2)公钥加密,私钥解密
(注意:上面用的是同一把公钥)
非对称加密的特点
优点:
1.不用担心密钥阐述过程中泄漏
2.存储、管理成本低
不足:
1.加密、解密更慢----》会涉及到大数乘法大数模等运算
2.如果服务器在发放公钥的时候,被黑客或者别的骗子掉包了怎么办 ?(身份冒充问题)
公钥传输:如果公钥被调包–中间人攻击(MITM)
Man-in-the-Middle Attack (MITM)
服务器将公钥发送出去之后,是不够权威和可信的,无法验证。
可信问题
看一个生活中的案例:
某村首富突然去世,留下巨额遗产
儿子继承遗产,需要证明他爸是他爸
需要一个公信力的机构,比如派出所;也可以亲自鉴定,DNA
如何证明身份?
数字证书:Digital Certificate
证书授权中心(CA):Certificate Authority ----- CA
全球有很多颁发这种证书的机构,比如百度的证书:
CA颁发的证书
提交:域名、公司、联系人、公钥等
证书包含:
颁发者、域名、公钥、有效期等
颁发证书就基本可以解决中间人攻击了。
问题:有人冒充CA咋办
1.如果骗子冒充你的身份,用他的域名去办证书会怎么样?
·不要怀疑这么多!CA不是吃干饭的,有权威性
·如果域名不匹配,浏览器会报错:
2.骗子网站把你的证书放到他的网站,冒充你怎么办?
·偷了证书没有用,没有私钥都是白扯
3.骗子把你的证书里面的公钥换成他自己的怎么办?
·如何防止证书内容被篡改?联系生活实际,借条和合同如何保证是你本人签署,未经篡改?—>签名+指纹
·计算机中,指纹就是校验哈希值
比如kali官网中,文件下载会提供Hash Code:
(check sum----摘要)
生成摘要,防止篡改
哈希方法:比如MD5(现在已经基本不用了),SHA256
对摘要加密,得到指纹
1.CA用哈希算法对服务器公钥生成摘要(哈希值),机密后得到指纹
2.客户端用哈希算法对服务器公钥生成摘要(哈希值),解密指纹,对比摘要
这样子就会陷入俄罗斯套娃。
摘要的加密和对比(非常重要!!!)
第一步:
CA生成一对密钥对
CA用它的私钥对摘要进行加密,得到一串密文,这个就是指纹
第二步:客户端用CA的公钥对指纹进行解密,得到摘要,然后跟自己计算出来的摘要进行对比。
问题:
1.为什么用私钥加密,公钥解密
2.如果浏览器要用CA的公钥解密(验签)指纹,CA的公钥在哪?浏览器从哪里得到?
CA公钥的传输
CA的公钥是怎么发给客户端的?
谁来证明CA的公钥的权威性?
如果有人冒充CA颁发的证书给自己怎么办?
加签验签(非常重要!
!!)
·公钥加密——私钥解密
·私钥加签——公钥验签
·用公钥加密,就可以保证只有持有私钥的人才能读取消息,没有私钥的人不能解密。保证私密。
·用私钥加签,就可以保证只有持有私钥的人才能发出消息,否则无法通过公钥验签。保证身份不可伪造。
加签验签代码
Java代码(JDK1.8)
public class RSASignature {
public static final String ENCRYPT_ALGORITHM = "RSA";
public static final String SIGN_ALGORITHM = "SHA1WithRSA";
private static final int KEY_SIZE = 1024;
private static Map<Integer, String> keyMap = new HashMap<Integer, String>();
public static void main(String[] args) throws NoSuchAlgorithmException {
// 生成公钥和私钥
genKeyPair();
String publicKey = keyMap.get(0);
System.out.println("公钥:" +publicKey);
System.out.println();
String privateKey = keyMap.get(1);
System.out.println("私钥:" + privateKey);
System.out.println();
String sign = sign("bones666", privateKey);
System.out.println("私钥签名后结果:" + sign);
System.out.println();
System.out.println("对密文用明文、公钥验签后结果:" + doCheck("bones666",sign, publicKey));
}
/**
* 随机生成密钥对
*/
public static void genKeyPair() throws NoSuchAlgorithmException {
// RSA算法生成公钥和私钥对
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(ENCRYPT_ALGORITHM);
// 初始化密钥对生成器
keyPairGen.initialize(KEY_SIZE, new SecureRandom());
// 生成一个密钥对,保存在keyPair中
KeyPair keyPair = keyPairGen.generateKeyPair();
// 得到私钥、公钥
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
// 得到字符串
String publicKeyString = Base64.getEncoder().encodeToString(publicKey.getEncoded());
String privateKeyString = Base64.getEncoder().encodeToString(privateKey.getEncoded());
// 将公钥和私钥保存到Map
keyMap.put(0, publicKeyString);
keyMap.put(1, privateKeyString);
}
/**
* RSA签名
*/
public static String sign(String content, String privateKey) {
try {
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(new BASE64Decoder().decodeBuffer(privateKey));
KeyFactory factory = KeyFactory.getInstance(ENCRYPT_ALGORITHM);
PrivateKey priKey = factory.generatePrivate(priPKCS8);
java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHM);
signature.initSign(priKey);
signature.update(content.getBytes());
byte[] signed = signature.sign();
return new BASE64Encoder().encode(signed);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* RSA验签
*/
public static boolean doCheck(String content, String sign, String publicKey) {
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] encodedKey = new BASE64Decoder().decodeBuffer(publicKey);
PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHM);
signature.initVerify(pubKey);
signature.update(content.getBytes());
boolean bverify = signature.verify(new BASE64Decoder().decodeBuffer(sign));
return bverify;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}
运行结果:
签名和验签的流程
验签:
1.对服务器公曰用哈希算法生成摘要(哈希值)
2.用CA公钥对指纹(密文)进行解密,得到摘要(哈希值)
3.对比两个摘要(哈希值)是否匹配
加签:
1.对服务器公钥用哈希算法生成摘要(哈希值)
2.用CA私钥对摘要加密,得到指纹(密文)
CA的公钥其实在你的电脑中(CA的证书是有信任链的,CA的根证书早就放在了你的电脑中了)
win+R–MMC可以查看根证书
那如果操作系统很久很久没有更新那怎么办呢?那只能更新操作系统了
比如在win7系统中安装mysql报错了:
其实就是根证书没有。
有时候需要自己把这个根证书放在电脑中怎么做呢?
用AWVS漏洞扫描工具就要求安装一个根证书:
一般安装根证书这个操作是非常敏感的。
最后还有一个问题:
问题:服务器回复你的消息,但是没有加密怎么办?
浏览器给服务器发消息,用服务器的公钥加密,服务器用自己的私钥解密。
但是服务器没有浏览器的公钥,那服务器怎么响应消息给浏览器呢?
这里要补充一下:
安全的通信
1.认证
2.协商
3.通信
在通信过程中,其实并不是一直用非对称加密的。非对称加密更多的是用在身份认证中。
SSL/TLS在HTTP中的应用
上面介绍的这种通信方式就是网景公司(Netscape)提出的。
SSL(Secure Socket Layer)安全 套接层
后来又改名成
TLS(Transport Layer Security)传输层安全协议
HTTP和HTTPS
要说明HTTP和HTTPS的关系,需要了解SSL/TLS的工作层次