英文:
Unable to generate an X.509 certificate with a custom crypto.Signer implementation
问题
我正在尝试基于存储在HSM中的RSA密钥对生成一个基于X.509证书。我使用这个PKCS #11实现与我的HSM进行通信。
由于我的加密对象存储在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
)
// 从私钥中获取模数n
// 提示:n = p * q
modulus, err = p11.Object(privateKey).Attribute(pkcs11.CKA_MODULUS)
if err != nil {
return nil, err
}
// 从私钥中获取公共指数(e:"always" 65537)
// 提示:φ(n) = (p - 1) * (q - 1),e满足 1 < e < φ(n),且e和φ(n)互质
publicExponent, err = p11.Object(privateKey).Attribute(pkcs11.CKA_PUBLIC_EXPONENT)
if err != nil {
return nil, err
}
// 公钥为(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
}
// 创建基于给定id(PKCS #11属性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
}
// 创建X.509证书模板
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),
}
// 使用找到的私钥对象实例化RSASigner
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是有效的。
英文:
I am trying to generate an X.509 certificate based on an RSA key pair stored in an HSM. I use this PKCS #11 implementation to communicate with my HSM.
Since my cryptographic objects are stored in the latter, if the operations I want to perform need the private key (a signature for example), I have to implement the crypto.Signer interface to be able to "access the private key". Here is this 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 an instance of RSASigner
.
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 am trying to generate a self-signed X.509 certificate, so according to the API, the template
and parent
parameters are the same.
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
}
This function returns the following error regardless of the key type (RSA or EC).
FATA[2022-12-22 10:48:50] x509: signature over certificate returned by signer is invalid: crypto/rsa: verification error
This error is returned if the crypto.Signer
implementation was not done correctly.
I implemented crypto.Signer
to try to do the same thing with a key pair on elliptic curve but the error is the same.
I also tried different hash algorithms in the Sign
function but it doesn't change anything.
The error seems to come from the implementation of crypto.Signer
although it works for generating a CSR.
答案1
得分: 0
虽然我几个月前就找到了解决这个问题的方法,但我从未花时间分享答案,但现在是时候了。
当我们直接通过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),它们知道如何生成该结构,但它们都假设数据未经哈希处理,而在crypto.Signer接口中并非如此,因此我们必须使用通用的CKA_RSA_PKCS机制,它只执行原始签名操作。这意味着我们必须自己生成ASN.1结构,我们可以通过为可能使用的所有哈希值提供正确的前缀来实现。
通过crypto.SignerOpts类型的opts参数,我们可以在调用Sign()函数时检索到哈希函数的标识符,类型为crypto.Hash,并应用正确的前缀。
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。
愉快的签名!
英文:
Although I've had the solution to this problem for several months now, I've never taken the time to share the answer but, the time has come.
When we sign things via PKCS #11 directly, we need to manage hash prefixes by manually prefixing the hash with the DigestInfo value referenced here: https://www.rfc-editor.org/rfc/rfc3447#page-43.
To be more precise, for RSASSA-PKCS1-v1_5 signatures, the input to the actual signature function is an ASN.1 DER-encoded structure. PKCS #11 has hash-specific mechanisms (e.g. CKM_SHA256_RSA_PKCS) which know how to generate that structure, but they all assume the data is un-hashed, which is not the case with the crypto.Signer interface, so we have to use the generic CKA_RSA_PKCS mechanism, which just performs the raw signature operation. This means we have to generate the ASN.1 structure ourselves, which we can do by just having the correct prefixes for all the hashes we might want to use.
Thanks to the opts parameter of type crypto.SignerOpts, we can retrieve the identifier of the hash function of type crypto.Hash when the Sign() function is called, 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. However, there is something better to do.
The CKM_RSA_PKCS mechanism provides a signature of the RSASSA-PKCS1-v1_5 type. I leave it to interested readers to do their own research on this old signature scheme, which should no longer be used in new products/software.
Indeed, it is recommended to use the CKM_RSA_PKCS_PSS mechanism, which provides a signature of the RSASSA-PSS type.
Starting from this principle, here's the implementation I now use.
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
}
Prefixes are therefore no longer necessary, however, the correspondence between the hash algorithm identifier and the signature algorithm to be used, as well as the MGF to be used, is necessary.
Finally, in Go, the signature algorithm to be used is no longer x509.SHA256WithRSA, x509.SHA384WithRSA or x509.SHA512WithRSA, but rather SHA256WithRSAPSS, SHA384WithRSAPSS and SHA512WithRSAPSS.
Happy signing.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论