导航菜单

  • 0.api
  • 0.Async
  • 0.module
  • 1.ES2015
  • 2.Promise
  • 3.Node
  • 4.NodeInstall
  • 5.REPL
  • 6.NodeCore
  • 7.module&NPM
  • 8.Encoding
  • 9.Buffer
  • 10.fs
  • 11.Stream-1
  • 11.Stream-2
  • 11.Stream-3
  • 11.Stream-4
  • 12-Network-2
  • 12.NetWork-3
  • 12.Network-1
  • 13.tcp
  • 14.http-1
  • 14.http-2
  • 15.compress
  • 16.crypto
  • 17.process
  • 18.yargs
  • 19.cache
  • 20.action
  • 21.https
  • 22.cookie
  • 23.session
  • 24.express-1
  • 24.express-2
  • 24.express-3
  • 24.express-4
  • 25.koa-1
  • 26.webpack-1-basic
  • 26.webpack-2-optimize
  • 26.webpack-3-file
  • 26.webpack-4.tapable
  • 26.webpack-5-AST
  • 26.webpack-6-sources
  • 26.webpack-7-loader
  • 26.webpack-8-plugin
  • 26.webpack-9-hand
  • 26.webpack-10-prepare
  • 28.redux
  • 28.redux-jwt-back
  • 28.redux-jwt-front
  • 29.mongodb-1
  • 29.mongodb-2
  • 29.mongodb-3
  • 29.mongodb-4
  • 29.mongodb-5
  • 29.mongodb-6
  • 30.cms-1-mysql
  • 30.cms-2-mysql
  • 30.cms-3-mysql
  • 30.cms-4-nunjucks
  • 30.cms-5-mock
  • 30.cms-6-egg
  • 30.cms-7-api
  • 30.cms-8-roadhog
  • 30.cms-9-yaml
  • 30.cms-10-umi
  • 30.cms-12-dva
  • 30.cms-13-dva-ant
  • 30.cms-14-front
  • 30.cms-15-deploy
  • 31.dva
  • 31.cms-13-dva-antdesign
  • 33.redis
  • 34.unittest
  • 35.jwt
  • 36.websocket-1
  • 36.websocket-2
  • 38.chat-api-1
  • 38.chat-api-2
  • 38.chat-3
  • 38.chat-api-3
  • 38.chat
  • 38.chat2
  • 38.chat2
  • 39.crawl-0
  • 39.crawl-1
  • 39.crawl-2
  • 40.deploy
  • 41.safe
  • 42.test
  • 43.nginx
  • 44.enzyme
  • 45.docker
  • 46.elastic
  • 47.oauth
  • 48.wxpay
  • index
  • 52.UML
  • 53.design
  • index
  • 54.linux
  • 57.ts
  • 56.react-ssr
  • 58.ts_react
  • 59.ketang
  • 59.ketang2
  • 61.1.devops-linux
  • 61.2.devops-vi
  • 61.3.devops-user
  • 61.4.devops-auth
  • 61.5.devops-shell
  • 61.6.devops-install
  • 61.7.devops-system
  • 61.8.devops-service
  • 61.9.devops-network
  • 61.10.devops-nginx
  • 61.11.devops-docker
  • 61.12.devops-jekins
  • 61.13.devops-groovy
  • 61.14.devops-php
  • 61.15.devops-java
  • 61.16.devops-node
  • 61.17.devops-k8s
  • 62.1.react-basic
  • 62.2.react-state
  • 62.3.react-high
  • 62.4.react-optimize
  • 62.5.react-hooks
  • 62.6.react-immutable
  • 62.7.react-mobx
  • 62.8.react-source
  • 63.1.redux
  • 63.2.redux-middleware
  • 63.3.redux-hooks
  • 63.4.redux-saga
  • 63.5.redux-saga-hand
  • 64.1.router
  • 64.2.router-connected
  • 65.1.typescript
  • 65.2.typescript
  • 65.3.typescript
  • 65.4.antd
  • 65.4.definition
  • 66-1.vue-base
  • 66-2.vue-component
  • 66-3.vue-cli3.0
  • 66-4.$message组件
  • 66-5.Form组件
  • 66-6.tree
  • 66-7.vue-router-apply
  • 66-8.axios-apply
  • 66-9.vuex-apply
  • 66-10.jwt-vue
  • 66-11.vue-ssr
  • 66-12.nuxt-apply
  • 66-13.pwa
  • 66-14.vue单元测试
  • 66-15.权限校验
  • 67-1-network
  • 68-2-wireshark
  • 7.npm2
  • 69-hooks
  • 70-deploy
  • 71-hmr
  • 72.deploy
  • 73.import
  • 74.mobile
  • 75.webpack-1.文件分析
  • 75.webpack-2.loader
  • 75.webpack-3.源码流程
  • 75.webpack-4.tapable
  • 75.webpack-5.prepare
  • 75.webpack-6.resolve
  • 75.webpack-7.loader
  • 75.webpack-8.module
  • 75.webpack-9.chunk
  • 75.webpack-10.asset
  • 75.webpack-11.实现
  • 76.react_optimize
  • 77.ts_ketang_back
  • 77.ts_ketang_front
  • 78.vue-domdiff
  • 79.grammar
  • 80.tree
  • 81.axios
  • 82.1.react
  • 82.2.react-high
  • 82.3.react-router
  • 82.4.redux
  • 82.5.redux_middleware
  • 82.6.connected
  • 82.7.saga
  • 82.8.dva
  • 82.8.dva-source
  • 82.9.roadhog
  • 82.10.umi
  • 82.11.antdesign
  • 82.12.ketang-front
  • 82.12.ketang-back
  • 83.upload
  • 84.graphql
  • 85.antpro
  • 86.1.uml
  • 86.2.design
  • 87.postcss
  • 88.react16-1
  • 89.nextjs
  • 90.react-test
  • 91.react-ts
  • 92.rbac
  • 93.tsnode
  • 94.1.JavaScript
  • 94.2.JavaScript
  • 94.3.MODULE
  • 94.4.EventLoop
  • 94.5.文件上传
  • 94.6.https
  • 94.7. nginx
  • 95.1. react
  • 95.2.react
  • 96.1.react16
  • 96.2.fiber
  • 96.3.fiber
  • 97.serverless
  • 98.websocket
  • 100.1.react-basic
  • 101.1.monitor
  • 101.2.monitor
  • 102.java
  • 103.1.webpack-usage
  • 103.2.webpack-bundle
  • 103.3.webpack-ast
  • 103.4.webpack-flow
  • 103.5.webpack-loader
  • 103.6.webpack-tapable
  • 103.7.webpack-plugin
  • 103.8.webpack-optimize1
  • 103.9.webpack-optimize2
  • 103.10.webpack-hand
  • 103.11.webpack-hmr
  • 103.11.webpack5
  • 103.13.splitChunks
  • 103.14.webpack-sourcemap
  • 103.15.webpack-compiler1
  • 103.15.webpack-compiler2
  • 103.16.rollup.1
  • 103.16.rollup.2
  • 103.16.rollup.3
  • 103.16.vite.basic
  • 103.16.vite.source
  • 103.16.vite.plugin
  • 103.16.vite.1
  • 103.16.vite.2
  • 103.17.polyfill
  • 104.1.binary
  • 104.2.binary
  • 105.skeleton
  • 106.1.react
  • 106.2.react_hooks
  • 106.3.react_router
  • 106.4.redux
  • 106.5.redux_middleware
  • 106.6.connected-react-router
  • 106.6.redux-first-history
  • 106.7.redux-saga
  • 106.8.dva
  • 106.9.umi
  • 106.10.ketang
  • 106.11.antdesign
  • 106.12.antpro
  • 106.13.router-6
  • 106.14.ssr
  • 106.15.nextjs
  • 106.16.1.cms
  • 106.16.2.cms
  • 106.16.3.cms
  • 106.16.4.cms
  • 106.16.mobx
  • 106.17.fomily
  • 107.fiber
  • 108.http
  • 109.1.webpack_usage
  • 109.2.webpack_source
  • 109.3.dll
  • 110.nest.js
  • 111.xstate
  • 112.Form
  • 113.redux-saga
  • 114.react+typescript
  • 115.immer
  • 116.pro5
  • 117.css-loader
  • 118.1.umi-core
  • 119.2.module-federation
  • 119.1.module-federation
  • 120.create-react-app
  • 121.react-scripts
  • 122.react-optimize
  • 123.jsx-runtime
  • 124.next.js
  • 125.1.linux
  • 125.2.linux-vi
  • 125.3.linux-user
  • 125.4.linux-auth
  • 125.5.linux-shell
  • 125.6.linux-install
  • 125.7.linux-system
  • 125.8.linux-service
  • 125.9.linux-network
  • 125.10.nginx
  • 125.11.docker
  • 125.12.ci
  • 125.13.k8s
  • 125.14.k8s
  • 125.15.k8s
  • 125.16.k8s
  • 126.11.react-1
  • 126.12.react-2
  • 126.12.react-3
  • 126.12.react-4
  • 126.12.react-5
  • 126.12.react-6
  • 126.12.react-7
  • 126.12.react-8
  • 127.frontend
  • 128.rollup
  • 129.px2rem-loader
  • 130.health
  • 131.hooks
  • 132.keepalive
  • 133.vue-cli
  • 134.react18
  • 134.2.react18
  • 134.3.react18
  • 135.function
  • 136.toolkit
  • 137.lerna
  • 138.create-vite
  • 139.cli
  • 140.antd
  • 141.react-dnd
  • 142.1.link
  • 143.1.gulp
  • 143.2.stream
  • 143.3.gulp
  • 144.1.closure
  • 144.2.v8
  • 144.3.gc
  • 145.react-router-v6
  • 146.browser
  • 147.lighthouse
  • 148.1.basic
  • 148.2.basic
  • 148.3.basic
  • 148.4.basic
  • 148.5.basic
  • 149.1.vite
  • 149.2.vite
  • 149.3.vite
  • 149.4.vite
  • 150.react-window
  • 151.react-query
  • 152.useRequest
  • 153.transition
  • 154.emotion
  • 155.1.formily
  • 155.2.formily
  • 155.3.formily
  • 155.3.1.mobx.usage
  • 155.3.2.mobx.source
  • 156.vue-loader
  • 103.11.mf
  • 157.1.react18
  • 158.umi4
  • 159.rxjs
  • 159.rxjs2
  • 160.bff
  • 161.zustand
  • 162.vscode
  • 163.emp
  • 164.cors
  • 0.HTTPS简介
    • 0.1. SSL和TLS
    • 0.2. HTTPS
  • 1. 加密
    • 1.1 对称加密
      • 1.1.1 描述
      • 1.1.2 简单实现
      • 1.1.3 AES
    • 1.2 非对称加密
      • 1.2.1 单向函数
      • 1.2.2 RSA加密算法
      • 1.2.3 RSA加密
    • 1.3 哈希
      • 1.3.1 哈希函数
      • 1.3.2 哈希碰撞
      • 1.3.3 哈希分类
      • 1.3.4 hash使用
        • 1.3.4.1 简单哈希
        • 1.3.4.2 md5
        • 1.3.4.3 sha256
    • 1.4 数字签名
    • 1.5 数字证书
      • 1.5.1 数字证书原理
    • 1.6 Diffie-Hellman算法
      • 1.6.1 Diffie-Hellman实现
      • 1.6.2 Diffie-Hellman算法
    • 1.7 ECC
      • 1.7.1 ECC原理
      • 1.7.2 ECC使用
  • 2. UDP服务器
    • 2.1 createCA.js
    • 2.2 ca.js
    • 2.3 utils.js
    • 2.4 udp_server.js
    • 2.5 udp_client.js
  • 3. 数字证书实战
    • 3.1 自建CA
    • 3.2 生成服务器CA证书
    • 3.3 服务端
    • 3.4 客户端

