J'ai deux certificats auto-signés extrêmement similaires, générés via deux méthodes différentes.
Pour les tester, j'ai:
openssl s_client -connect local.mydomain.com -CAfile /path/to/the/ca/cert.pem
Un certificat échoue:
CONNECTED(00000003)
depth=0 CN = local.mydomain.com
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN = local.mydomain.com
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
0 s:/CN=local.mydomain.com
i:/CN=local.mydomain.com
---
Un certificat réussit:
CONNECTED(00000003)
depth=0 CN = local.mydomain.com
verify return:1
---
Certificate chain
0 s:/CN = local.mydomain.com
i:/CN = local.mydomain.com
---
Je compare les détails des certificats avec openssl x509 -in /path/to/the/ca/cert.pem -text -noout
Le certificat défaillant:
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
47:dc:02:c7:11:fc:8e:96:45:22:aa:6b:23:79:32:ca
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=local.mydomain.com
Validity
Not Before: Nov 18 11:55:31 2016 GMT
Not After : Nov 18 12:15:31 2017 GMT
Subject: CN=local.mydomain.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
<stuff>
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Client Authentication, TLS Web Server Authentication
X509v3 Subject Alternative Name:
DNS:local.mydomain.com
X509v3 Subject Key Identifier:
6D:4F:AF:E4:60:23:72:E5:83:27:91:7D:1D:5F:E9:7C:D9:B6:00:2A
Signature Algorithm: sha256WithRSAEncryption
<stuff>
Le cert de travail:
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
9b:6b:3d:a3:b9:a3:a4:b4
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=local.mydomain.com
Validity
Not Before: Nov 19 13:27:30 2016 GMT
Not After : Nov 19 13:27:30 2017 GMT
Subject: CN=local.mydomain.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
<stuff>
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
03:E7:DA:AA:2E:CC:23:ED:C5:07:3D:E1:33:86:F5:22:D4:76:EB:CB
X509v3 Authority Key Identifier:
keyid:03:E7:DA:AA:2E:CC:23:ED:C5:07:3D:E1:33:86:F5:22:D4:76:EB:CB
X509v3 Basic Constraints:
CA:TRUE
Signature Algorithm: sha256WithRSAEncryption
57<stuff>
En regardant cela, la différence la plus évidente est que le certificat de travail a CA:TRUE
en dessous de X509v3 Basic Constraints
. Cependant, après avoir lu sur le Web, j'avais l'impression que les certificats auto-signés n'étaient pas censés être des autorités de certification, en particulier cela dit qu'ils ne le seront normalement pas:
Questions de base sur les certificats auto-signés
La réponse là-bas dit qu'étant auto-signé, aucune autorité de certification n'est impliquée. Mais peut-être que openssl nécessite des certificats auto-signés pour que cela soit réglé de toute façon? Ou peut-être que les certificats diffèrent d'une autre manière pertinente?
MISE À JOUR:
J'ai passé quelque temps à essayer de déboguer printf les internes d'OpenSL, pas que je comprenne tout cela. Dans le fichier v3_purp.c se trouve la macro suivante:
#define ku_reject(x, usage) \
(((x)->ex_flags & EXFLAG_KUSAGE) && !((x)->ex_kusage & (usage)))
Il est utilisé dans ce morceau de code qui vérifie les certificats auto-signés:
/* Does subject name match issuer ? */
if (X509_check_akid(x, x->akid) == X509_V_OK &&
!ku_reject(x, KU_KEY_CERT_SIGN))
x->ex_flags |= EXFLAG_SS;
Dans le cas d'un certificat défaillant (x)->ex_flags & EXFLAG_KUSAGE
est égal à 2
EXFLAG_KUSAGE
est défini ici pour le certificat défaillant, plus tôt dans le même fichier:
static void x509v3_cache_extensions(X509 *x)
{
......
if ((usage = X509_get_ext_d2i(x, NID_key_usage, NULL, NULL))) {
if (usage->length > 0) {
x->ex_kusage = usage->data[0];
if (usage->length > 1)
x->ex_kusage |= usage->data[1] << 8;
} else
x->ex_kusage = 0;
x->ex_flags |= EXFLAG_KUSAGE;
ASN1_BIT_STRING_free(usage);
}
....
Il semble donc que le problème soit que le certificat défaillant a le X509v3 Key Usage
extension, mais ne spécifie pas KU_KEY_CERT_SIGN dans cette extension?
MISE À JOUR 2:
Selon https://tools.ietf.org/html/rfc5280#section-4.2.1. :
"Le bit keyCertSign est affirmé lorsque la clé publique objet est utilisée pour vérifier les signatures sur les certificats de clé publique. Si le bit keyCertSign est affirmé, alors le bit cA dans l'extension de contraintes de base (Section 4.2.1.9) DOIT également être affirmé."
Ainsi, le bit CA dans les contraintes de base n'a pas besoin d'être présent, mais si vous incluez une section X509v3 Key Usage dans le certificat, selon la base de code openssl, vous devez spécifier keyCertSign, et selon la RFC si vous spécifiez keyCertSign, vous devez également inclure les contraintes de base du bit CA?
Le problème décrit dans la question peut être reproduit avec une configuration plus simple. J'ai créé deux certificats auto-signés pour les tests qui ne diffèrent que par le fait que l'un a un indicateur CA (ss-ca.pem
) tandis que l'autre ne le fait pas (ss-noca.pem
). Avec openssl verify
on peut vérifier si le certificat peut être vérifié par rapport à un chemin CA spécifique.
Le certificat auto-signé avec CA: true est vérifié avec succès contre lui-même ('OK') bien qu'il tombe sur X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT (erreur 18) lors de la vérification de la chaîne:
$ openssl verify -CAfile ss-ca.pem ss-ca.pem
ss-ca.pem: CN = test CA
error 18 at 0 depth lookup:self signed certificate
OK
Avec le certificat auto-signé avec CA: faux, la vérification échoue (pas de "OK") et elle montre l'erreur X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY (erreur 20):
$ openssl verify -CAfile ss-noca.pem ss-noca.pem
ss-noca.pem: CN = test no CA
error 20 at 0 depth lookup:unable to get local issuer certificate
Ma théorie est qu'OpenSSL essaie de construire la chaîne de confiance à un certificat donné avec -CAfile
. Pour créer la chaîne de confiance, le sujet du certificat d'émetteur doit correspondre à l'émetteur du certificat, la signature doit être valide (c'est-à-dire validée à l'aide de la clé publique de l'émetteur) et le certificat d'émetteur doit être autorisé à signer des certificats, c'est-à-dire CA: vrai. Bien que les deux premières vérifications ne retournent aucun problème avec les deux certificats, la vérification de CA: true n'est valide que pour ss-ca.pem
.
Je pense que la principale différence avec OpenSSL -CAfile
à d'autres concepts d'un magasin de confiance est que -CAfile
ne fait que ce que son nom implique: il contient une liste de CA de confiance qui sont utilisées pour valider la chaîne de confiance. Contrairement à ces autres implémentations du magasin de relations de confiance, il peut également contenir tout type de certificats où la routine de validation vérifie simplement si le certificat envoyé par le serveur est directement approuvé car il est contenu dans le magasin de relations de confiance, quel que soit le type d'indicateurs du certificat ou même s'il est expiré.
Cette différence entre un magasin de clés de confiance à usage général et OpenSSL -CAfile
peut également être vu lors de la mise en place d'un certificat d'entité finale non CA comme approuvé dans le magasin. Dans l'exemple, j'utilise un certificat CA ca.pem
qui a délivré un certificat d'entité finale server.pem
qui bien sûr n'a pas CA: vrai.
La validation du certificat d'entité finale par rapport au certificat CA fonctionne comme prévu:
$ openssl verify -CAfile ca.pem server.pem
server.pem: OK
Mais essayer de faire confiance au certificat d'entité finale directement en le plaçant dans le magasin CA ne fonctionne pas parce que le magasin CA n'est pas un magasin d'approbation à usage général mais limité aux certificats CA:
$ openssl verify -CAfile server.pem server.pem
error 20 at 0 depth lookup:unable to get local issuer certificate
Avec un magasin d'approbations à usage général, la dernière vérification aurait également dû réussir car le certificat d'entité finale a été explicitement déclaré comme approuvé.
Ce problème a été corrigé aujourd'hui dans OpenSSL (voir https://github.com/openssl/openssl/issues/1418 ); devrait être disponible prochainement.