pkcs7

package module
v0.0.0-...-2cdf7eb Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 11, 2024 License: MIT Imports: 26 Imported by: 1

README

pkcs7

Go Reference Build Status

pkcs7 implements parsing and creating signed and enveloped messages.

package main

import (
	"bytes"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"os"

    "github.com/ploynomail/pkcs7"
)

func SignAndDetach(content []byte, cert *x509.Certificate, privkey *rsa.PrivateKey) (signed []byte, err error) {
	toBeSigned, err := NewSignedData(content)
	if err != nil {
		return fmt.Errorf("Cannot initialize signed data: %w", err)
	}
	if err = toBeSigned.AddSigner(cert, privkey, SignerInfoConfig{}); err != nil {
		return fmt.Errorf("Cannot add signer: %w", err)
	}

	// Detach signature, omit if you want an embedded signature
	toBeSigned.Detach()

	signed, err = toBeSigned.Finish()
	if err != nil {
		return fmt.Errorf("Cannot finish signing data: %w", err)
	}

	// Verify the signature
	pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: signed})
	p7, err := pkcs7.Parse(signed)
	if err != nil {
		return fmt.Errorf("Cannot parse our signed data: %w", err)
	}

	// since the signature was detached, reattach the content here
	p7.Content = content

	if bytes.Compare(content, p7.Content) != 0 {
		return fmt.Errorf("Our content was not in the parsed data:\n\tExpected: %s\n\tActual: %s", content, p7.Content)
	}
	if err = p7.Verify(); err != nil {
		return fmt.Errorf("Cannot verify our signed data: %w", err)
	}

	return signed, nil
}

Credits

This is a fork of mozilla-services/pkcs7 which, itself, was a fork of fullsailor/pkcs7.

Documentation

Overview

Package pkcs7 implements parsing and generation of some PKCS#7 structures.

Index

Examples

Constants

View Source
const (
	// EncryptionAlgorithmDESCBC is the DES CBC encryption algorithm
	EncryptionAlgorithmDESCBC = iota

	// EncryptionAlgorithmAES128CBC is the AES 128 bits with CBC encryption algorithm
	// Avoid this algorithm unless required for interoperability; use AES GCM instead.
	EncryptionAlgorithmAES128CBC

	// EncryptionAlgorithmAES256CBC is the AES 256 bits with CBC encryption algorithm
	// Avoid this algorithm unless required for interoperability; use AES GCM instead.
	EncryptionAlgorithmAES256CBC

	// EncryptionAlgorithmAES128GCM is the AES 128 bits with GCM encryption algorithm
	EncryptionAlgorithmAES128GCM

	// EncryptionAlgorithmAES256GCM is the AES 256 bits with GCM encryption algorithm
	EncryptionAlgorithmAES256GCM
)

Variables