0.HTTPS简介 #

0.1. SSL和TLS #

  • 传输层安全性协议(Transport Layer Security,缩写TLS),及其前身安全套接层(Secure Sockets Layer,缩写SSL)是一种安全协议,目的是为互联网通信,提供安全及数据完整性保障

https_http

0.2. HTTPS #

  • HTTPS 协议的主要功能基本都依赖于 TLS/SSL 协议,TLS/SSL 的功能实现主要依赖于三类基本算法
    • 散列函数 散列函数验证信息的完整性
    • 对称加密 对称加密算法采用协商的密钥对数据加密
    • 非对称加密 非对称加密实现身份认证和密钥协商

tls_ssl

1. 加密 #

  • 加密就是研究如何安全通信的
  • 保证数据在传输过程中不会被窃听
  • crypto

1.1 对称加密 #

  • 对称加密是最快速、最简单的一种加密方式,加密(encryption)与解密(decryption)用的是同样的密钥(secret key)
  • 主流的有AES和DES

1.1.1 描述 #

1.1.2 简单实现 #

  • 消息 abc
  • 密钥 3
  • 密文 def

let secretKey = 3;
function encrypt(str) {
    let buffer = Buffer.from(str);
    for (let i = 0; i < buffer.length; i++) {
        buffer[i] = buffer[i] + secretKey;
    }
    return buffer.toString();
}
function decrypt(str) {
    let buffer = Buffer.from(str);
    for (let i = 0; i < buffer.length; i++) {
        buffer[i] = buffer[i] - secretKey;
    }
    return buffer.toString();
}
let message = 'abc';
let secret = encrypt(message);
console.log(secret);
let value = decrypt(secret);
console.log(value);

