Самозаверяющая аутентификация клиента сертификата с клиентом и сервером как на локальном хосте - PullRequest
0 голосов
/ 08 января 2019

У меня есть узел / экспресс-сервер, работающий по протоколу https на localhost: 9002, и я хочу использовать сертификат клиента для приложения реагирования, работающего на localhost: 8080 (сервер webpack dev). Приложение реагирования использует ajax-запрос с суперагентом к серверу https, и у меня есть промежуточное ПО для паспорта для автоматической проверки сертификата.

Окружающая среда

Windows 10, версия Chrome 71.0.3578.98

Настройка

Используя openssl, я создал корневой CA. Затем я сгенерировал свой сертификат сервера и сертификат клиента. Это используемый скрипт (я запускаю его с помощью git bash, так что это стиль UNIX, но я на Windows):

## CREATE CERTIFICATES FOR AUTHENTICATION

#########################################
## 1. Create Root Certificate Authority #
#########################################
# Root CA private key
openssl genrsa -out ./rootCA.key 4096
# Root CA certificate to register in RootCA store on OS
openssl req -x509 -new -nodes -key ./rootCA.key -sha256 -days 3650 -out ./rootCA.pem

#################################
## 2. Create Server certificate #
#################################
# Create private key for server
openssl genrsa -out ./server.key 4096
# Create server certificate sign request (CSR) based on the private key
openssl req -new -sha256 -nodes -out ./server.csr -key ./server.key -config ./server.csr.conf
# Create server certificate linked to the previoulsy created Root CA
openssl x509 -req -in ./server.csr -CA ./rootCA.pem -CAkey ./rootCA.key -CAcreateserial -out ./server.crt -days 3650 -sha256 -extfile ./v3.ext

#################################
## 3. Create Client certificate #
#################################
# Create private key for client
openssl genrsa -out ./client.key 4096

# Create the Certificate Sign Request (CSR) file from the client private key
openssl req -new -config ./client.csr.conf -key ./client.key -out ./client.csr

# Self sign the certificate for 10 years
openssl x509 -req -days 3650 -in ./client.csr -CA ./server.crt -CAkey ./server.key -CAcreateserial -out ./client.crt

# Display the fingerprint of the newly generated fingerprint
openssl x509 -noout -fingerprint -inform pem -in ./client.crt

# Generate a PFX file for integration in browser
openssl pkcs12 -export -out ./client.pfx -inkey ./client.key -in ./client.crt -passout pass:

Вот различные используемые конфигурации:

server.csr.conf

[ req ]
default_bits       = 4096
default_md         = sha512
prompt             = no
encrypt_key        = no
distinguished_name = req_distinguished_name
# distinguished_name
[ req_distinguished_name ]
countryName            = "FR"
localityName           = "Lille"
organizationName       = "Sopra Steria"
organizationalUnitName = "Webskillz"
commonName             = "localhost"

v3.ext

authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = localhost

client.csr.conf

[ req ]
default_bits       = 4096
default_md         = sha512
default_keyfile    = server.key
prompt             = no
encrypt_key        = no
distinguished_name = req_distinguished_name
# distinguished_name
[ req_distinguished_name ]
countryName            = "FR"
localityName           = "Lille"
organizationName       = "Sopra Steria"
organizationalUnitName = "Webskillz"
commonName             = "localhost"

Наконец, я добавил rootCA.pem в Доверенные корневые центры сертификации , используя certmgr.msc, и добавил сертификаты client.pfx и server.crt в Personnal хранилище.

Выпуск 1

Chrome досадно перенаправляет http://localhost:8080 на https://localhost:8080,, и я не хочу систематически открывать chrome://net-internals/#hsts для удаления ключа localhost ...

Выпуск 2

Когда я наконец получаю доступ к http://localhost:8080,, меня просят выбрать сертификат, который я хочу аутентифицировать на https://localhost:9002 (да!), Но я все равно получаю 401, что не вызвано промежуточное ПО аттестации паспорта (в моем промежуточном ПО нет логина).

Дополнительная информация

1. Почти рабочая настройка