View Source
var (
	// Signed Data OIDs
	OIDData                   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1}
	OIDSignedData             = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 2}
	OIDEnvelopedData          = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 3}
	OIDEncryptedData          = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 6}
	OIDAttributeContentType   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 3}
	OIDAttributeMessageDigest = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 4}
	OIDAttributeSigningTime   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 5}

	// Digest Algorithms
	OIDDigestAlgorithmSHA1   = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26}
	OIDDigestAlgorithmSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1}
	OIDDigestAlgorithmSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2}
	OIDDigestAlgorithmSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3}
	OIDDigestAlgorithmSHA224 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 4}

	OIDDigestAlgorithmDSA     = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}
	OIDDigestAlgorithmDSASHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3}

	OIDDigestAlgorithmECDSASHA1   = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
	OIDDigestAlgorithmECDSASHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
	OIDDigestAlgorithmECDSASHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
	OIDDigestAlgorithmECDSASHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}

	// Signature Algorithms
	OIDEncryptionAlgorithmRSAMD5    = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4}  // see https://www.rfc-editor.org/rfc/rfc8017#appendix-A.2.4
	OIDEncryptionAlgorithmRSASHA1   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}  // ditto
	OIDEncryptionAlgorithmRSASHA256 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} // ditto
	OIDEncryptionAlgorithmRSASHA384 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} // ditto
	OIDEncryptionAlgorithmRSASHA512 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} // ditto
	OIDEncryptionAlgorithmRSASHA224 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 14} // ditto

	OIDEncryptionAlgorithmECDSAP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7}
	OIDEncryptionAlgorithmECDSAP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34}
	OIDEncryptionAlgorithmECDSAP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35}

	// Asymmetric Encryption Algorithms
	OIDEncryptionAlgorithmRSA       = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} // see https://www.rfc-editor.org/rfc/rfc8017#appendix-A.2.2
	OIDEncryptionAlgorithmRSAESOAEP = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 7} // see https://www.rfc-editor.org/rfc/rfc8017#appendix-A.2.1

	// Symmetric Encryption Algorithms
	OIDEncryptionAlgorithmDESCBC     = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 7}               // see https://www.rfc-editor.org/rfc/rfc8018.html#appendix-B.2.1
	OIDEncryptionAlgorithmDESEDE3CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 3, 7}         // see https://www.rfc-editor.org/rfc/rfc8018.html#appendix-B.2.2
	OIDEncryptionAlgorithmAES256CBC  = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 42} // see https://www.rfc-editor.org/rfc/rfc3565.html#section-4.1
	OIDEncryptionAlgorithmAES128GCM  = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 6}  // see https://www.rfc-editor.org/rfc/rfc5084.html#section-3.2
	OIDEncryptionAlgorithmAES128CBC  = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 2}  // see https://www.rfc-editor.org/rfc/rfc8018.html#appendix-B.2.5
	OIDEncryptionAlgorithmAES256GCM  = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 46} // see https://www.rfc-editor.org/rfc/rfc5084.html#section-3.2
)
View Source
var ContentEncryptionAlgorithm = EncryptionAlgorithmDESCBC

ContentEncryptionAlgorithm determines the algorithm used to encrypt the plaintext message. Change the value of this variable to change which algorithm is used in the Encrypt() function.

View Source
var EC2IdentityDocumentFixture = `` /* 2165-byte string literal not displayed */
View Source
var ErrNotEncryptedContent = errors.New("pkcs7: content data is a decryptable data type")

ErrNotEncryptedContent is returned when attempting to Decrypt data that is not encrypted data

View Source
var ErrPSKNotProvided = errors.New("pkcs7: cannot encrypt content: PSK not provided")

ErrPSKNotProvided is returned when attempting to encrypt using a PSK without actually providing the PSK.

View Source
var ErrUnsupportedAlgorithm = errors.New("pkcs7: cannot decrypt data: only RSA, DES, DES-EDE3, AES-256-CBC and AES-128-GCM supported")

ErrUnsupportedAlgorithm tells you when our quick dev assumptions have failed

View Source
var ErrUnsupportedAsymmetricEncryptionAlgorithm = errors.New("pkcs7: cannot decrypt data: only RSA PKCS#1 v1.5 and RSA OAEP are supported")

ErrUnsupportedAsymmetricEncryptionAlgorithm is returned when attempting to use an unknown asymmetric encryption algorithm

View Source
var ErrUnsupportedContentType = errors.New("pkcs7: cannot parse data: unimplemented content type")

ErrUnsupportedContentType is returned when a PKCS7 content type is not supported. Currently only Data (1.2.840.113549.1.7.1), Signed Data (1.2.840.113549.1.7.2), and Enveloped Data are supported (1.2.840.113549.1.7.3)

View Source
var ErrUnsupportedEncryptionAlgorithm = errors.New("pkcs7: cannot encrypt content: only DES-CBC, AES-CBC, and AES-GCM supported")

