https是如何做到安全加密的?

1,255 阅读4分钟

由于 HTTP 天生“明文”的特点,整个传输过程完全透明,任何人都能够在链路中截获、修改或者伪造请求 / 响应报文,数据不具有可信性。因此产生了如下问题:

  1. 信息被窃听
  2. 信息被篡改
  3. 信息被劫持

什么是https

HTTPS 协议的主要功能基本都依赖于 TLS/SSL 协议,TLS/SSL 的功能实现主要依赖于三类基本算法

  • 散列函数 散列函数验证信息的完整性
  • 对称加密 对称加密算法采用协商的密钥对数据加密
  • 非对称加密 非对称加密实现身份认证和密钥协商

image.png

SSL/TLS的关系

SSL 即安全套接层(Secure Sockets Layer),在 OSI 模型中处于第 5 层(会话层),由网景公司于 1994 年发明,有 v2 和 v3 两个版本,而 v1 因为有严重的缺陷从未公开过。 SSL 发展到 v3 时已经证明了它自身是一个非常好的安全通信协议,于是互联网工程组 IETF 在 1999 年把它改名为 TLS(传输层安全,Transport Layer Security),正式标准化,版本号从 1.0 重新算起,所以 TLS1.0 实际上就是 SSLv3.1。

了解了基础概念,我们一起来看看https是如何解决上述问题的。

加密信息

我们知道http 是明文传输的,那我们很容易想到给明文加密呗,把传输的内容进行加密,服务端进行解密。这样子别人就不知道里面的内容是什么了。

常见的加密算法叫做加密算法(RSA)

const crypto = require('crypto');
function encrypt(data, key, iv) {
    let decipher = crypto.createCipheriv('aes-128-cbc', key, iv);
    decipher.update(data);
    return decipher.final('hex');
}

function decrypt(data, key, iv) {
    let decipher = crypto.createDecipheriv('aes-128-cbc', key, iv);
    decipher.update(data, 'hex');
    return decipher.final('utf8');
}

let key = '1234567890123456';
let iv = '1234567890123456';
let data = "hello";
let encrypted = encrypt(data, key, iv);
console.log("数据加密后:", encrypted);
let decrypted = decrypt(encrypted, key, iv);
console.log("数据解密后:", decrypted);

我们发现堆成加密有一个秘钥,可以用这个秘钥进行加密解密。

** 但是服务器和客户端怎么公用一个秘钥呢?在互联网上没有办法安全的交换密钥。**

使用非对称加密,加密秘钥

let {
  generateKeyPairSync,
  privateEncrypt,
  publicDecrypt
} = require('crypto');
let rsa = generateKeyPairSync('rsa', {
  modulusLength: 1024,
  publicKeyEncoding: {
    type: 'spki',
    format: 'pem'
  },
  privateKeyEncoding: {
    type: 'pkcs8',
    format: 'pem',
    cipher: 'aes-256-cbc',
    passphrase: 'server_passphrase'
  }
});
let message = 'hello';
console.log(rsa)
let enc_by_prv = privateEncrypt({
  key: rsa.privateKey,
  passphrase: 'server_passphrase'
}, Buffer.from(message, 'utf8'));
console.log('encrypted by private key: ' + enc_by_prv.toString('hex'));


let dec_by_pub = publicDecrypt(rsa.publicKey, enc_by_prv);
console.log('decrypted by public key: ' + dec_by_pub.toString('utf8'));

这样子我们就可以安全的权属秘钥了,黑客无法知道我们秘钥,就无法知道我们的信息是啥,这样子就可以做到信息防止被窃听了。

现在虽然可以做到数据防止被窃听,但是还是非常有可能被篡改的;

比如说:
浏览器 发送 我爱你 加密后 waai;
浏览器 发送 我不爱你 加密后 wbai;

黑客拦截到数据,经过观察和分析,发现了规律。

当浏览器发送 我爱你 给服务器试,直接讲密文篡改成 wbai; 这样子就篡改了你消息的本意;

数字签名

用数字签名可以防止,信息被篡改。(数字签名使用的是摘要算法)

我们用浏览器的私钥,进行信息摘要,做成签名,和信息一起发送给服务端;服务端进行验签,发现是本人,才继续执行逻辑;这样子就可以防止信息被篡改了。 image.png

let { generateKeyPairSync, createSign, createVerify } = require('crypto');
let passphrase = 'zhufeng';
let rsa = generateKeyPairSync('rsa', {
    modulusLength: 1024,
    publicKeyEncoding: {
        type: 'spki',
        format: 'pem'
    },
    privateKeyEncoding: {
        type: 'pkcs8',
        format: 'pem',
        cipher: 'aes-256-cbc',
        passphrase
    }
});
let content = 'hello';
const sign = getSign(content, rsa.privateKey, passphrase);
let serverCertIsValid = verifySign(content, sign, rsa.publicKey);
console.log('serverCertIsValid', serverCertIsValid);
function getSign(content, privateKey, passphrase) {
    var sign = createSign('RSA-SHA256');
    sign.update(content);
    return sign.sign({ key: privateKey, format: 'pem', passphrase }, 'hex');
}
function verifySign(content, sign, publicKey) {
    var verify = createVerify('RSA-SHA256');
    verify.update(content);
    return verify.verify(publicKey, sign, 'hex');
}

钓鱼网站

通过上面的方式,黑客无法做好窃听和篡改了,但是可以做到截取,自己假装一个服务器(俗称钓鱼网站);听过这种方式来欺骗用户;那么怎么防止呢?

通过权威机构的颁布的数字证书,证明你的网站是好网站。

image.png

let { generateKeyPairSync, createSign, createVerify, createHash } = require('crypto');
let passphrase = 'zhufeng';
let rsa = generateKeyPairSync('rsa', {
    modulusLength: 1024,
    publicKeyEncoding: {
        type: 'spki',
        format: 'pem'
    },
    privateKeyEncoding: {
        type: 'pkcs8',
        format: 'pem',
        cipher: 'aes-256-cbc',
        passphrase
    }
});
const info = {
    domain: "http://127.0.0.1:8080",
    publicKey: rsa.publicKey
};
const hash = createHash('sha256').update(JSON.stringify(info)).digest('hex');
const sign = getSign(hash, rsa.privateKey, passphrase);
const cert = { info, sign };

let certIsValid = verifySign(hash, cert.sign, rsa.publicKey);
console.log('certIsValid', certIsValid);

function getSign(content, privateKey, passphrase) {
    var sign = createSign('RSA-SHA256');
    sign.update(content);
    return sign.sign({ key: privateKey, format: 'pem', passphrase }, 'hex');
}
function verifySign(content, sign, publicKey) {
    var verify = createVerify('RSA-SHA256');
    verify.update(content);
    return verify.verify(publicKey, sign, 'hex');
}

通过如上流程,可以成功避免坏人的信息劫持。

结论:

上述就是https如果保证我们安全做的事情。