editor php Youzi di sini untuk memperkenalkan kepada anda masalah tentang penjanaan sijil X.509. Kadangkala apabila menggunakan pelaksanaan crypto.Signer tersuai untuk menjana sijil, anda mungkin menghadapi masalah yang tidak boleh digunakan. Isu ini boleh menyebabkan pembangun keliru tentang cara menyelesaikannya. Dalam artikel ini, kami akan meneroka punca masalah ini dan menyediakan beberapa penyelesaian untuk membantu pembangun berjaya menjana sijil X.509 mereka sendiri.
Saya cuba menjana sijil x.509 berdasarkan pasangan kunci rsa yang disimpan dalam hsm. Saya menggunakan pelaksanaan pkcs #11 ini untuk berkomunikasi dengan hsm saya.
Memandangkan objek crypto saya disimpan dalam yang kedua, jika operasi yang saya ingin lakukan memerlukan kunci peribadi (seperti menandatangani), saya perlu melaksanakan antara muka crypto.signer untuk "mengakses kunci peribadi". Inilah pelaksanaannya.
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 }
Pelaksanaan ini berfungsi. Contohnya, untuk mencipta csr, fungsi createcertificaterequest memerlukan kunci peribadi untuk menandatangani csr (di mana contoh priv any
参数),这是我提供 rsasigner
berada.
createcertificate agak serupa, parameter pub
是要生成的证书的公钥,priv
ialah kunci peribadi penandatangan.
Dalam kod di bawah, saya cuba menjana sijil x.509 yang ditandatangani sendiri, jadi template
和parent
parameter adalah sama mengikut 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 }
Tidak kira jenis kunci (rsa atau ec), fungsi ini mengembalikan ralat berikut.
FATA[2022-12-22 10:48:50] x509: signature over certificate returned by signer is invalid: crypto/rsa: verification error
Ralat ini akan dikembalikan jika pelaksanaan crypto.signer
tidak diselesaikan dengan betul.
Saya melaksanakan crypto.signer
untuk mencuba melakukan perkara yang sama menggunakan pasangan kunci pada lengkung elips, tetapi ralatnya adalah sama.
Saya juga mencuba algoritma pencincangan yang berbeza dalam fungsi sign
tetapi ia tidak mengubah apa-apa.
Ralat nampaknya datang dari pelaksanaan crypto.signer
walaupun ia boleh digunakan untuk menjana csr.
Walaupun saya telah menemui penyelesaian untuk masalah ini beberapa bulan yang lalu, saya tidak pernah meluangkan masa untuk berkongsi jawapannya, namun, inilah masanya.
Apabila kami menandatangani terus melalui pkcs #11, kami perlu mengurus awalan cincang dengan memberi awalan cincang secara manual menggunakan nilai digestinfo yang dirujuk di sini: https://www.rfc-editor.org/rfc /rfc3447#page-43 .
Lebih tepat lagi, untuk tandatangan rsassa-pkcs1-v1_5, input kepada fungsi tandatangan sebenar ialah struktur yang dikodkan asn.1 der. pkcs #11 mempunyai mekanisme khusus cincang (cth. ckm_sha256_rsa_pkcs) yang tahu cara menjana struktur, tetapi mereka semua menganggap bahawa data tidak dicincang, yang tidak berlaku dengan mata wang kripto. antara muka penandatangan, jadi kita perlu menggunakan mekanisme cka_rsa_pkcs generik, yang hanya menjalankan operasi tandatangan mentah. Ini bermakna kita perlu menjana sendiri struktur asn.1, yang boleh kita lakukan dengan hanya menyediakan awalan yang betul untuk semua cincang yang mungkin ingin kita gunakan.
Dengan bantuan parameter opts jenis crypto.signeropts kita boleh mendapatkan semula pengecam fungsi cincang jenis crypto.hash apabila fungsi sign() dipanggil, dengan awalan yang betul digunakan.
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 }
Ia berfungsi seperti azimat. Terdapat perkara yang lebih baik untuk dilakukan, walaupun.
Mekanismeckm_rsa_pkcs menyediakan tandatangan jenis rsassa-pkcs1-v1_5. Saya serahkan kepada pembaca yang berminat untuk menyiasat sendiri skim tandatangan lama ini, yang sepatutnya tidak lagi digunakan dalam produk/perisian baharu.
Sememangnya, adalah disyorkan untuk menggunakan mekanisme ckm_rsa_pkcs_pss, yang menyediakan tandatangan jenis rsassa-pss.
Bermula dari prinsip ini, inilah perlaksanaan yang saya gunakan sekarang.
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 }
Jadi awalan tidak lagi diperlukan, tetapi korespondensi antara pengecam algoritma cincang dan algoritma tandatangan yang akan digunakan dan mgf yang akan digunakan diperlukan.
Akhirnya, dalam proses, algoritma tandatangan yang digunakan bukan lagi x509.sha256withrsa, x509.sha384withrsa atau x509.sha512withrsasha25, tetapi .8pssha2 s dan sha512withrsapss.
Selamat menandatangani.Atas ialah kandungan terperinci Tidak dapat menjana sijil X.509 menggunakan pelaksanaan crypto.Signer tersuai. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!