1.1.3 AES #

  • crypto.html
    • algorithm用于指定加密算法,如aes-128-ecb、aes-128-cbc等
    • key是用于加密的密钥
    • iv参数用于指定加密时所用的向量
  • 如果加密算法是128,则对应的密钥是16位,加密算法是256,则对应的密钥是32位

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);

1.2 非对称加密 #

  • 互联网上没有办法安全的交换密钥

1.2.1 单向函数 #

  • 单向函数顺向计算起来非常的容易,但求逆却非常的困难。也就是说,已知x,我们很容易计算出f(x)。但已知f(x),却很难计算出x
  • 整数分解又称素因数分解,是将一个正整数写成几个约数的乘积
  • 给出45这个数,它可以分解成 9×5,这样的分解结果应该是独一无二的

1.2.2 RSA加密算法 #

let p = 3, q = 11;//计算完立刻销毁
let N = p * q;
let fN = (p - 1) * (q - 1);//欧拉函数
let e = 7;
for (var d = 1; e * d % fN !== 1; d++) {//拓展欧几里得算法
    d++;
}
//d=3
let publicKey = { e, N };
let privateKey = { d, N };

function encrypt(data) {
    return Math.pow(data, publicKey.e) % publicKey.N;
}
function decrypt(data) {
    return Math.pow(data, privateKey.d) % privateKey.N;
}
let data = 5;
let secret = encrypt(data);
console.log(secret);//14

