OpenSSL实现双向认证教程
此教程将指导如何使用OpenSSL实现双向认证,包含服务端与客户端代码。在本教程中,我们将学习:
- 什么是双向认证
- 生成RSA密钥对
- 生成自签名的根证书
- 生成服务器证书请求(CSR)
- 生成服务器证书
- 配置服务端
- 生成客户端证书请求(CSR)
- 生成客户端证书
- 配置客户端
- 测试双向认证
什么是双向认证
在SSL/TLS连接中,通常只有服务器需要验证,因为它提供了服务(例如网站)给客户端。但是,在某些情况下,需要对客户端进行验证(例如API或者是移动设备上的应用程序)。这时就需要使用双向认证,确保只有经过认证的客户端可以访问服务器提供的服务。
生成RSA密钥对
首先,需要生成RSA密钥对。我们将使用OpenSSL生成2048位的RSA密钥对。以下命令可以生成密钥对:
openssl genrsa -out rootCA.key 2048
生成自签名的根证书
使用生成的RSA密钥对,可以生成自签名的根证书。以下命令可以生成根证书和私钥:
openssl req -x509 -new -key rootCA.key -days 3650 -out rootCA.crt
其中,-x509
参数指定我们将要生成自签名证书,-new
参数表示新建证书,-days
参数指定证书的有效期天数,-out
参数指定输出文件。执行完该命令后,将生成名为rootCA.crt
的自签名根证书。
生成服务器证书请求(CSR)
使用以下命令生成服务器证书请求(CSR):
openssl req -new -key server.key -out server.csr
其中,-new
参数表示新建证书请求,-key
参数表示指定密钥文件,-out
参数指定输出文件。执行完该命令后,将生成一个名为server.csr
的证书请求文件。
生成服务器证书
使用以下命令生成服务器证书:
openssl x509 -req -in server.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out server.crt -days 3650
其中,-req
参数表示该证书是从证书请求生成的,-in
参数指定证书请求文件,-CA
参数指定根证书,-CAkey
指定RSA私钥,-CAcreateserial
参数表示自动创建序列号,-out
参数指定输出文件名,-days
参数指定证书的有效期天数。执行完该命令后,会生成一个名为server.crt
的服务器证书。
配置服务端
生成一个名为server.js
的Node.js文件,将以下代码添加到文件中:
const fs = require('fs');
const https = require('https');
const express = require('express');
const app = express();
const options = {
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.crt')
};
app.get('/', function(req, res) {
res.send('Hello World!');
});
https.createServer(options, app).listen(8000, function() {
console.log('Server started on port 8000.');
});
在代码运行之前,需要确保安装express
和https
模块。执行以下命令安装这两个依赖项:
npm install express https
要启动服务器,请运行以下命令:
node server.js
生成客户端证书请求(CSR)
使用以下命令生成客户端证书请求(CSR):
openssl req -new -key client.key -out client.csr
其中,-new
参数表示新建证书请求,-key
参数表示指定密钥文件,-out
参数指定输出文件。执行完该命令后,将生成一个名为client.csr
的证书请求文件。
生成客户端证书
使用以下命令生成客户端证书:
openssl x509 -req -in client.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out client.crt -days 3650
其中,-req
参数表示该证书是从证书请求生成的,-in
参数指定证书请求文件,-CA
参数指定根证书,-CAkey
指定RSA私钥,-CAcreateserial
参数表示自动创建序列号,-out
参数指定输出文件名,-days
参数指定证书的有效期天数。执行完该命令后,会生成一个名为client.crt
的客户端证书。
配置客户端
生成一个名为client.js
的Node.js文件,将以下代码添加到文件中:
const fs = require('fs');
const https = require('https');
const options = {
hostname: 'localhost',
port: 8000,
path: '/',
method: 'GET',
key: fs.readFileSync('client.key'),
cert: fs.readFileSync('client.crt'),
ca: fs.readFileSync('rootCA.crt')
};
https.request(options, function(res) {
console.log('statusCode:', res.statusCode);
console.log('headers:', res.headers);
res.on('data', function(d) {
process.stdout.write(d);
});
}).end();
在运行代码之前,确保已经生成client.key
和client.crt
文件。此外,还需要将rootCA.crt
文件放置到运行代码的目录中。
要运行客户端,执行以下命令:
node client.js
测试双向认证
为了测试双向认证是否成功工作,您应该分别启动客户端和服务器,然后检查服务器的输出以查看是否成功。在终端运行以下两个命令:
$ node server.js
Server started on port 8000.
$ node client.js
Hello World!
如果您看到“Hello World!”的消息,则表明双向认证已成功工作!
示例
以下是使用双向认证的API服务器端和移动应用程序客户端的示例说明。
API服务器端
您可以使用此代码在API服务器端上实现双向认证。
const fs = require('fs');
const https = require('https');
const express = require('express');
const app = express();
const options = {
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.crt'),
requestCert: true,
rejectUnauthorized: true,
ca: [ fs.readFileSync('client.crt') ]
};
app.get('/', function(req, res) {
res.send('Hello World!');
});
https.createServer(options, app).listen(8000, function() {
console.log('Server started on port 8000.');
});
在客户端证书中指定完整的路径,而不是只指定文件名,例如:
ca: [ fs.readFileSync('/certs/client.crt') ]
移动应用程序客户端
您可以使用此代码在移动应用程序中实现双向认证。
try {
// Create a KeyStore containing our trusted CA
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
InputStream is = context.getAssets().open("rootCA.crt");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate caCert = cf.generateCertificate(is);
keyStore.setCertificateEntry("caCert", caCert);
// Create a KeyStore containing our client certificate
KeyStore clientKeyStore = KeyStore.getInstance("PKCS12");
InputStream clientCert = context.getAssets().open("client.p12");
clientKeyStore.load(clientCert, "client_password".toCharArray());
// Create a TrustManager that trusts the CA in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// Create a KeyManager that uses our client certificate
String kmfAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
KeyManagerFactory kmf = KeyManagerFactory.getInstance(kmfAlgorithm);
kmf.init(clientKeyStore, "client_password".toCharArray());
// Create an SSLContext that uses our TrustManager and KeyManager
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
// Create an HTTPS connection and send a request
URL url = new URL("https://example.com/api");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(sslContext.getSocketFactory());
conn.setRequestMethod("GET");
conn.connect();
// Read the response
InputStream in = conn.getInputStream();
int bytesRead;
byte[] buffer = new byte[1024];
while ((bytesRead = in.read(buffer)) != -1) {
System.out.write(buffer, 0, bytesRead);
}
in.close();
} catch (Exception e) {
e.printStackTrace();
}
在客户端证书中指定完整的路径,而不是只指定文件名,例如:
InputStream clientCert = context.getAssets().open("client.p12");
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Openssl实现双向认证教程(附服务端客户端代码) - Python技术站