ErrUnsupportedEncryptionAlgorithm is returned when attempting to encrypt content with an unsupported algorithm.

View Source
var ErrUnsupportedKeyEncryptionAlgorithm = errors.New("pkcs7: unsupported key encryption algorithm provided")

ErrUnsupportedKeyEncryptionAlgorithm is returned when an unsupported key encryption algorithm OID is provided.

View Source
var ErrUnsupportedKeyEncryptionHash = errors.New("pkcs7: unsupported key encryption hash provided")

ErrUnsupportedKeyEncryptionHash is returned when an unsupported key encryption hash is provided.

View Source
var ErrUnsupportedKeyType = errors.New("pkcs7: only RSA keys are supported")

ErrUnsupportedKeyType is returned when attempting to encrypting keys using a key that's not an RSA key

View Source
var KeyEncryptionAlgorithm = OIDEncryptionAlgorithmRSA

KeyEncryptionAlgorithm determines the algorithm used to encrypt a content key. Change the value of this variable to change which algorithm is used in the Encrypt() function.

View Source
var KeyEncryptionHash = crypto.SHA256

KeyEncryptionHash determines the crypto.Hash algorithm to use when encrypting a content key. Change the value of this variable to change which algorithm is used in the Encrypt() function.

Functions

func DegenerateCertificate

func DegenerateCertificate(cert []byte) ([]byte, error)

DegenerateCertificate creates a signed data structure containing only the provided certificate or certificate chain.

func Encrypt

func Encrypt(content []byte, recipients []*x509.Certificate) ([]byte, error)

Encrypt creates and returns an envelope data PKCS7 structure with encrypted recipient keys for each recipient public key.

The algorithm used to perform encryption is determined by the current value of the global ContentEncryptionAlgorithm package variable. By default, the value is EncryptionAlgorithmDESCBC. To use a different algorithm, change the value before calling Encrypt(). For example:

ContentEncryptionAlgorithm = EncryptionAlgorithmAES256GCM

TODO(fullsailor): Add support for encrypting content with other algorithms

func EncryptUsingPSK

func EncryptUsingPSK(content []byte, key []byte) ([]byte, error)

EncryptUsingPSK creates and returns an encrypted data PKCS7 structure, encrypted using caller provided pre-shared secret.

func SetFallbackLegacyX509CertificateParserEnabled

func SetFallbackLegacyX509CertificateParserEnabled(v bool)

SetFallbackLegacyX509CertificateParserEnabled enables parsing certificates embedded in a PKCS7 message using the logic from crypto/x509 from before Go 1.23. Go 1.23 introduced a breaking change in case a certificate contains a critical authority key identifier, which is the correct thing to do based on RFC 5280, but it breaks Windows devices performing the Simple Certificate Enrolment Protocol (SCEP), as the certificates embedded in those requests apparently have authority key identifier extensions marked critical.

See https://go-review.googlesource.com/c/go/+/562341 for the change in the Go source.

When SetFallbackLegacyX509CertificateParserEnabled is called with true, it enables parsing using the legacy crypto/x509 certificate parser. It'll first try to parse the certificates using the regular Go crypto/x509 package, but if it fails on the above case, it'll retry parsing the certificates using a copy of the crypto/x509 package based on Go 1.23, but skips checking the authority key identifier extension being critical or not.

func TestDSASignWithOpenSSLAndVerify

func TestDSASignWithOpenSSLAndVerify(t *testing.T)

func TestVerifyEC2

func TestVerifyEC2(t *testing.T)

Types

type Attribute

type Attribute struct {
	Type  asn1.ObjectIdentifier
	Value interface{}
}

Attribute represents a key value pair attribute. Value must be marshalable byte `encoding/asn1`

type DSATestFixture

type DSATestFixture struct {
	Input       []byte
	Certificate *x509.Certificate
}

func UnmarshalDSATestFixture

func UnmarshalDSATestFixture(testPEMBlock string) DSATestFixture

