远程开启硬件权限,会用到SSL证书。
以下是Win10系统下用OpenSSL生成测试用证书的步骤。
Step 1. 下载OpenSSL,一般选择64位的MSI
Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions
一路点下来,如果后续请你捐款,可以不选择。
win10下很可能的安装路径为: C:\Program Files\OpenSSL-Win64
Step 2. 将 C:\Program Files\OpenSSL-Win64\bin这个路径添加到系统环境变量中。
Step 3. 新建一个目录,例如我的: D:\dev\openssl\
新建一个文件夹是防止系统环境下有读写权限限制问题。
Step 4. 在这个目录下新建一个 openssl.cnf 文件保存为utf-8格式。
文件内容为:
# # OpenSSL configuration file. # # Establish working directory. dir = . [ ca ] default_ca = CA_default [ CA_default ] serial = $dir/serial database = $dir/certindex.txt new_certs_dir = $dir/certs certificate = $dir/cacert.pem private_key = $dir/private/cakey.pem default_days = 365 default_md = md5 preserve = no email_in_dn = no nameopt = default_ca certopt = default_ca policy = policy_match [ policy_match ] countryName = match stateOrProvinceName = match organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional [ req ] default_bits = 1024 # Size of keys default_keyfile = key.pem # name of generated keys default_md = md5 # message digest algorithm string_mask = nombstr # permitted characters distinguished_name = req_distinguished_name req_extensions = v3_req [ req_distinguished_name ] # Variable name Prompt string #------------------------- ---------------------------------- 0.organizationName = Organization Name (company) organizationalUnitName = Organizational Unit Name (department, division) emailAddress = Email Address emailAddress_max = 40 localityName = Locality Name (city, district) stateOrProvinceName = State or Province Name (full name) countryName = Country Name (2 letter code) countryName_min = 2 countryName_max = 2 commonName = Common Name (hostname, IP, or your name) commonName_max = 64 # Default values for the above, for consistency and less typing. # Variable name Value #------------------------ ------------------------------ 0.organizationName_default = My Company localityName_default = My Town stateOrProvinceName_default = State or Providence countryName_default = US [ v3_ca ] basicConstraints = CA:TRUE subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer:always [ v3_req ] basicConstraints = CA:FALSE subjectKeyIdentifier = hash
感谢: Unable to load config info from /usr/local/ssl/openssl.cnf on Windows - Stack Overflow
Step 5. 在新建的D:\dev\openssl\文件夹下,打开cmd窗口,设置openssl.cnf路径环境变量,命令如下:
set OPENSSL_CONF=D:\dev\openssl\openssl.cnf
如果没有正确指定这个环境变量,则会报如下错误:
Unable to load config info from /z/extlib/_openssl_/ssl/openssl.cnf
Step 6. 在命令行中创建privateKey.pem
openssl.exe genrsa -out privateKey.pem 4096
执行成功,打印如下:
Generating RSA private key, 4096 bit long modulus ..............................................................................................................................................++ ............................................................................++ e is 65537 (0x10001)
Step7. 生成证书,命令如下:
openssl.exe req -new -x509 -nodes -days 3600 -key privateKey.pem -out caKey.pem
会提示你输入组织名称,email地址,联系地址、所属国家等信息,正常输入就ok了。
如果没有正确生成 privateKey.pem或者找不到这个文件,则会报错:
req: Can't open "privateKey.key" for writing, Permission denied
Step 8. 恭喜,搞定。
Step 9. 在用NodeJS写一个简单的https Server试试。代码如下:
// server.js const https = require('https'); const fs = require('fs'); const options = { key: fs.readFileSync('privateKey.pem'), cert: fs.readFileSync('caKey.pem') }; const app = function (req, res) { res.writeHead(200); res.end("hello world\n"); } https.createServer(options, app).listen(9000);
Step 10. 在浏览器中输入 https://localhost:9000/就能访问。如果是chrome浏览器,会提示这是不安全链接,需要你在当前页面里点击高级,然后选择继续访问。成功访问的话,会在页面中显示:
hello world
Step 11. 再来一个功能更丰富的Sever。
const https = require('https'); const fs = require('fs'); const path = require('path'); const options = { key: fs.readFileSync('privateKey.pem'), cert: fs.readFileSync('./caKey.pem') }; var serverPort = 9100; https.createServer(options, (req, res) => { const filePath = '.' + req.url; const extname = path.extname(filePath); let contentType = 'text/html'; switch (extname) { case '.js': contentType = 'text/javascript'; break; case '.css': contentType = 'text/css'; break; case '.json': contentType = 'application/json'; break; case '.png': contentType = 'image/png'; break; case '.jpg': contentType = 'image/jpg'; break; case '.wav': contentType = 'audio/wav'; break; } fs.readFile(filePath, (error, content) => { if (error) { if (error.code == 'ENOENT') { fs.readFile('./404.html', (error, content) => { res.writeHead(200, { 'Content-Type': contentType }); res.end(content, 'utf-8'); }); } else { res.writeHead(500); res.end('Sorry, check with the site admin for error: ' + error.code + ' ..\n'); res.end(); } } else { res.writeHead(200, { 'Content-Type': contentType }); res.end(content, 'utf-8'); } }); }).listen(serverPort); console.log(`Server running at https://127.0.0.1:${serverPort}/`);