


Unable to generate X.509 certificate using custom crypto.Signer implementation
php editor Youzi is here to introduce to you a problem about generating X.509 certificates. Sometimes when using a custom crypto.Signer implementation to generate a certificate, you may encounter an unusable problem. This issue can leave developers confused as to how to resolve it. In this article, we'll explore the cause of this problem and provide some solutions to help developers successfully generate their own X.509 certificates.
Question content
I am trying to generate an x.509 certificate based on an rsa key pair stored in hsm. I use this pkcs #11 implementation to communicate with my hsm.
Since my crypto objects are stored in the latter, if the operation I want to perform requires the private key (such as signing), I have to implement the crypto.signer interface to "access the private key". This is the implementation.
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 }
This implementation works. For example, to create a csr, the createcertificaterequest function requires the private key to sign the csr (priv any
parameter), which is where I provide the rsasigner
instance.
createcertificate function is somewhat similar. The parameter pub
is the public key of the certificate to be generated, and priv
is the private key of the signer.
In the code below, I try to generate a self-signed x.509 certificate, so the template
and parent
parameters are the same according to the 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 }
Regardless of the key type (rsa or ec), this function returns the following error.
FATA[2022-12-22 10:48:50] x509: signature over certificate returned by signer is invalid: crypto/rsa: verification error
This error will be returned if the crypto.signer
implementation is not completed correctly.
I implemented crypto.signer
to try to do the same thing using a key pair on an elliptic curve, but the error is the same.
I also tried different hashing algorithms in the sign
function but it didn't change anything.
The error seems to come from the implementation of crypto.signer
, although it can be used to generate csr.
SOLUTION
Although I found the solution to this problem months ago, I never took the time to share the answer, however, now is the time.
When we sign directly via pkcs #11 we need to manage the hash prefix by manually prefixing the hash with the digestinfo value referenced here: https://www .rfc-editor.org/rfc/rfc3447#page-43.
More precisely, for the rsassa-pkcs1-v1_5 signature, the input to the actual signing function is an asn.1 der encoded structure. pkcs #11 have hash-specific mechanisms (e.g. ckm_sha256_rsa_pkcs) that know how to generate the structure, but they all assume that the data is not hashed, which is not the case with cryptocurrencies. signer interface, so we must use the generic cka_rsa_pkcs mechanism, which only performs raw signing operations. This means we have to generate the asn.1 structure ourselves, which we can do by simply providing the correct prefix for all the hashes we might want to use.
With the help of the opts parameter of type crypto.signeropts we can retrieve the identifier of a hash function of type crypto.hash when: calling sign() function and apply the correct prefix.
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 }
It works like a charm. There are better things to do, though.
ckm_rsa_pkcs mechanism provides rsassa-pkcs1-v1_5 type of signature. I leave it to interested readers to investigate this old signature scheme on their own, which should no longer be used in new products/software.
Indeed, it is recommended to use the ckm_rsa_pkcs_pss mechanism, which provides rsassa-pss type signatures.
Starting from this principle, this is the implementation I use now.
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 }
So the prefix is no longer needed, but the correspondence between the hash algorithm identifier and the signature algorithm to be used and the mgf to be used are required.
Finally, in go, the signature algorithm used is no longer x509.sha256withrsa, x509.sha384withrsa or x509.sha512withrsa, but sha256withrsapss, sha384withrsapss and sha512withrsapss.
Happy signing.
The above is the detailed content of Unable to generate X.509 certificate using custom crypto.Signer implementation. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics











With the popularization and development of digital currency, more and more people are beginning to pay attention to and use digital currency apps. These applications provide users with a convenient way to manage and trade digital assets. So, what kind of software is a digital currency app? Let us have an in-depth understanding and take stock of the top ten digital currency apps in the world.

The built-in quantization tools on the exchange include: 1. Binance: Provides Binance Futures quantitative module, low handling fees, and supports AI-assisted transactions. 2. OKX (Ouyi): Supports multi-account management and intelligent order routing, and provides institutional-level risk control. The independent quantitative strategy platforms include: 3. 3Commas: drag-and-drop strategy generator, suitable for multi-platform hedging arbitrage. 4. Quadency: Professional-level algorithm strategy library, supporting customized risk thresholds. 5. Pionex: Built-in 16 preset strategy, low transaction fee. Vertical domain tools include: 6. Cryptohopper: cloud-based quantitative platform, supporting 150 technical indicators. 7. Bitsgap:

Recommended cryptocurrency trading platforms include: 1. Binance: the world's largest trading volume, supports 1,400 currencies, FCA and MAS certification. 2. OKX: Strong technical strength, supports 400 currencies, approved by the Hong Kong Securities Regulatory Commission. 3. Coinbase: The largest compliance platform in the United States, suitable for beginners, SEC and FinCEN supervision. 4. Kraken: a veteran European brand, ISO 27001 certified, holds a US MSB and UK FCA license. 5. Gate.io: The most complete currency (800), low transaction fees, and obtained a license from multiple countries. 6. Huobi Global: an old platform that provides a variety of services, and holds Japanese FSA and Hong Kong TCSP licenses. 7. KuCoin

The download, installation and registration process of the Hong Kong Digital Currency Exchange app is very simple. Users can quickly obtain and use this app through the official app download link provided in this article. This article will introduce in detail how to download, install and register the Hong Kong Digital Currency Exchange app to ensure that every user can complete the operation smoothly.

Huobi Digital Currency Trading App is one of the world's leading digital asset trading platforms and is favored by the majority of users. In order to facilitate users to quickly and safely download and install Huobi app, this article will provide you with detailed download and installation tutorials. Please note that this article provides a download link to Huobi official app. Use the download link to this article to download safely to avoid mistakenly entering a copycat website or downloading to unofficial versions. Next, let us download and install Huobi app step by step.

Binance C2C transactions allow users to buy and sell cryptocurrencies directly, and pay attention to the risks of counterparty, payment and price fluctuations. Choosing high-credit users and secure payment methods can reduce risks.

The methods to download the Hong Kong Digital Currency Exchange APP include: 1. Select a compliant platform, such as OSL, HashKey or Binance HK, etc.; 2. Download through official channels, iOS users download on the App Store, Android users download through Google Play or official website; 3. Register and verify their identity, use Hong Kong mobile phone number or email address to upload identity and address certificates; 4. Set security measures, enable two-factor authentication and regularly check account activities.

Ranking of cryptocurrency trading platforms: 1. Binance, 2. OKX, 3. Coinbase, 4. Kraken, 5. Huobi, 6. Bitfinex, 7. KuCoin, 8. Gemini, 9. Bybit, 10. Bitstamp, these exchanges are highly regarded for their high security, high trading volume, good user experience and extensive currency support.
