Table of Contents
Question content
SOLUTION
Home Backend Development Golang Unable to generate X.509 certificate using custom crypto.Signer implementation

Unable to generate X.509 certificate using custom crypto.Signer implementation

Feb 10, 2024 pm 08:42 PM
cryptocurrency

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

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
}
Copy after login

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.

The

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
}
Copy after login

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
Copy after login

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
}
Copy after login

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
}
Copy after login

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!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

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

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

What are the digital currency trading platforms in 2025? The latest rankings of the top ten digital currency apps What are the digital currency trading platforms in 2025? The latest rankings of the top ten digital currency apps Apr 22, 2025 pm 03:09 PM

Recommended apps for the top ten virtual currency viewing platforms: 1. OKX, 2. Binance, 3. Gate.io, 4. Huobi, 5. Coinbase, 6. Kraken, 7. Bitfinex, 8. KuCoin, 9. Bybit, 10. Bitstamp, these platforms provide real-time market trends, technical analysis tools and user-friendly interfaces to help investors make effective market analysis and trading decisions.

What are the free market viewing software websites? Ranking of the top ten free market viewing software in the currency circle What are the free market viewing software websites? Ranking of the top ten free market viewing software in the currency circle Apr 22, 2025 am 10:57 AM

The top three top ten free market viewing software in the currency circle are OKX, Binance and gate.io. 1. OKX provides a simple interface and real-time data, supporting a variety of charts and market analysis. 2. Binance has powerful functions, accurate data, and is suitable for all kinds of traders. 3. gate.io is known for its stability and comprehensiveness, and is suitable for long-term and short-term investors.

Reliable and easy-to-use virtual currency exchange app recommendations The latest ranking of the top ten exchanges in the currency circle Reliable and easy-to-use virtual currency exchange app recommendations The latest ranking of the top ten exchanges in the currency circle Apr 22, 2025 pm 01:21 PM

The reliable and easy-to-use virtual currency exchange apps are: 1. Binance, 2. OKX, 3. Gate.io, 4. Coinbase, 5. Kraken, 6. Huobi Global, 7. Bitfinex, 8. KuCoin, 9. Bittrex, 10. Poloniex. These platforms were selected as the best for their transaction volume, user experience and security, and all offer registration, verification, deposit, withdrawal and transaction operations.

Top 10 digital currency exchange app recommendations, top ten virtual currency exchanges in the currency circle Top 10 digital currency exchange app recommendations, top ten virtual currency exchanges in the currency circle Apr 22, 2025 pm 03:03 PM

Recommended apps on top ten digital currency exchanges: 1. OKX, 2. Binance, 3. gate.io, 4. Huobi, 5. Coinbase, 6. KuCoin, 7. Kraken, 8. Bitfinex, 9. Bybit, 10. Bitstamp, these apps provide real-time market trends, technical analysis and price reminders to help users monitor market dynamics in real time and make informed investment decisions.

Top 10 safe and easy-to-use virtual currency trading platforms, ranking of the top ten reliable digital currency exchanges Top 10 safe and easy-to-use virtual currency trading platforms, ranking of the top ten reliable digital currency exchanges Apr 22, 2025 pm 12:45 PM

The top ten safe and easy-to-use virtual currency trading platforms are: Binance, OKX, gate.io, Coinbase, Kraken, Huobi, Bybit, KuCoin, Bitfinex, and Bittrex. These platforms are highly praised for their high liquidity, low transaction fees, diversified trading products, global layout, strong technical support, innovative trading systems, high security, rich currency and user-friendly interface.

Top 10 digital currency exchange apps Recommended by the top ten digital currency exchanges Top 10 digital currency exchange apps Recommended by the top ten digital currency exchanges Apr 22, 2025 pm 03:12 PM

The top ten digital currency exchange apps are ranked: 1. Binance, 2. OKX, 3. gate.io, 4. Coinbase, 5. Kraken, 6. Huobi, 7. KuCoin, 8. Bybit, 9. Bitfinex, 10. Bittrex, these platforms were selected for their excellent performance in user experience, security, handling fees and transaction volume.

gate.io sesame door latest official app address gate.io sesame door latest official app address Apr 22, 2025 pm 01:03 PM

The official Gate.io APP can be downloaded in the following ways: 1. Visit the official website gate.io to download; 2. Search "Gate.io" on the App Store or Google Play to download. Be sure to download it through the official channel to ensure safety.

Top 10 Global Virtual Currency Exchange Rankings Top 10 Latest Virtual Currency APPs in 2025 Top 10 Global Virtual Currency Exchange Rankings Top 10 Latest Virtual Currency APPs in 2025 Apr 22, 2025 pm 02:39 PM

Top 10 global virtual currency exchanges rankings: 1. OKX, 2. Binance, 3. Gate.io, 4. Huobi, 5. Coinbase, 6. Kraken, 7. Bitfinex, 8. KuCoin, 9. Bybit, 10. Bitstamp, these platforms provide real-time market trends, technical analysis tools and user-friendly interfaces to help investors conduct effective market analysis and trading decisions.

See all articles