PKCS#7: SignedData and SignerInfo
To support Authenticode, this library includes some code to parse and validate SignedData structures. These are defined in several RFCs, which used to be called PKCS#7 The structure of all relevant RFC’s follow ASN.1 notation to define the relevant structures. These definitions are not always easily digested, but it does show which fields are available.
This chapter of the documentation shows how these basic structures work, so we can dive deep into their operations in the next chapter.
The following diagram shows the relation between SignedData and SignerInfo:
Note that although this diagram is not very complicated, when discussing Authenticode, we will be creating multiple SignedData and SignerInfo structures, nested in each other, so it’s important to fully understand this structure.
SignedData
The SignedData object is the root structure for sending encrypted data in PKCS#7.
- class signify.pkcs7.SignedData(asn1: SignedData)
A generic SignedData object. The SignedData object is defined in RFC2315 and RFC5652 (amongst others) and defines data that is signed by one or more signers.
It is based on the following ASN.1 object (as per RFC2315):
SignedData ::= SEQUENCE { version Version, digestAlgorithms DigestAlgorithmIdentifiers, contentInfo ContentInfo, certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL, crls [1] IMPLICIT CertificateRevocationLists OPTIONAL, signerInfos SignerInfos }
In general, it describes some form of data, that is (obviously) signed. In the ASN.1 structure, you see the
contentInfo, which describes the signed content. (Seecontent_typeandcontent_asn1.Each
SignedDataobject may contain multiple signers. Information about these is found insigner_infos, pointing to one or moreSignerInfoclasses.Additionally,
certificatescontains any additional (intermediate) certificates that may be required to verify these signers.- property certificates: CertificateStore
A list of all included certificates in the SignedData. These can be used to determine a valid validation path from the signer to a root certificate.
- property content_asn1: Asn1Value
The actual content, as parsed by the
content_typespec.
- property content_type: str
The class of the type of the content in the object.
- property digest_algorithm: HashFunction
The digest algorithm, i.e. the hash algorithm, that is used by the signers of the data.
- classmethod from_envelope(data: bytes, *args: Any, **kwargs: Any) Self
Loads a
SignedDataobject from raw data that contains ContentInfo.- Parameters:
data – The bytes to parse
- get_content_digest() bytes
Returns the actual digest of the content of the SignedData object, adhering to the specs in RFC2315, 9.3; the identifier (tag) and length need to be stripped for hashing.
- property signer_info: SignerInfo
The included
signer_info.SignerInfoobject, if there’s one.
- property signer_infos: Sequence[SignerInfo]
A list of all included
signer_info.SignerInfoobjects
- verify(verification_context: VerificationContext | None = None, *, cs_verification_context: VerificationContext | None = None, trusted_certificate_store: CertificateStore | None = None, extended_key_usages: list[str] | None = None, verification_context_kwargs: dict[str, Any] | None = None, countersignature_mode: Literal['strict', 'permit', 'ignore'] = 'strict') Iterable[list[Certificate]]
Verifies the SignedData structure:
Verifies that the content, when hashed, is the same as the value in
SignerInfo.message_digestIn the case of a countersigner, calls
check_message_digest()on the countersigner to verify that the hashed value ofSignerInfo.encrypted_digestis contained in the countersigner.Verifies the chain of the countersigner up to a trusted root, see
SignerInfo.verify()andRFC3161SignedData.verify()Verifies the chain of the signer up to a trusted root, see
SignerInfo.verify()
In the case of a countersigner, the verification is performed using the timestamp of the
CounterSignerInfo, otherwise now is assumed. If there is no countersigner, you can override this by specifying a different timestamp in theVerificationContext. Note that you cannot set a timestamp when checking against the CRL; this is not permitted by the underlying library. If you need to do this, you must therefore set countersignature_mode toignore.- Parameters:
verification_context – The VerificationContext for verifying the chain of the
SignerInfo. The timestamp is overridden in the case of a countersigner. Default stores aretrusted_certificate_storeand the certificates of thisSignedDataobject. Required EKU is provided asextended_key_usagescs_verification_context – The VerificationContext for verifying the chain of the
CounterSignerInfo. The timestamp is overridden in the case of a countersigner. Default stores aretrusted_certificate_storeand the certificates of thisSignedDataobject. Required EKU istime_stamping.trusted_certificate_store – A
CertificateStoreobject that contains a list of trusted certificates to be used whenNoneis passed to eitherverification_contextorcs_verification_contextand aVerificationContextis created.extended_key_usages – EKU’s to check for in the verification context of this
SignedDataobject.verification_context_kwargs (dict) – If provided, keyword arguments that are passed to the instantiation of
VerificationContexts created in this function. Used for e.g. providing a timestamp.countersignature_mode (str) – Changes how countersignatures are handled. Defaults to ‘strict’, which means that errors in the countersignature result in verification failure. If set to ‘permit’, the countersignature is checked, but when it errors, it is verified as if the countersignature was never set. When set to ‘ignore’, countersignatures are never checked.
- Raises:
VerificationError – when the verification failed
- Returns:
A list of valid certificate chains for this SignedData.
SignerInfo
- class signify.pkcs7.SignerInfo(asn1: SignerInfo, parent: SignedData | None = None)
The SignerInfo class is defined in RFC2315 and RFC5652 (amongst others) and defines the per-signer information in a
SignedDatastructure.It is based on the following ASN.1 object (as per RFC2315):
SignerInfo ::= SEQUENCE { version Version, issuerAndSerialNumber IssuerAndSerialNumber, digestAlgorithm DigestAlgorithmIdentifier, authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL, digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier, encryptedDigest EncryptedDigest, unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL }
The most important part of this structure are the authenticated attributes. These will at least contain the hash of the content of the
SignedDatastructure. We can verify this hash by hashing the same content using the hash indigest_algorithmThe
encrypted_digestcontains a signature by the issuer over these authenticated attributes (the authenticated attributes are hashed and verified using thedigest_encryption_algorithm). Theissuerandserial_numbercontains a reference to the certificate of the issuer, that is used for this signature.This class defines how a certain signer, (identified by their
issuer)- asn1
The underlying ASN.1 data object
- parent
The parent
SignedDataobject (or if other SignerInfos are present, it may be another object)
- property authenticated_attributes: dict[str, list[Asn1Value]]
A SignerInfo object can contain both signed and unsigned attributes. These contain additional information about the signature, but also the content type and message digest. The difference between signed and unsigned is that unsigned attributes are not validated.
The type of this attribute is a dictionary. You should not need to access this value directly, rather using one of the attributes listed below.
- check_message_digest(data: bytes) bool
Given the data, returns whether the hash_algorithm and message_digest match the data provided.
- property content_type: str | None
This is an authenticated attribute, containing the content type of the content being signed.
- property countersigner: CounterSignerInfo | None
This is an unauthenticated attribute, containing the countersigner of the SignerInfo.
- property digest_algorithm: HashFunction
The digest algorithm, i.e. the hash algorithm, under which the content and the authenticated attributes are signed.
- property digest_encryption_algorithm: str
This is the algorithm used for signing the digest with the signer’s key.
- property encrypted_digest: bytes
The result of encrypting the message digest and associated information with the signer’s private key.
- property issuer: CertificateName
The issuer of the SignerInfo, i.e. the certificate of the signer of the SignedData object.
- property message_digest: bytes | None
This is an authenticated attribute, containing the signed digest of the data.
- potential_chains(context: VerificationContext) Iterable[list[Certificate]]
Retrieves all potential chains from this SignerInfo instance.
- Parameters:
context (VerificationContext) – The context
- Returns:
A list of potential certificate chains for this SignerInfo.
- property serial_number: int
The serial number as specified by the issuer.
- property signing_time: datetime | None
This is an authenticated attribute, containing the timestamp of signing. Note that this should only be present in countersigner objects.
- property unauthenticated_attributes: dict[str, list[Asn1Value]]
A SignerInfo object can contain both signed and unsigned attributes. These contain additional information about the signature, but also the content type and message digest. The difference between signed and unsigned is that unsigned attributes are not validated.
The type of this attribute is a dictionary. You should not need to access this value directly, rather using one of the attributes listed below.
- verify(context: VerificationContext, *, countersigner_context: VerificationContext | None = None, countersignature_mode: Literal['strict', 'permit', 'ignore'] = 'strict') Iterable[list[Certificate]]
Verifies that this
SignerInfoverifies up to a chain with the root of a trusted certificate.- Parameters:
context – The context for verifying the SignerInfo.
countersigner_context – The VerificationContext for verifying the chain of the
CounterSignerInfo.countersignature_mode –
Changes how countersignatures are handled. Defaults to
strict, which means that errors in the countersignature result in verification failure.If set to
permit, the countersignature is checked, but when it errors, it is verified as if the countersignature was never set.When set to
ignore, countersignatures are never checked.
- Returns:
A list of valid certificate chains for this SignerInfo.
- Raises:
AuthenticodeVerificationError – When the SignerInfo could not be verified.
CounterSignerInfo
- class signify.pkcs7.CounterSignerInfo(asn1: SignerInfo, parent: SignedData | None = None)
A counter-signer provides information about when a
SignerInfowas signed. It basically acts as the SignerInfo of the SignerInfo, linking the message digest to the original SignerInfo’s encrypted_digest.This normally works by sending the digest of the SignerInfo to an external trusted service, that will include a signed time in its response.