type MessageDigestMismatchError

type MessageDigestMismatchError struct {
	ExpectedDigest []byte
	ActualDigest   []byte
}

MessageDigestMismatchError is returned when the signer data digest does not match the computed digest for the contained content

func (*MessageDigestMismatchError) Error

func (err *MessageDigestMismatchError) Error() string

type PKCS7

type PKCS7 struct {
	Content      []byte
	Certificates []*x509.Certificate
	CRLs         []pkix.CertificateList
	Signers      []signerInfo
	// contains filtered or unexported fields
}

PKCS7 Represents a PKCS7 structure

func Parse

func Parse(data []byte) (p7 *PKCS7, err error)

Parse decodes a DER encoded PKCS7 package

func (*PKCS7) Decrypt

func (p7 *PKCS7) Decrypt(cert *x509.Certificate, pkey crypto.PrivateKey) ([]byte, error)

Decrypt decrypts encrypted content info for recipient cert and private key

func (*PKCS7) DecryptUsingPSK

func (p7 *PKCS7) DecryptUsingPSK(key []byte) ([]byte, error)

DecryptUsingPSK decrypts encrypted data using caller provided pre-shared secret

func (*PKCS7) GetOnlySigner

func (p7 *PKCS7) GetOnlySigner() *x509.Certificate

GetOnlySigner returns an x509.Certificate for the first signer of the signed data payload. If there are more or less than one signer, nil is returned

func (*PKCS7) UnmarshalSignedAttribute

func (p7 *PKCS7) UnmarshalSignedAttribute(attributeType asn1.ObjectIdentifier, out interface{}) error

UnmarshalSignedAttribute decodes a single attribute from the signer info

func (*PKCS7) Verify

func (p7 *PKCS7) Verify() (err error)

Verify is a wrapper around VerifyWithChain() that initializes an empty trust store, effectively disabling certificate verification when validating a signature.

func (*PKCS7) VerifyWithChain

func (p7 *PKCS7) VerifyWithChain(truststore *x509.CertPool) (err error)

VerifyWithChain checks the signatures of a PKCS7 object.

If truststore is not nil, it also verifies the chain of trust of the end-entity signer cert to one of the roots in the truststore. When the PKCS7 object includes the signing time authenticated attr verifies the chain at that time and UTC now otherwise.

func (*PKCS7) VerifyWithChainAtTime

func (p7 *PKCS7) VerifyWithChainAtTime(truststore *x509.CertPool, currentTime time.Time) (err error)

VerifyWithChainAtTime checks the signatures of a PKCS7 object.

If truststore is not nil, it also verifies the chain of trust of the end-entity signer cert to a root in the truststore at currentTime. It does not use the signing time authenticated attribute.

type SignedData

type SignedData struct {
	// contains filtered or unexported fields
}

SignedData is an opaque data structure for creating signed data payloads

Example
// generate a signing cert or load a key pair
cert, err := createTestCertificate(x509.SHA256WithRSA)
if err != nil {
	fmt.Printf("Cannot create test certificates: %s", err)
}

// Initialize a SignedData struct with content to be signed
signedData, err := NewSignedData([]byte("Example data to be signed"))
if err != nil {
	fmt.Printf("Cannot initialize signed data: %s", err)
}

// Add the signing cert and private key
if err := signedData.AddSigner(cert.Certificate, cert.PrivateKey, SignerInfoConfig{}); err != nil {
	fmt.Printf("Cannot add signer: %s", err)
}

// Call Detach() is you want to remove content from the signature
// and generate an S/MIME detached signature
signedData.Detach()

// Finish() to obtain the signature bytes
detachedSignature, err := signedData.Finish()
if err != nil {
	fmt.Printf("Cannot finish signing data: %s", err)
}
pem.Encode(os.Stdout, &pem.Block{Type: "PKCS7", Bytes: detachedSignature})
Output:

func NewSignedData

