目录
问题内容
解决方法
首页 后端开发 Golang 无法使用自定义 crypto.Signer 实现生成 X.509 证书

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

Feb 10, 2024 pm 08:42 PM
加密货币

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

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,templateparent参数是相同的。

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.sha384withrsax509.sha512withrsa,而是 sha256withrsapsssha384withrsapsssha512withrsapss

签约愉快。

以上是无法使用自定义 crypto.Signer 实现生成 X.509 证书的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
4 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

gate.io新手注册教程 gate.io新手注册教程 Mar 31, 2025 pm 11:09 PM

本文提供了一份详细的Gate.io新手注册教程,涵盖了从访问官网到完成注册的每一个步骤,包括填写注册信息、进行验证、阅读用户协议等。文章还强调了注册成功后的安全措施,如设置二次验证和完成实名认证,并给出了新手提示,帮助用户安全地开启数字资产交易之旅。

okx欧易交易所网页版进入链接点击进入 okx欧易交易所网页版进入链接点击进入 Mar 31, 2025 pm 06:21 PM

1、okx欧易交易所网页版进入☜☜☜☜☜点击保存2、okx欧易交易所app链接点击☜☜☜☜☜点击保存3、 进入官网后,清晰的界面提供登录和注册入口,用户可根据自身情况选择登录已有账户或注册新账户。 无论是查看实时行情、进行交易,还是管理资产,OKX网页版都提供简洁流畅的操作体验,适合新手和老手使用。 立即访问OKX官网,体验便

欧易是干嘛的 欧易是什么 欧易是干嘛的 欧易是什么 Apr 01, 2025 pm 03:18 PM

欧易(OKX)是一个全球性的数字资产交易平台,主要功能包括:1. 买卖数字资产(现货交易),2. 进行数字资产之间的交易,3. 提供市场行情和数据,4. 提供多样化的交易产品(如衍生品),5. 提供资产增值相关服务,6. 方便资产管理。

ok官方入口网页版 ok交易所官方网页版登录入口 ok官方入口网页版 ok交易所官方网页版登录入口 Mar 31, 2025 pm 06:24 PM

本文详细介绍了如何使用OK交易所官方网页版进行登录。用户只需在浏览器搜索“OK交易所官方网页版”,进入官网后点击右上角的登录按钮,输入用户名和密码即可登录。 注册用户可轻松管理资产、进行交易及资金存取等操作。官网界面简洁易用,并提供完善的客服支持,确保用户获得流畅的数字资产交易体验。 还在等什么?立即访问OK交易所官方网站,开启您的数字资产之旅!

币安binance电脑版入口币安binance电脑版pc官网登录入口 币安binance电脑版入口币安binance电脑版pc官网登录入口 Mar 31, 2025 pm 04:36 PM

本文提供Binance币安电脑版登录与注册的完整指南。首先,详细讲解了币安电脑版登录步骤:在浏览器搜索“币安官网”,点击登录按钮,输入邮箱和密码(启用2FA需输入验证码)即可登录。其次,文章阐述了注册流程:点击“注册”按钮,填写邮箱地址,设置强密码,验证邮箱即可完成注册。最后,文章还特别强调了账户安全,提醒用户注意官方域名、网络环境以及定期更新密码,确保账户安全,更好地使用币安电脑版提供的各项功能,例如查看行情、进行交易和管理资产。

虚拟币app软件推荐网站有哪些? 虚拟币app软件推荐网站有哪些? Mar 31, 2025 pm 09:06 PM

本文推荐十个知名的虚拟币相关APP推荐网站,涵盖币安学院(Binance Academy)、OKX Learn、CoinGecko、CryptoSlate、CoinDesk、Investopedia、CoinMarketCap、火币大学(Huobi University)、Coinbase Learn和CryptoCompare。这些网站不仅提供虚拟货币市场数据、价格走势分析等信息,还提供丰富的学习资源,包括区块链基础知识、交易策略、以及各个交易平台APP的使用教程和评测,帮助用户更好地了解和使

货币交易网官方网站大全2025 货币交易网官方网站大全2025 Mar 31, 2025 pm 03:57 PM

全球用户量排名前列,支持现货、合约、Web3钱包等全品类交易,安全性高且手续费低。历史悠久的综合交易平台,以合规性和高流动性着称,支持多语言服务。行业龙头,覆盖币币交易、杠杆、期权等,流动性强且支持BNB抵扣费用。

web3在哪个平台交易? web3在哪个平台交易? Mar 31, 2025 pm 07:54 PM

本文盘点十大知名Web3交易平台,包括币安(Binance)、欧易(OKX)、Gate.io(芝麻开门)、Kraken、Bybit、Coinbase、KuCoin、Bitget、Gemini和Bitstamp。 文章详细对比了各平台的特色,例如币种数量、交易类型(现货、期货、期权、NFT等)、手续费、安全性、合规性、用户群体等,旨在帮助投资者选择最合适的交易平台。无论是高频交易者、合约交易爱好者,还是注重合规性和安全性的投资者,都能从中找到参考信息。

See all articles