Heim  >  Artikel  >  Backend-Entwicklung  >  Das X.509-Zertifikat kann nicht mit der benutzerdefinierten crypto.Signer-Implementierung generiert werden

Das X.509-Zertifikat kann nicht mit der benutzerdefinierten crypto.Signer-Implementierung generiert werden

PHPz
PHPznach vorne
2024-02-10 20:42:07797Durchsuche

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

php-Editor Youzi ist hier, um Ihnen ein Problem bei der Generierung von X.509-Zertifikaten vorzustellen. Wenn Sie eine benutzerdefinierte crypto.Signer-Implementierung zum Generieren eines Zertifikats verwenden, kann es manchmal zu einem unbrauchbaren Problem kommen. Dieses Problem kann bei Entwicklern zu Verwirrung darüber führen, wie es behoben werden kann. In diesem Artikel untersuchen wir die Ursache dieses Problems und stellen einige Lösungen bereit, die Entwicklern dabei helfen, erfolgreich ihre eigenen X.509-Zertifikate zu erstellen.

Frageninhalt

Ich versuche, ein x.509-Zertifikat basierend auf dem im HSM gespeicherten RSA-Schlüsselpaar zu generieren. Ich verwende diese pkcs #11-Implementierung, um mit meinem HSM zu kommunizieren.

Da meine Kryptoobjekte in letzterem gespeichert sind, muss ich, wenn eine Operation, die ich ausführen möchte, einen privaten Schlüssel erfordert (z. B. Signieren), die Schnittstelle crypto.signer implementieren, um „auf den privaten Schlüssel zuzugreifen“. Das ist die Umsetzung.

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
}

Diese Implementierung funktioniert. Um beispielsweise eine CSR zu erstellen, benötigt die Funktion „createcertificaterequest“ den privaten Schlüssel zum Signieren der CSR (wo sich die priv any 参数),这是我提供 rsasigner-Instanz befindet.

). Die Funktion

createcertificate ist etwas ähnlich, der Parameter pub是要生成的证书的公钥,priv ist der private Schlüssel des Unterzeichners.

Im folgenden Code versuche ich, ein selbstsigniertes x.509-Zertifikat zu generieren, sodass die templateparentParameter laut API identisch sind.

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
}

Unabhängig vom Schlüsseltyp (rsa oder ec) gibt diese Funktion den folgenden Fehler zurück.

FATA[2022-12-22 10:48:50] x509: signature over certificate returned by signer is invalid: crypto/rsa: verification error

Dieser Fehler wird zurückgegeben, wenn die crypto.signer Implementierung nicht korrekt abgeschlossen wurde.

Ich habe crypto.signer implementiert, um zu versuchen, dasselbe mit Schlüsselpaaren auf elliptischen Kurven zu erreichen, aber der Fehler ist derselbe.

Ich habe auch verschiedene Hashing-Algorithmen in der sign-Funktion ausprobiert, aber es hat nichts geändert.

Der Fehler scheint auf die Implementierung von crypto.signer zurückzuführen zu sein, obwohl es zum Generieren von CSR verwendet werden kann.

Lösung

Obwohl ich die Lösung für dieses Problem schon vor Monaten gefunden habe, habe ich mir nie die Zeit genommen, die Antwort mitzuteilen, aber jetzt ist die Zeit gekommen.

Wenn wir direkt über pkcs #11 signieren, müssen wir das Hash-Präfix verwalten, indem wir dem Hash manuell den digestinfo-Wert voranstellen, auf den hier verwiesen wird: https://www.rfc-editor.org/rfc /rfc3447#page- 43.

Genauer gesagt ist für die Signatur rsassa-pkcs1-v1_5 die Eingabe in die eigentliche Signaturfunktion eine asn.1 der-codierte Struktur. pkcs #11 verfügt über Hash-spezifische Mechanismen (z. B. ckm_sha256_rsa_pkcs), die wissen, wie die Struktur generiert wird, aber alle gehen davon aus, dass die Daten nicht gehasht sind, was bei Kryptowährungen nicht der Fall ist. Unterzeichnerschnittstelle, daher müssen wir den generischen Mechanismus cka_rsa_pkcs verwenden, der nur Rohsignaturvorgänge ausführt. Das bedeutet, dass wir die asn.1-Struktur selbst generieren müssen, was wir tun können, indem wir einfach das richtige Präfix für alle Hashes angeben, die wir möglicherweise verwenden möchten.

Mit Hilfe des Parameters

opts vom Typ crypto.signeropts können wir die Kennung einer Hash-Funktion vom Typ crypto.hash abrufen, wenn die Funktion sign() mit dem richtigen Präfix aufgerufen wird.

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
}

Es funktioniert wie ein Zauber. Es gibt jedoch bessere Dinge, die man tun kann.

Der

ckm_rsa_pkcs-Mechanismus stellt Signaturen vom Typ rsassa-pkcs1-v1_5 bereit. Ich überlasse es interessierten Lesern, dieses alte Signaturschema selbst zu untersuchen, das in neuen Produkten/Software nicht mehr verwendet werden sollte.

Tatsächlich wird empfohlen, den Mechanismus

ckm_rsa_pkcs_pss zu verwenden, der Signaturen vom Typ rsassa-pss bereitstellt.

Ausgehend von diesem Prinzip verwende ich jetzt diese Implementierung.

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
}

Das Präfix wird also nicht mehr benötigt, aber die Entsprechung zwischen der Hash-Algorithmus-ID und dem zu verwendenden Signaturalgorithmus sowie dem zu verwendenden mgf ist erforderlich.

Schließlich ist der verwendete

Signaturalgorithmus nicht mehr x509.sha256withrsa, x509.sha384withrsa oder x509.sha512withrsa, sondern sha256withrsapss, sha38 4withrsapss und sha512withrsapss.

Viel Spaß beim Signieren.

Das obige ist der detaillierte Inhalt vonDas X.509-Zertifikat kann nicht mit der benutzerdefinierten crypto.Signer-Implementierung generiert werden. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:stackoverflow.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen