php小編柚子在這裡為大家介紹一個關於產生 X.509 憑證的問題。有時在使用自訂 crypto.Signer 實作產生憑證的過程中,可能會遇到一個無法使用的問題。這個問題可能會讓開發者感到困惑,不知道該如何解決。在本文中,我們將探討這個問題的原因,並提供一些解決方案,以幫助開發者順利產生自己的 X.509 憑證。
我正在嘗試根據儲存在 hsm 中的 rsa 金鑰對產生 x.509 憑證。我使用此 pkcs #11 實作與我的 hsm 進行通訊。
由於我的加密物件儲存在後者中,如果我想要執行的操作需要私鑰(例如簽署),我必須實作 crypto.signer 介面才能「存取私鑰」 。這是這個實作。
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 }
這個實作有效。例如,要建立 csr,createcertificaterequest 函數需要私鑰來簽署 csr(priv any
參數),這是我提供 rsasigner
實例的地方。
createcertificate函數有些類似,參數pub
是要產生的憑證的公鑰,priv
是簽署者的私鑰。
在下面的程式碼中,我嘗試產生自簽名的x.509證書,因此根據api,template
和parent
參數是相同的。
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 }
無論金鑰類型(rsa 或 ec)為何,此函數都會傳回下列錯誤。
FATA[2022-12-22 10:48:50] x509: signature over certificate returned by signer is invalid: crypto/rsa: verification error
如果 crypto.signer
實作未正確完成,則會傳回此錯誤。
我實作了 crypto.signer
來嘗試使用橢圓曲線上的金鑰對執行相同的操作,但錯誤是相同的。
我還在 sign
函數中嘗試了不同的雜湊演算法,但它沒有改變任何東西。
該錯誤似乎來自 crypto.signer
的實現,儘管它可以用於生成 csr。
儘管我幾個月前就已經找到了這個問題的解決方案,但我從未花時間分享答案,但是,現在是時候了。
當我們直接透過pkcs #11 進行簽名時,我們需要透過使用此處引用的digestinfo 值手動為雜湊添加前綴來管理雜湊前綴:https://www .rfc-editor.org/rfc/rfc3447#page-43。
更精確地說,對於 rsassa-pkcs1-v1_5 簽名,實際簽名函數的輸入是 asn.1 der 編碼的結構。 pkcs #11 具有特定於哈希的機制(例如ckm_sha256_rsa_pkcs),它們知道如何產生該結構,但它們都假設資料未經過哈希處理,而加密貨幣的情況並非如此。 signer 接口,因此我們必須使用通用的 cka_rsa_pkcs 機制,該機制僅執行原始簽名操作。這意味著我們必須自己產生 asn.1 結構,只需為我們可能想要使用的所有雜湊值提供正確的前綴即可做到這一點。
借助crypto.signeropts類型的opts參數,我們可以在以下情況下檢索crypto.hash類型的雜湊函數的識別碼:調用sign() 函數,並套用正確的前綴。
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 }
它就像一個魅力。不過,還有更好的事要做。
ckm_rsa_pkcs機制提供rsassa-pkcs1-v1_5類型的簽章。我留給有興趣的讀者自己研究這個舊的簽名方案,該方案不應再在新產品/軟體中使用。
確實,建議使用ckm_rsa_pkcs_pss機制,它提供rsassa-pss類型的簽章。
從這個原則出發,這是我現在使用的實作。
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 }
因此不再需要前綴,但是需要雜湊演算法識別碼和要使用的簽章演算法以及要使用的mgf之間的對應關係。
最後,在go中,使用的簽名演算法不再是x509.sha256withrsa、x509.sha384withrsa 或x509.sha512withrsa,而是sha256withrsapss、sha384withrsapss 和sha512withrsapss。
簽約愉快。
以上是無法使用自訂 crypto.Signer 實作產生 X.509 證書的詳細內容。更多資訊請關注PHP中文網其他相關文章!