Maison >développement back-end >Golang >Impossible de générer le certificat X.509 à l'aide de l'implémentation crypto.Signer personnalisée
l'éditeur php Youzi est là pour vous présenter un problème de génération de certificats X.509. Parfois, lorsque vous utilisez une implémentation crypto.Signer personnalisée pour générer un certificat, vous pouvez rencontrer un problème inutilisable. Ce problème peut laisser les développeurs perplexes quant à la manière de le résoudre. Dans cet article, nous explorerons la cause de ce problème et proposerons des solutions pour aider les développeurs à générer avec succès leurs propres certificats X.509.
J'essaie de générer un certificat x.509 basé sur une paire de clés rsa stockée dans hsm. J'utilise cette implémentation pkcs #11 pour communiquer avec mon hsm.
Comme mes objets crypto sont stockés dans ce dernier, si une opération que je souhaite effectuer nécessite une clé privée (comme la signature), je dois implémenter l'interface crypto.signer afin de "accéder à la clé privée". C'est la mise en œuvre.
type rsasigner struct { privatekey p11.privatekey publickey *rsa.publickey } func (s rsasigner) public() crypto.publickey { return s.publickey } func (s rsasigner) sign(_ io.reader, digest []byte, _ crypto.signeropts) ([]byte, error) { return s.privatekey.sign(pkcs11.mechanism{mechanism: pkcs11.ckm_sha512_rsa_pkcs}, digest) } func newrsasigner(privatekey p11.privatekey) (*rsasigner, error) { var ( modulus, publicexponent []byte err error ) // retrieve modulus n from the private key // reminder: n = p * q modulus, err = p11.object(privatekey).attribute(pkcs11.cka_modulus) if err != nil { return nil, err } // retrieve public exponent (e: "always" 65537) from the private key // reminder: φ(n) = (p - 1) * (q - 1), e such that 1 < e < φ(n) and e and φ(n) are co prime publicexponent, err = p11.object(privatekey).attribute(pkcs11.cka_public_exponent) if err != nil { return nil, err } // public key is (e, n) publickey := &rsa.publickey{ n: new(big.int).setbytes(modulus), e: int(big.newint(0).setbytes(publicexponent).uint64()), } return &rsasigner{privatekey: privatekey, publickey: publickey}, nil }
Cette implémentation fonctionne. Par exemple, pour créer un csr, la fonction createcertificaterequest nécessite la clé privée pour signer le csr (là où se trouve l'instance priv any
参数),这是我提供 rsasigner
.
createcertificate est quelque peu similaire, le paramètre pub
是要生成的证书的公钥,priv
est la clé privée du signataire.
Dans le code ci-dessous, j'essaie de générer un certificat x.509 auto-signé, donc les template
和parent
paramètres sont les mêmes selon l'API.
func (t *token) x509(id, objecttype, output string) ([]time.duration, error) { startfunction := time.now() var ( keytype int privatekeytemplate []*pkcs11.attribute privatekeyobject p11.object err error timings []time.duration signer *rsasigner cert []byte file *os.file writtenbytes int ) objecttype = strings.tolower(objecttype) if objecttype != "rsa" && objecttype != "ec" { logger.fatalf("%s: unrecognized type, it can only be equal to rsa or ec", objecttype) } switch objecttype { case "rsa": keytype = pkcs11.ckk_rsa case "ec": keytype = pkcs11.ckk_ec } // creation of the template to find the private key based on the given id (pkcs #11 attribute cka_id) privatekeytemplate = []*pkcs11.attribute{ pkcs11.newattribute(pkcs11.cka_key_type, keytype), pkcs11.newattribute(pkcs11.cka_class, pkcs11.cko_private_key), pkcs11.newattribute(pkcs11.cka_id, id), } startfindobject := time.now() privatekeyobject, err = t.session.findobject(privatekeytemplate) timings = append(timings, time.since(startfindobject)) if err != nil { return nil, err } // creation of the x.509 certificate template certtemplate := &x509.certificate{ serialnumber: big.newint(2023), subject: pkix.name{ commonname: "test", }, signaturealgorithm: x509.sha512withrsa, notbefore: time.now(), notafter: time.now().adddate(1, 0, 0), } // instantiate the rsasigner with the found private key object signer, err = newrsasigner(p11.privatekey(privatekeyobject)) if err != nil { return nil, err } startcreatecert := time.now() cert, err = x509.createcertificate(rand.reader, certtemplate, certtemplate, signer.publickey, signer) timings = append(timings, time.since(startcreatecert)) if err != nil { return nil, err } file, err = os.create(output) if err != nil { return nil, err } writtenbytes, err = file.write(cert) if err != nil { return nil, err } logger.printf("wrote %d bytes in %s", writtenbytes, output) return append(timings, time.since(startfunction)), nil }
Quel que soit le type de clé (rsa ou ec), cette fonction renvoie l'erreur suivante.
FATA[2022-12-22 10:48:50] x509: signature over certificate returned by signer is invalid: crypto/rsa: verification error
Cette erreur sera renvoyée si l'implémentation crypto.signer
n'est pas terminée correctement.
J'ai implémenté crypto.signer
pour essayer de faire la même chose en utilisant des paires de clés sur des courbes elliptiques, mais l'erreur est la même.
J'ai également essayé différents algorithmes de hachage dans la fonction sign
mais cela n'a rien changé.
L'erreur semble provenir de l'implémentation de crypto.signer
bien qu'elle puisse être utilisée pour générer du csr.
Bien que j'ai trouvé la solution à ce problème il y a des mois, je n'ai jamais pris le temps de partager la réponse, cependant, c'est le moment.
Lorsque nous signons directement via pkcs #11, nous devons gérer le préfixe de hachage en préfixant manuellement le hachage en utilisant la valeur digestinfo référencée ici : https://www.rfc-editor.org/rfc /rfc3447#page-43 .
Plus précisément, pour la signature rsassa-pkcs1-v1_5, l'entrée de la fonction de signature réelle est une structure codée asn.1 der. pkcs #11 a des mécanismes spécifiques au hachage (par exemple ckm_sha256_rsa_pkcs) qui savent comment générer la structure, mais ils supposent tous que les données ne sont pas hachées, ce qui n'est pas le cas des crypto-monnaies. signer, nous devons donc utiliser le mécanisme générique cka_rsa_pkcs, qui effectue uniquement des opérations de signature brutes. Cela signifie que nous devons générer nous-mêmes la structure asn.1, ce que nous pouvons faire en fournissant simplement le préfixe correct pour tous les hachages que nous pourrions vouloir utiliser.
À l'aide du paramètre opts de type crypto.signeropts nous pouvons récupérer l'identifiant d'une fonction de hachage de type crypto.hash lorsque la fonction sign() est appelée, avec le préfixe correct appliqué.
type signer struct { prikey p11.privatekey pubkey *rsa.publickey } var hashprefixes = map[crypto.hash][]byte{ crypto.sha256: {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20}, crypto.sha384: {0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30}, crypto.sha512: {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40}, } func (s signer) public() crypto.publickey { return s.pubkey } func (s signer) sign(_ io.reader, digest []byte, opts crypto.signeropts) ([]byte, error) { return s.prikey.sign(*pkcs11.newmechanism(pkcs11.ckm_rsa_pkcs, nil), append(hashprefixes[opts.hashfunc()], digest...)) } func newsigner(key p11.privatekey) (*signer, error) { // retrieve modulus n from the private key // reminder: n = p * q modulus, err := p11.object(key).attribute(pkcs11.cka_modulus) if err != nil { return nil, err } var pubexp []byte // retrieve public exponent (e: "always" 65537) from the private key // reminder: φ(n) = (p - 1) * (q - 1), e such that 1 < e < φ(n) and e and φ(n) are co prime pubexp, err = p11.object(key).attribute(pkcs11.cka_public_exponent) if err != nil { return nil, err } // public key is (e, n) pubkey := &rsa.publickey{ n: new(big.int).setbytes(modulus), e: int(new(big.int).setbytes(pubexp).uint64()), } return &signer{prikey: key, pubkey: pubkey}, nil }
Cela fonctionne à merveille. Il y a cependant de meilleures choses à faire.
Le mécanismeckm_rsa_pkcs fournit des signatures de type rsassa-pkcs1-v1_5. Je laisse aux lecteurs intéressés le soin d'étudier par eux-mêmes cet ancien schéma de signature, qui ne devrait plus être utilisé dans les nouveaux produits/logiciels.
En effet, il est recommandé d'utiliser le mécanisme ckm_rsa_pkcs_pss, qui fournit des signatures de type rsassa-pss.
Partant de ce principe, c'est l'implémentation que j'utilise maintenant.
type Signer struct { priKey p11.PrivateKey pubKey *rsa.PublicKey } var sigAlg = map[crypto.Hash]uint{ crypto.SHA256: pkcs11.CKM_SHA256_RSA_PKCS_PSS, crypto.SHA384: pkcs11.CKM_SHA384_RSA_PKCS_PSS, crypto.SHA512: pkcs11.CKM_SHA512_RSA_PKCS_PSS, } var mgf = map[crypto.Hash]uint{ crypto.SHA256: pkcs11.CKG_MGF1_SHA256, crypto.SHA384: pkcs11.CKG_MGF1_SHA384, crypto.SHA512: pkcs11.CKG_MGF1_SHA512, } func (s Signer) Public() crypto.PublicKey { return s.pubKey } func (s Signer) Sign(_ io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { return s.priKey.Sign(*pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS_PSS, pkcs11.NewPSSParams(sigAlg[opts.HashFunc()], mgf[opts.HashFunc()], uint(opts.HashFunc().Size()))), digest) } func NewSigner(key p11.PrivateKey) (*Signer, error) { // Retrieve modulus n from the private key // Reminder: n = p * q modulus, err := p11.Object(key).Attribute(pkcs11.CKA_MODULUS) if err != nil { return nil, err } var pubExp []byte // Retrieve public exponent (e: "always" 65537) from the private key // Reminder: φ(n) = (p - 1) * (q - 1), e such that 1 < e < φ(n) and e and φ(n) are co prime pubExp, err = p11.Object(key).Attribute(pkcs11.CKA_PUBLIC_EXPONENT) if err != nil { return nil, err } // Public key is (e, n) pubKey := &rsa.PublicKey{ N: new(big.Int).SetBytes(modulus), E: int(new(big.Int).SetBytes(pubExp).Uint64()), } return &Signer{priKey: key, pubKey: pubKey}, nil }
Le préfixe n'est donc plus nécessaire, mais la correspondance entre l'identifiant de l'algorithme de hachage et l'algorithme de signature à utiliser et le mgf à utiliser est requise.
Enfin, en go, l'algorithme de signature utilisé n'est plus x509.sha256withrsa, x509.sha384withrsa ou x509.sha512withrsa, mais sha256withrsapss, 4withrsapss et sha512withrsapss.
Bonne signature.Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!