let _data = decrypt(secret);
console.log(_data);//5
// 1024位二进制数分解
/**
公开 N e c
私密 d
e * d % fN == 1
(p - 1) * (q - 1)
N = p * q
*/

1.2.3 RSA加密 #

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';
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'));

1.3 哈希 #

  • hash 切碎的食物

1.3.1 哈希函数 #

  • 哈希函数的作用是给一个任意长度的数据生成出一个固定长度的数据
  • 安全性 可以从给定的数据X计算出哈希值Y,但不能从哈希值Y计算机数据X
  • 独一无二 不同的数据一定会产出不同的哈希值
  • 长度固定 不管输入多大的数据,输出长度都是固定的

1.3.2 哈希碰撞 #

  • 所谓哈希(hash),就是将不同的输入映射成独一无二的、固定长度的值(又称"哈希值")。它是最常见的软件运算之一
  • 如果不同的输入得到了同一个哈希值,就发生了哈希碰撞(collision)
  • 防止哈希碰撞的最有效方法,就是扩大哈希值的取值空间
  • 16个二进制位的哈希值,产生碰撞的可能性是 65536 分之一。也就是说,如果有65537个用户,就一定会产生碰撞。哈希值的长度扩大到32个二进制位,碰撞的可能性就会下降到 4,294,967,296 分之一