func NewSignedData(data []byte) (*SignedData, error)

NewSignedData takes data and initializes a PKCS7 SignedData struct that is ready to be signed via AddSigner. The digest algorithm is set to SHA1 by default and can be changed by calling SetDigestAlgorithm.

func (*SignedData) AddCertificate

func (sd *SignedData) AddCertificate(cert *x509.Certificate)

AddCertificate adds the certificate to the payload. Useful for parent certificates

func (*SignedData) AddSigner

func (sd *SignedData) AddSigner(ee *x509.Certificate, pkey crypto.PrivateKey, config SignerInfoConfig) error

AddSigner is a wrapper around AddSignerChain() that adds a signer without any parent.

func (*SignedData) AddSignerChain

func (sd *SignedData) AddSignerChain(ee *x509.Certificate, pkey crypto.PrivateKey, parents []*x509.Certificate, config SignerInfoConfig) error

AddSignerChain signs attributes about the content and adds certificates and signers infos to the Signed Data. The certificate and private key of the end-entity signer are used to issue the signature, and any parent of that end-entity that need to be added to the list of certifications can be specified in the parents slice.

The signature algorithm used to hash the data is the one of the end-entity certificate.

func (*SignedData) Detach

func (sd *SignedData) Detach()

Detach removes content from the signed data struct to make it a detached signature. This must be called right before Finish()

func (*SignedData) Finish

func (sd *SignedData) Finish() ([]byte, error)

Finish marshals the content and its signers

func (*SignedData) GetSignedData

func (sd *SignedData) GetSignedData() *signedData

GetSignedData returns the private Signed Data

func (*SignedData) RemoveAuthenticatedAttributes

func (sd *SignedData) RemoveAuthenticatedAttributes()

RemoveAuthenticatedAttributes removes authenticated attributes from signedData similar to OpenSSL's PKCS7_NOATTR or -noattr flags

func (*SignedData) RemoveUnauthenticatedAttributes

func (sd *SignedData) RemoveUnauthenticatedAttributes()

RemoveUnauthenticatedAttributes removes unauthenticated attributes from signedData

func (*SignedData) SetDigestAlgorithm

func (sd *SignedData) SetDigestAlgorithm(d asn1.ObjectIdentifier)

SetDigestAlgorithm sets the digest algorithm to be used in the signing process.

This should be called before adding signers

func (*SignedData) SetEncryptionAlgorithm

func (sd *SignedData) SetEncryptionAlgorithm(d asn1.ObjectIdentifier)

SetEncryptionAlgorithm sets the encryption algorithm to be used in the signing process.

This should be called before adding signers

func (*SignedData) SignWithoutAttr

func (sd *SignedData) SignWithoutAttr(ee *x509.Certificate, pkey crypto.PrivateKey, config SignerInfoConfig) error

SignWithoutAttr issues a signature on the content of the pkcs7 SignedData. Unlike AddSigner/AddSignerChain, it calculates the digest on the data alone and does not include any signed attributes like timestamp and so on.

This function is needed to sign old Android APKs, something you probably shouldn't do unless you're maintaining backward compatibility for old applications.

type SignerInfoConfig

type SignerInfoConfig struct {
	ExtraSignedAttributes   []Attribute
	ExtraUnsignedAttributes []Attribute
}

SignerInfoConfig are optional values to include when adding a signer

type SigningTimeNotValidError

type SigningTimeNotValidError struct {
	SigningTime time.Time
	NotBefore   time.Time // NotBefore of signer
	NotAfter    time.Time // NotAfter of signer
}

SigningTimeNotValidError is returned when the signing time attribute falls outside of the signer certificate validity.

func (*SigningTimeNotValidError) Error

func (e *SigningTimeNotValidError) Error() string

Directories

Path Synopsis
internal
legacy/x509
Package x509 implements a subset of the X.509 standard.
Package x509 implements a subset of the X.509 standard.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL