Files
2025-08-13 13:06:41 +02:00

125 lines
3.3 KiB
Go

package pkinit
import (
"crypto"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"fmt"
)
// PKCS7Sign signs the data according to PKCS#7.
func PKCS7Sign(data []byte, key *rsa.PrivateKey, cert *x509.Certificate) ([]byte, error) {
serializedData, err := asn1.Marshal(data)
if err != nil {
return nil, fmt.Errorf("marshal data: %w", err)
}
rawCert, err := RawCertificate(cert)
if err != nil {
return nil, fmt.Errorf("marshal certificate: %w", err)
}
digest := sha1.Sum(data)
serializedDigest, err := asn1.Marshal(digest[:])
if err != nil {
return nil, fmt.Errorf("marshal digest: %w", err)
}
serializedPKInitOID, err := asn1.Marshal(idPKINITAuthDataOID)
if err != nil {
return nil, fmt.Errorf("marshal PKInit OID: %w", err)
}
sha1AlgorithmIdentifier := pkix.AlgorithmIdentifier{
Algorithm: sha1HashOID,
}
rsaWithSHA1AlgorithmIdentifier := pkix.AlgorithmIdentifier{
Algorithm: sha1WithRSAEncryptionOID,
}
authenticatedAttributes := []Attribute{
{
// ContentType
Type: contentTypeOID,
// id-pkinit-authData
Value: asn1.RawValue{Tag: 17, IsCompound: true, Bytes: serializedPKInitOID},
},
{
// MessageDigest
Type: messageDigestOID,
Value: asn1.RawValue{Tag: 17, IsCompound: true, Bytes: serializedDigest},
},
}
signature, err := signAuthenicatedAttributes(authenticatedAttributes, key)
if err != nil {
return nil, fmt.Errorf("sign authenticated data: %w", err)
}
signedDataBytes, err := asn1.Marshal(SignedData{
Version: 3,
DigestAlgorithmIdentifiers: []pkix.AlgorithmIdentifier{sha1AlgorithmIdentifier},
ContentInfo: ContentInfo{
// id-pkinit-authData
ContentType: idPKINITAuthDataOID,
Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: serializedData, IsCompound: true},
},
Certificates: rawCert,
SignerInfos: []SignerInfo{
{
Version: 1,
IssuerAndSerialNumber: IssuerAndSerial{
IssuerName: asn1.RawValue{FullBytes: cert.RawIssuer},
SerialNumber: cert.SerialNumber,
},
DigestAlgorithm: sha1AlgorithmIdentifier,
AuthenticatedAttributes: authenticatedAttributes,
DigestEncryptionAlgorithm: rsaWithSHA1AlgorithmIdentifier,
EncryptedDigest: signature,
},
},
})
if err != nil {
return nil, fmt.Errorf("marshal signed data: %w", err)
}
contentInfo := ContentInfo{
// signed data
ContentType: signedDataOID,
Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: signedDataBytes, IsCompound: true},
}
signedContent, err := asn1.Marshal(contentInfo)
if err != nil {
return nil, fmt.Errorf("marshal signed content: %w", err)
}
return signedContent, nil
}
func signAuthenicatedAttributes(attrs []Attribute, key *rsa.PrivateKey) ([]byte, error) {
rawAuthenticatedAttributesAsSequence, err := asn1.Marshal(struct {
A []Attribute `asn1:"set"`
}{A: attrs})
if err != nil {
return nil, fmt.Errorf("marshal authenticated data: %w", err)
}
// Remove the leading sequence octets
var rawAuthenticatedAttributes asn1.RawValue
_, err = asn1.Unmarshal(rawAuthenticatedAttributesAsSequence, &rawAuthenticatedAttributes)
if err != nil {
return nil, fmt.Errorf("remove sequence bytes: %w", err)
}
hash := sha1.Sum(rawAuthenticatedAttributes.Bytes)
return rsa.SignPKCS1v15(nil, key, crypto.SHA1, hash[:])
}