console.log(Math.pow(2, 16));//65536
console.log(Math.pow(2, 32));//42亿

1.3.3 哈希分类 #

  • 哈希还可以叫摘要(digest)、校验值(chunkSum)和指纹(fingerPrint)
  • 如果两段数据完全一样,就可以证明数据是一样的
  • 哈希有二种
    • 普通哈希用来做完整性校验,流行的是MD5
    • 加密哈希用来做加密,目前最流行的加密算法是 SHA256( Secure Hash Algorithm) 系列

1.3.4 hash使用 #

1.3.4.1 简单哈希 #
function hash(input) {
    return input % 1024;
}
let r1 = hash(100);
let r2 = hash(1124);
console.log(r1, r2);
1.3.4.2 md5 #
var crypto = require('crypto');
var content = '123456';
var result = crypto.createHash('md5').update(content).digest("hex")
console.log(result);//32位十六进制 = 128位二进制
1.3.4.3 sha256 #
const salt = '123456';
const sha256 = str => crypto.createHmac('sha256', salt)
    .update(str, 'utf8')
    .digest('hex')

let ret = sha256(content);
console.log(ret);//64位十六进制 = 256位二进制

1.4 数字签名 #

  • 数字签名的基本原理是用私钥去签名,而用公钥去验证签名

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');
}

1.5 数字证书 #

  • 数字证书是一个由可信的第三方发出的,用来证明所有人身份以及所有人拥有某个公钥的电子文件

1.5.1 数字证书原理 #

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');
}

1.6 Diffie-Hellman算法 #

  • Diffie-Hellman算法是一种密钥交换协议,它可以让双方在不泄漏密钥的情况下协商出一个密钥来

1.6.1 Diffie-Hellman实现 #

let N = 23;
let p = 5;
let secret1 = 6;//这是密钥
let A = Math.pow(p, secret1) % N;//8
console.log('p=', p, 'N=', N, 'A=', A);

let secret2 = 15;
let B = Math.pow(p, secret2) % N;//19
console.log('p=', p, 'N=', N, 'B=', B);

console.log(Math.pow(B, secret1) % N);
console.log(Math.pow(A, secret2) % N);

1.6.2 Diffie-Hellman算法 #

const { createDiffieHellman } = require('crypto');

var client = createDiffieHellman(512);
var client_keys = client.generateKeys();

var prime = client.getPrime();
var generator = client.getGenerator();

var server = createDiffieHellman(prime, generator);
var server_keys = server.generateKeys();

var client_secret = client.computeSecret(server_keys);
var server_secret = server.computeSecret(client_keys);

console.log('client_secret: ' + client_secret.toString('hex'));
console.log('server_secret: ' + server_secret.toString('hex'));

1.7 ECC #

  • 椭圆曲线加密算法(ECC) 是基于椭圆曲线数学的一种公钥加密的算法

1.7.1 ECC原理 #

let G = 3;
let a = 5;
let A = G * a;
let b = 7;
let B = G * b;
console.log(a * B);
console.log(b * A);

1.7.2 ECC使用 #

let { createECDH } = require('crypto');
const clientDH = createECDH('secp521r1');
const clientDHParams = clientDH.generateKeys();

const serverDH = createECDH('secp521r1');
const serverDHParams = serverDH.generateKeys();

const clientKey = clientDH.computeSecret(serverDHParams);
const serverKey = serverDH.computeSecret(clientDHParams);
console.log('clientKey', clientKey.toString('hex'));
console.log('serverKey', serverKey.toString('hex'));

2. UDP服务器 #

wiresharkhttp

wireshark

tls and (ip.src == 47.111.100.159 or ip.dst == 47.111.100.159)

2.1 createCA.js #

const ca_passphrase = 'ca';
let CA = generateKeyPairSync('rsa', {
    modulusLength: 1024,
    publicKeyEncoding: {
        type: 'spki',
        format: 'pem'
    },
    privateKeyEncoding: {
        type: 'pkcs8',
        format: 'pem',
        cipher: 'aes-256-cbc',
        passphrase: ca_passphrase
    }
});
let fs = require('fs');
let path = require('path');
fs.writeFileSync(path.resolve(__dirname, 'CA.publicKey'), CA.publicKey);
fs.writeFileSync(path.resolve(__dirname, 'CA.privateKey'), CA.privateKey);