Мне удалось заставить эту настройку клиент / сервер работать без корневого сертификата, но проблема заключалась в том, что я получил NET::ERR_CERT_AUTHORITY_INVALID от Chrome ... Вот почему я добавил корневой CA, следуя некоторым советам по World Wide Web ... И действительно, это исправило проблему, но тогда я не смог пройти проверку подлинности, и Chrome начал автоматически перенаправлять http на https ಠ ෴ ಠ Кстати, CORS разрешен на стороне сервера, поэтому никаких проблем с CORS.

2. Код сервера

Стратегия проверки подлинности паспорта: мы просто проверяем отпечатки пальцев в базе данных.

серт-auth.js

import { Strategy } from 'passport-client-cert';

export default new Strategy(async (clientCert, done) => {
  console.log(clientCert); // NO LOG HERE!!
  if (clientCert.fingerprint) {
    try {
      const user = await findByFingerprintInMyAwesomeDb({ fingerprint: clientCert.fingerprint });
      return done(null, user);
    } catch (err) {
      return done(new Error(err));
    }
  }

  return done(null, false);
});

бутстраповские-express.js

import passport from 'passport';
import certificateStrategy from 'cert-auth';

export default (app) => {
  // CORS setup, bodyparser stuff & all...
  // ... //

  // Using authentication based on certificate
  passport.use(certificateStrategy);
  app.use(passport.initialize());
  app.use(passport.authenticate('client-cert', { session: false }));

  // Api routes.
  app.get('/api/stream',
    passport.authenticate('client-cert', { session: false }),
    (req, res) => {
      // Some router stuff
    });
};

index.js

import https from 'https';
import express from 'express';
import fs from 'fs';
import path from 'path';
import bootstrapExpress from 'bootstrap-express';

const certDir = path.join(__dirname, '..', 'cert');
const listenPromise = server => port => new Promise((resolve, reject) => {
  const listener = server.listen(port, err => (err ? reject(err) : resolve(listener)));
});

const options = {
  key: fs.readFileSync(path.join(certDir, 'server.key')),
  cert: fs.readFileSync(path.join(certDir, 'server.crt')),
  ca: fs.readFileSync(path.join(certDir, 'server.crt')),
  requestCert: true,
  rejectUnauthorized: false,
};

(async function main() {
  try {
    logger.info('Initializing server');

    const app = express();

    bootstrapExpress(app);

    const httpsServer = https.createServer(options, app);
    const httpsListener = await listenPromise(httpsServer)(9002);

    logger.info(`HTTPS listening on port ${httpsListener.address().port} in ${app.get('env')} environment`);
  } catch (err) {
    logger.error(err);
    process.exit(1);
  }
}());

Заключение

Любая помощь приветствуется :)

Привет

1 Ответ

0 голосов
/ 09 января 2019

Хорошо, я сделал много изменений, чтобы цепочка сертификатов была более понятной, но причина, по которой я все еще имел 401 после всех моих усилий, была из-за этой конфигурации на моем экспресс-сервере:

const options = {
  key: fs.readFileSync(path.join(certDir, 'server.key')),
  cert: fs.readFileSync(path.join(certDir, 'server.crt')),
  ca: fs.readFileSync(path.join(certDir, 'server.crt')),
  requestCert: true,
  rejectUnauthorized: false,
};

Рабочая конфигурация следующая (замена ca на rootCA):

const options = {
  key: fs.readFileSync(path.join(certDir, 'server.key')),
  cert: fs.readFileSync(path.join(certDir, 'server.crt')),
  ca: fs.readFileSync(path.join(certDir, 'rootCA.pem')),
  requestCert: true,
  rejectUnauthorized: false,
};

Кстати, эта проблема мне помогла, но я нашел ее всего несколько минут назад: https://github.com/nodejs/help/issues/253^

Дополнительная информация: чтобы избежать перенаправления с http на https, потому что мой сервер был на локальном DNS, я просто добавил новый DNS в C: \ Windows \ System32 \ drivers \ etc \ host

127.0.0.1 mysuperdns

Следовательно, общее имя для сертификата сервера должно быть mysuperdns.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...