Heim >Backend-Entwicklung >Golang >Das X.509-Zertifikat kann nicht mit der benutzerdefinierten crypto.Signer-Implementierung generiert werden
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.
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.
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 template
和parent
Parameter 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.
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 Parametersopts 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 Mechanismusckm_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!