2.2 ca.js #

const { createHash } = require('crypto');
const { getSign } = require('./utils');
const ca_passphrase = 'ca';
const fs = require('fs');
const path = require('path');
let cAPrivateKey = fs.readFileSync(path.resolve(__dirname, 'CA.privateKey'), 'utf8');
function requestCert(info) {
    const infoHash = createHash('sha256').update(JSON.stringify(info)).digest('hex');
    const sign = getSign(infoHash, cAPrivateKey, ca_passphrase);
    return { info, sign };
}
exports.requestCert = requestCert;

2.3 utils.js #

const { createCipheriv, createDecipheriv, createSign, createVerify } = require('crypto');
function encrypt(data, key) {
    let decipher = createCipheriv('aes-256-cbc', key, '1234567890123456');
    decipher.update(data);
    return decipher.final('hex');
}

function decrypt(data, key) {
    let decipher = createDecipheriv('aes-256-cbc', key, '1234567890123456');
    decipher.update(data, 'hex');
    return decipher.final('utf8');
}
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');
}
module.exports = {
    encrypt, decrypt, getSign, verifySign
}

2.4 udp_server.js #

const dgram = require('dgram')
const udp_server = dgram.createSocket('udp4')
const protocol = require('./protocol');
const { generateKeyPairSync, randomBytes, createHash, createECDH } = require('crypto');
const server_passphrase = 'server';
const { getSign, decrypt, encrypt } = require('./utils');
const { requestCert } = require('./ca');

let serverRSA = generateKeyPairSync('rsa', {
    modulusLength: 1024,
    publicKeyEncoding: {
        type: 'spki',
        format: 'pem'
    },
    privateKeyEncoding: {
        type: 'pkcs8',
        format: 'pem',
        cipher: 'aes-256-cbc',
        passphrase: server_passphrase
    }
});
let serverRandom = randomBytes(8).toString('hex');
const serverInfo = {
    domain: "http://127.0.0.1:20000",
    publicKey: serverRSA.publicKey
};
let serverCert = requestCert(serverInfo);
let clientRandom;
const serverDH = createECDH('secp521r1');
const ecDHServerParams = serverDH.generateKeys().toString('hex');
const ecDHServerParamsSign = getSign(ecDHServerParams, serverRSA.privateKey, server_passphrase);
let masterKey;
let sessionKey;
udp_server.on('listening', () => {
    const address = udp_server.address();
    console.log(`client running ${address.address}: ${address.port}`)
})
udp_server.on('message', (data, remote) => {
    let message = JSON.parse(data);
    switch (message.type) {
        case protocol.ClientHello:
            //2.在服务器生成随机数,通过ServerHello发送给客户端
            clientRandom = message.clientRandom;
            udp_server.send(JSON.stringify({
                type: protocol.ServerHello,
                serverRandom,//服务器端随机数
                cipherSuite: 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA'//约定的加密套件
            }), remote.port, remote.address);
            //3.Certificate 服务器把包含自己公钥的证书发送给客户端进行验证
            udp_server.send(JSON.stringify({
                type: protocol.Certificate,
                serverCert,//服务器公钥证书
            }), remote.port, remote.address);
            //4.ServerKeyExchange 服务器端生成DH参数,并用服务器私钥进行签名发给客户端
            udp_server.send(JSON.stringify({
                type: protocol.ServerKeyExchange,
                ecDHServerParams,
                ecDHServerParamsSign
            }), remote.port, remote.address);
            //5.Server Hello Done 服务器发送完成
            udp_server.send(JSON.stringify({
                type: protocol.ServerHelloDone
            }), remote.port, remote.address);
            break;
        case protocol.ClientKeyExchange:
            //6.ClientKeyExchange 服务器收到客户端DH参数后加上服务器DH参数生成pre-master-key
            //再由pre-master-key生成masterKey和sessionKey
            let { ecDHClientParams } = message;
            preMasterKey = serverDH.computeSecret(Buffer.from(ecDHClientParams, 'hex')).toString('hex');
            masterKey = createHash('md5').update(preMasterKey + clientRandom + serverRandom).digest('hex');
            sessionKey = createHash('md5').update(masterKey + clientRandom + serverRandom).digest('hex');
            break;
        case protocol.ChangeCipherSpec:
            //9.服务器通知客户端服务器也已经准备好切换加密套件了
            udp_server.send(JSON.stringify({
                type: protocol.ChangeCipherSpec
            }), remote.port, remote.address);
            break;
        case protocol.EncryptedHandshakeMessage:
            console.log("服务器收到解密后的数据:", decrypt(message.data, sessionKey));
            //10.服务器收到客户端的加密数据后向客户端回复加密数据
            udp_server.send(JSON.stringify({
                type: protocol.EncryptedHandshakeMessage,
                data: encrypt("i am server", sessionKey)
            }), remote.port, remote.address);
            break;
        default:
            break;
    }

})
udp_server.on('error', (error) => {
    console.log(error);
});
udp_server.bind(20000, '127.0.0.1');

