Maison > développement back-end > Golang > le corps du texte

Impossible de générer le certificat X.509 à l'aide de l'implémentation crypto.Signer personnalisée

PHPz
Libérer: 2024-02-10 20:42:07
avant
793 Les gens l'ont consulté

无法使用自定义 crypto.Signer 实现生成 X.509 证书

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.

Contenu de la question

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
}
Copier après la connexion

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.

La fonction

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 templateparentparamè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
}
Copier après la connexion

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
Copier après la connexion

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.

Solution

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
}
Copier après la connexion

Cela fonctionne à merveille. Il y a cependant de meilleures choses à faire.

Le mécanisme

ckm_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
}
Copier après la connexion

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!

Étiquettes associées:
source:stackoverflow.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!