1) Создать "центр сертификации" (CA - Certificate Authority), который позволит создавать и проверять "самоподписанные сертфикаты" (Self-Signed SSL Certificate).
2) Создать "приватный ключ" (Private key) для подписи сертификатов.
3) С помощью "приватного ключа" (Private key) создать "запрос на подпись сертификата" (CSR - Certificate signing request).
4) С помощью "приватного ключа" (Private key) и "запроса на подпись сертификата" (CSR - Certificate signing request) создать сам "сертификат" (Cert - Certificate).
Создание "центра сертификации" (Certificate Authority).
Для упрощения процесса скопируйте эти настройки в файл "ca.cnf" и пропишите в них ваши значения.
[ ca ]
default_ca = CA_default
[ CA_default ]
serial = ca-serial
crl = ca-crl.pem
database = ca-database.txt
name_opt = CA_default
cert_opt = CA_default
default_crl_days = 9999
default_md = md5
[ req ]
default_bits = 4096
days = 9999
distinguished_name = req_distinguished_name
attributes = req_attributes
prompt = no
output_password = password
[ req_distinguished_name ]
C = US
ST = MA
L = Boston
O = Example Co
OU = techops
CN = ca
emailAddress = certs@example.com
[ req_attributes ]
challengePassword = test
Далее в командной строке вашего терминала выполните такую команду:
openssl req -new -x509 -days 9999 -config ca.cnf -keyout ca-key.pem -out ca-crt.pem
В результате вы создадите файлы "центра сертификации": ca-key.pem и ca-crt.pem.
Теперь создадим "приватный ключ" (Private key) для сервера Node.js с помощью следующей команды:
openssl genrsa -out server-key.pem 4096
В результате вы создадите файл server-key.pem.
Теперь создадим "запрос на подпись сертификата" (CSR - Certificate signing request).
Для упрощения процесса скопируйте эти настройки в файл "server.cnf" и пропишите в них ваши значения.
[ req ]
default_bits = 4096
days = 9999
distinguished_name = req_distinguished_name
attributes = req_attributes
prompt = no
x509_extensions = v3_ca
[ req_distinguished_name ]
C = US
ST = MA
L = Boston
O = Example Co
OU = techops
CN = localhost
emailAddress = certs@example.com
[ req_attributes ]
challengePassword = password
[ v3_ca ]
authorityInfoAccess = @issuer_info
[ issuer_info ]
OCSP;URI.0 = http://ocsp.example.com/
caIssuers;URI.0 = http://example.com/ca.cert
После этого выполните команду:
openssl req -new -config server.cnf -key server-key.pem -out server-csr.pem
В результате вы создадите файл server-csr.pem.
Далее подпишем "запрос на подпись сертификата" (CSR - Certificate signing request) нашим "приватным ключем" (Private key) для создания итогового "сертификата" (Cert - Certificate) нашего Node.js HTTPS, TLS/SSL сервера, выполнив команду:
openssl x509 -req -extfile server.cnf -days 999 -passin "pass:password" -in server-csr.pem -CA ca-crt.pem -CAkey ca-key.pem -CAcreateserial -out server-crt.pem
Обратите внимание на заданный в команде пароль: "pass:password".
В результате вы создадите файл server-crt.pem.
Теперь созданный сертификат готов для использования не сервере.
Создадим файл "server.js" и пропишем в нем данный код для создания HTTPS сервера:
var https = require('https');
var fs = require('fs');
var options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-crt.pem'),
ca: fs.readFileSync('ca-crt.pem')
};
https.createServer(options, function (req, res) {
console.log(new Date() + ' ' + req.connection.remoteAddress + ' ' + req.method + ' '+ req.url);
res.writeHead(200);
res.end("Hello world!");
}).listen(443, '127.0.0.1');
Проверим, как работает наш сервер при получения запроса от браузера, перейдя в браузере по адресу "127.0.0.1:443".
Не забудьте добавить в вашу операционную систему созданный нами "центр сертификации" (CA - Certificate Authority) путем установки файла "ca-crt.pem" и пометки его, как доверенного.
После проверки работы сервера через браузер создадим HTTPS-клиент для Node.js, который мог бы соединяться с нашим HTTPS-сервером c проверкой сертификата сервера через созданный нами "центр сертификации" (CA - Certificate Authority).
Создадим файл "client.js" и пропишем в нем данный код для создания HTTPS клиента:
var https = require('https');
var fs = require('fs');
var options = {
method: 'GET',
hostname: 'localhost',
port: 443,
path: '/',
ca: fs.readFileSync('ca-crt.pem')
};
var req = https.request(options, function (res) {
res.on('data', function (data) {
process.stdout.write(data);
});
});
req.end();
Обратите внимание, что в options мы добавили свойство "ca", в котором прописан наш файл "центра сертификации" (CA - Certificate Authority) для того, чтобы наш клиент мог доверять "публичному ключу" (Public key), который он получит от сервера при подключении к нему.
Теперь выполните код подключения клиента к серверу и в консоли командной строки вы увидите надпись: "Hello world!".
Далее создадим свой сертификат для клиента, чтобы он тоже мог с помощью него представляться серверу.
Мы сделаем два сертификата для того, чтобы иметь возможность делать запросы к серверу из двух разных клиентов.
Создадим каждому клиенту свой "приватный ключ" (Private key) командами:
openssl genrsa -out client1-key.pem 4096
openssl genrsa -out client2-key.pem 4096
В результате вы создадите файл client1-key.pem и файл client2-key.pem.
Далее создадим два "запроса на подпись сертификата" (CSR - Certificate signing request).
Для упрощения процесса скопируйте эти настройки в файл "client1.cnf" и пропишите в них ваши значения.
[ req ]
default_bits = 4096
days = 9999
distinguished_name = req_distinguished_name
attributes = req_attributes
prompt = no
x509_extensions = v3_ca
[ req_distinguished_name ]
C = US
ST = MA
L = Boston
O = Example Co
OU = techops
CN = client1
emailAddress = certs@example.com
[ req_attributes ]
challengePassword = password
[ v3_ca ]
authorityInfoAccess = @issuer_info
[ issuer_info ]
OCSP;URI.0 = http://ocsp.example.com/
caIssuers;URI.0 = http://example.com/ca.cert
А настройки ниже скопируйте в файл "client2.cnf" и пропишите в них ваши значения.
[ req ]
default_bits = 4096
days = 9999
distinguished_name = req_distinguished_name
attributes = req_attributes
prompt = no
x509_extensions = v3_ca
[ req_distinguished_name ]
C = US
ST = MA
L = Boston
O = Example Co
OU = techops
CN = client2
emailAddress = certs@example.com
[ req_attributes ]
challengePassword = password
[ v3_ca ]
authorityInfoAccess = @issuer_info
[ issuer_info ]
OCSP;URI.0 = http://ocsp.example.com/
caIssuers;URI.0 = http://example.com/ca.cert
"Запросы на подпись сертификата" (CSR - Certificate signing request) создадим с помощью пары команд:
openssl req -new -config client1.cnf -key client1-key.pem -out client1-csr.pem
openssl req -new -config client2.cnf -key client2-key.pem -out client2-csr.pem
В результате вы создадите файл client1-csr.pem и файл client2-csr.pem.
Теперь подпишем "запросы на подпись сертификата" (CSR - Certificate signing request) нашим "приватным ключем" (Private key) для создания итоговых "сертификатов" (Cert - Certificate) наших Node.js HTTPS, TLS/SSL клиентов, выполнив команду:
openssl x509 -req -extfile client1.cnf -days 999 -passin "pass:password" -in client1-csr.pem -CA ca-crt.pem -CAkey ca-key.pem -CAcreateserial -out client1-crt.pem
openssl x509 -req -extfile client2.cnf -days 999 -passin "pass:password" -in client2-csr.pem -CA ca-crt.pem -CAkey ca-key.pem -CAcreateserial -out client2-crt.pem
В результате вы создадите файл client1-crt.pem и файл client2-crt.pem.
Для того, чтобы убедиться, что мы все сделали верно проверим наши сертификаты, выполнив команды:
openssl verify -CAfile ca-crt.pem client1-crt.pem
openssl verify -CAfile ca-crt.pem client2-crt.pem
Такую же проверку вы можете сделать и для сертификата, который вы создали для сервера, выполнив команду:
openssl verify -CAfile ca-crt.pem server-crt.pem
Если вы все сделали верно, то вы получите "OK" в результате проверки.
Теперь напишем код сервера и клиента, использующего каждый свой сертификат.
Добавим в код сервера свойства requestCert и rejectUnauthorized, требующие, чтобы клиент всегда предоставлял серверу свой сертификат, изменив код в файле "server.js" так:
var https = require('https');
var fs = require('fs');
var options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-crt.pem'),
ca: fs.readFileSync('ca-crt.pem'),
requestCert: true,
rejectUnauthorized: true
};
https.createServer(options, function (req, res) {
console.log(new Date() + ' ' + req.connection.remoteAddress + ' ' + req.socket.getPeerCertificate().subject.CN + ' ' + req.method + ' ' + req.url);
res.writeHead(200);
res.end("Hello world!");
}).listen(443);
Теперь, если вы попробуйте подключиться к серверу из клиента, то подключение клиента будет отвергнуто сервером, поскольку в данный момент клиент еще не умеет передавать серверу свой сертификат.
В итоге Node.js выдаст сообщение об ошибке "socket hangup error" из-за того, что TLS не будет соблюдено.
Поэтому, чтобы все заработало мы добавим в код клиента в файле "client.js" опции key и cert так:
var https = require('https');
var fs = require('fs');
var options = {
method: 'GET',
hostname: 'localhost',
port: 443,
path: '/',
key: fs.readFileSync('client1-key.pem'),
cert: fs.readFileSync('client1-crt.pem'),
ca: fs.readFileSync('ca-crt.pem')
};
var req = https.request(options, function (res) {
res.on('data', function(data) {
process.stdout.write(data);
});
});
req.on('error', function(e) {
console.error(e);
});
req.end();
После этого выполните подключение клиента к серверу и сервер в ответ вернет "Hello world!".
В консоли сервера в этом случае вы увидите информации о подключении клиента и его "Common Name" из переданного серверу сертификата.
Поменяйте в коде клиента сертификат client1 на сертификат client2 и вы увидите, что клиент со вторым сертификатом также подключается к серверу.
Теперь проведем "отзыв сертификата" (CR - Certificate Revocation).
Для отзыва второго сертификата клиента мы создадим "список отозванных сертификатов" (CRL - Certificate Revocation List).
Чтобы это сделать создайте пустой текстовый файл "ca-database.txt", символизирующий базу данных.
Далее выполните команду для отзыва второго сертификата клиента client2-crt.pem:
openssl ca -revoke client2-crt.pem -keyfile ca-key.pem -config ca.cnf -cert ca-crt.pem -passin "pass:password"
Обратите внимание на заданный в команде пароль: "pass:password".
Обратите внимание на заданный в команде пароль: "pass:password".
После этого выполните команду для обновления "списка отозванных сертификатов" (CRL - Certificate Revocation List):
openssl ca -keyfile ca-key.pem -cert ca-crt.pem -config ca.cnf -gencrl -out ca-crl.pem -passin "pass:password"
Наконец добавьте в опции сервера свойство crl для получения доступа к "списку отозванных сертификатов" (CRL - Certificate Revocation List), изменив код так:
var https = require('https');
var fs = require('fs');
var options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-crt.pem'),
ca: fs.readFileSync('ca-crt.pem'),
crl: fs.readFileSync('ca-crl.pem'),
requestCert: true,
rejectUnauthorized: true
};
https.createServer(options, function (req, res) {
console.log(new Date()+ ' ' + req.connection.remoteAddress + ' ' + req.socket.getPeerCertificate().subject.CN + ' ' + req.method + ' ' + req.url);
res.writeHead(200);
res.end("Hello world!");
}).listen(443);
После этого сервер будет учитывать "список отозванных сертификатов" (CRL - Certificate Revocation List) и принимать запрос от клиента с сертификатом client1-crt.pem и отвергать запросы клиента с сертификатом client2-crt.pem.
Таким образом, мы научились создавать самоподписанные сертификаты для сервера и проверять сертификаты, приходящие от клиентов. Дополнительно к этому мы научились отзывать сертификаты.
Поскольку в сертификатах содержится имя клиента Common Name, то мы также можем использовать их для идентификации каждого клиента на нашем сервере.