2.5 udp_client.js #

const dgram = require('dgram')
const udp_client = dgram.createSocket('udp4')
const { randomBytes, createHash, createECDH } = require('crypto');
const { verifySign, encrypt, decrypt } = require('./utils');
const url = require('url');
const protocol = require('./protocol');
const fs = require('fs');
const path = require('path');
const cAPublicKey = fs.readFileSync(path.resolve(__dirname, 'CA.publicKey'), 'utf8');
const clientRandom = randomBytes(8).toString('hex');
let serverRandom;
let serverPublicKey;
let ecDHServerParams;
let clientDH = createECDH('secp521r1');
let ecDHClientParams = clientDH.generateKeys();
let masterKey;
let sessionKey;
udp_client.on('listening', () => {
    const address = udp_client.address();
    console.log(`client running ${address.address}: ${address.port}`)
})
udp_client.on('message', (data, remote) => {
    let message = JSON.parse(data.toString('utf8'));
    switch (message.type) {
        case protocol.ServerHello:
            serverRandom = message.serverRandom;
            break;
        case protocol.Certificate:
            //3.Certificate 客户收到服务器证书后会用CA的公钥进行验证证书是否合法
            let { serverCert } = message;
            let { info, sign } = serverCert;
            serverPublicKey = info.publicKey;
            const serverInfoHash = createHash('sha256').update(JSON.stringify(info)).digest('hex');
            let serverCertIsValid = verifySign(serverInfoHash, sign, cAPublicKey);
            console.log('验证服务器端证书是否正确?', serverCertIsValid);
            let urlObj = url.parse(info.domain);
            let serverDomainIsValid = urlObj.hostname === remote.address && urlObj.port == remote.port;
            console.log('验证服务器端域名正确?', serverDomainIsValid);
            break;
        case protocol.ServerKeyExchange:
            //4.ServerKeyExchange 客户端收到服务器的DH参数和参数签名后会用服务器的公钥进行签名,验证服务器拥有私钥
            ecDHServerParams = message.ecDHServerParams;
            ecDHServerParamsSign = message.ecDHServerParamsSign;
            let serverDHParamIsValid = verifySign(ecDHServerParams, ecDHServerParamsSign, serverPublicKey);
            console.log('验证服务器端证书DH参数是否正确?', serverDHParamIsValid);
            break;
        case protocol.ServerHelloDone:
            //6.ClientKeyExchange 客户端生成DH参数并且发给服务器
            udp_client.send(JSON.stringify({
                type: protocol.ClientKeyExchange,
                ecDHClientParams
            }), remote.port, remote.address);
            //6.ClientKeyExchange 服务器收到客户端DH参数后加上服务器DH参数生成pre-master-key
            //再由pre-master-key生成masterKey和sessionKey
            preMasterKey = clientDH.computeSecret(Buffer.from(ecDHServerParams, 'hex')).toString('hex');
            masterKey = createHash('md5').update(preMasterKey + clientRandom + serverRandom).digest('hex');
            sessionKey = createHash('md5').update(masterKey + clientRandom + serverRandom).digest('hex');
            //7.Change Cipher Spec 通知服务器客户端已经准备好切换成加密通信了
            udp_client.send(JSON.stringify({
                type: protocol.ChangeCipherSpec
            }), remote.port, remote.address);
            //8.加密握手信息并传送给服务器端
            udp_client.send(JSON.stringify({
                type: protocol.EncryptedHandshakeMessage,
                data: encrypt("i am client", sessionKey)
            }), remote.port, remote.address);
            break;
        case protocol.EncryptedHandshakeMessage:
            //10.客户端你好到服务器的加密握手数据
            //这个报文的目的就是告诉对端自己在整个握手过程中收到了什么数据,发送了什么数据。来保证中间没人篡改报文
            console.log("客户端收到解密后的数据:", decrypt(message.data, sessionKey));
            break;
        default:
            break;
    }
})
udp_client.on('error', (error) => {
    console.log(error);
});
//1.ClientHello 客户端向服务器发送客户端随机数,服务器需要保存在服务器端
udp_client.send(JSON.stringify({
    type: protocol.ClientHello,
    clientRandom
}), 20000, '127.0.0.1');

3. 数字证书实战 #

  • OpenSSL
  • WinOpenSSL
  • 安装后要添加环境变量 C:\Program Files\OpenSSL-Win64\bin

3.1 自建CA #

// 1.生成CA私匙
openssl genrsa -des3 -out ca.private.pem 1024
// 2.生成CA证书请求
openssl req -new -key ca.private.pem -out ca.csr
// 3.生成CA根证书
openssl x509 -req -in ca.csr -extensions v3_ca -signkey ca.private.pem -out ca.crt

3.2 生成服务器CA证书 #

// 1.生成server私匙
openssl genrsa -out server.private.pem 1024
// 2.生成server证书请求
openssl req -new -key server.private.pem -out server.csr
// 3.生成server证书
openssl x509 -days 365 -req -in server.csr -extensions  v3_req -CAkey  ca.private.pem -CA ca.crt -CAcreateserial -out server.crt  -extfile openssl.cnf

openssl.cnf

[req]  
    distinguished_name = req_distinguished_name  
    req_extensions = v3_req  
    [req_distinguished_name]  
    countryName = CN 
    countryName_default = CN  
    stateOrProvinceName = Beijing  
    stateOrProvinceName_default = Beijing  
    localityName = Beijing 
    localityName_default = Beijing
    organizationalUnitName  = HD
    organizationalUnitName_default  = HD
    commonName = localhost  
    commonName_max  = 64  

    [ v3_req ]  
    # Extensions to add to a certificate request  
    basicConstraints = CA:FALSE  
    keyUsage = nonRepudiation, digitalSignature, keyEncipherment  
    subjectAltName = @alt_names  

    [alt_names]  
    #注意这个IP.1的设置,IP地址需要和你的服务器的监听地址一样 DNS为server网址,可设置多个ip和dns
    IP.1 = 127.0.0.1
    DNS.1 = localhost

3.3 服务端 #

const https = require('https');
const fs = require('fs');
const path = require('path');

const options = {
    key: fs.readFileSync(path.resolve(__dirname, 'ssl/server.private.pem')),
    cert: fs.readFileSync(path.resolve(__dirname, 'ssl/server.crt'))
};


https.createServer(options, (req, res) => {
    res.end('hello world\n');
}).listen(9000);

console.log("server https is running 9000");

3.4 客户端 #

const https = require('https');
const options = {
    hostname: '127.0.0.1',
    port: 9000,
    path: '/',
    method: 'GET',
    requestCert: true,  //请求客户端证书
    rejectUnauthorized: false, //不拒绝不受信任的证书
};

const req = https.request(options, (res) => {
    let buffers = [];
    res.on('data', (chunk) => {
        buffers.push(chunk);
    });
    res.on('end', () => {
        console.log(buffers.toString());
    });
});
req.end();

访问验证

请输入访问令牌

Token不正确,请重新输入