pyhanko.sign.signers.csc_signer module

New in version 0.10.0.

Asynchronous Signer implementation for interacting with a remote signing service using the Cloud Signature Consortium (CSC) API.

This implementation is based on version 1.0.4.0 (2019-06) of the CSC API specification.

Usage notes

This module’s CSCSigner class supplies an implementation of the Signer class in pyHanko. As such, it is flexible enough to be used either through pyHanko’s high-level API (sign_pdf() et al.), or through the interrupted signing API.

CSCSigner overview

CSCSigner is only directly responsible for calling the signatures/signHash endpoint in the CSC API. Other than that, it only handles batch control. This means that the following tasks require further action on the API user’s part:

  • authenticating to the signing service (typically using OAuth2);

  • obtaining Signature Activation Data (SAD) from the signing service;

  • provisioning the certificates to embed into the document (usually those are supplied by the signing service as well).

The first two involve a degree of implementation/vendor dependence that is difficult to cater to in full generality, and the third is out of scope for Signer subclasses in general.

However, this module still provides a number of convenient hooks and guardrails that should allow you to fill in these blanks with relative ease. We briefly discuss these below.

Throughout, the particulars of how pyHanko should connect to a signing service are supplied in a CSCServiceSessionInfo object. This object contains the base CSC API URL, the CSC credential ID to use, and authentication data.

Authenticating to the signing service

While the authentication process itself is the API user’s responsibility, CSCServiceSessionInfo includes an oauth_token field that will (by default) be used to populate the HTTP Authorization header for every request.

To handle OAuth-specific tasks, you might want to use a library like OAuthLib.

Obtaining SAD from the signing service

This is done by subclassing CSCAuthorizationInfo and passing an instance to the CSCSigner. The CSCAuthorizationInfo instance should call the signer’s credentials/authorize endpoint with the proper parameters required by the service. See the documentation for CSCAuthorizationInfo for details and= information about helper functions.

Certificate provisioning

In pyHanko’s API, Signer instances need to be initialised with the signer’s certificate, preferably together with other relevant CA certificates. In a CSC context, these are typically retrieved from the signing service by calling the credentials/info endpoint.

This module offers a helper function to handle that task, see fetch_certs_in_csc_credential().

class pyhanko.sign.signers.csc_signer.CSCSigner(session: aiohttp.client.ClientSession, auth_manager: pyhanko.sign.signers.csc_signer.CSCAuthorizationManager, sign_timeout: int = 300, prefer_pss: bool = False, embed_roots: bool = True, client_data: Optional[str] = None, batch_autocommit: bool = True, batch_size: Optional[int] = None, est_raw_signature_size=512)

Bases: pyhanko.sign.signers.pdf_cms.Signer

Implements the Signer interface for a remote CSC signing service. Requests are made asynchronously, using aiohttp.

Parameters
  • session – The aiohttp session to use when performing queries.

  • auth_manager – A CSCAuthorizationManager instance capable of procuring signature activation data from the signing service.

  • sign_timeout – Timeout for signing operations, in seconds. Defaults to 300 seconds (5 minutes).

  • prefer_pss – When signing using an RSA key, prefer PSS padding to legacy PKCS#1 v1.5 padding. Default is False. This option has no effect on non-RSA signatures.

  • embed_roots – Option that controls whether or not additional self-signed certificates should be embedded into the CMS payload. The default is True.

  • client_data – CSC client data to add to any signing request(s), if applicable.

  • batch_autocommit – Whether to automatically commit a signing transaction as soon as a batch is full. The default is True. If False, the caller has to trigger commit() manually.

  • batch_size – The number of signatures to sign in one transaction. This defaults to 1 (i.e. a separate signatures/signHash call is made for every signature).

  • est_raw_signature_size – Estimated raw signature size (in bytes). Defaults to 512 bytes, which, combined with other built-in safety margins, should provide a generous overestimate.

signing_cert: asn1crypto.x509.Certificate

The certificate that will be used to create the signature.

cert_registry: pyhanko_certvalidator.registry.CertificateStore

Collection of certificates associated with this signer. Note that this is simply a bookkeeping tool; in particular it doesn’t care about trust.

get_signature_mechanism(digest_algorithm)

Get the signature mechanism for this signer to use. If signature_mechanism is set, it will be used. Otherwise, this method will attempt to put together a default based on mechanism used in the signer’s certificate.

Parameters

digest_algorithm – Digest algorithm to use as part of the signature mechanism. Only used if a signature mechanism object has to be put together on-the-fly.

Returns

A SignedDigestAlgorithm object.

async format_csc_signing_req(tbs_hashes: List[str], digest_algorithm: str) dict

Populate the request data for a CSC signing request

Parameters
  • tbs_hashes – Base64-encoded hashes that require signing.

  • digest_algorithm – The digest algorithm to use.

Returns

A dict that, when encoded as a JSON object, be used as the request body for a call to signatures/signHash.

async async_sign_raw(data: bytes, digest_algorithm: str, dry_run=False) bytes

Compute the raw cryptographic signature of the data provided, hashed using the digest algorithm provided.

Parameters
  • data – Data to sign.

  • digest_algorithm

    Digest algorithm to use.

    Warning

    If signature_mechanism also specifies a digest, they should match.

  • dry_run – Do not actually create a signature, but merely output placeholder bytes that would suffice to contain an actual signature.

Returns

Signature bytes.

async commit()

Commit the current batch by calling the signatures/signHash endpoint on the CSC service.

This coroutine does not return anything; instead, it notifies all waiting signing coroutines that their signature has been fetched.

class pyhanko.sign.signers.csc_signer.CSCServiceSessionInfo(service_url: str, credential_id: str, oauth_token: Optional[str] = None, api_ver: str = 'v1')

Bases: object

Information about the CSC service, together with the required authentication data.

service_url: str

Base URL of the CSC service. This is the part that precedes /csc/<version>/... in the API endpoint URLs.

credential_id: str

The identifier of the CSC credential to use when signing. The format is vendor-dependent.

oauth_token: Optional[str] = None

OAuth token to use when making requests to the CSC service.

api_ver: str = 'v1'

CSC API version.

Note

This section does not affect any of the internal logic, it only changes how the URLs are formatted.

endpoint_url(endpoint_name)

Complete an endpoint name to a full URL.

Parameters

endpoint_name – Name of the endpoint (e.g. credentials/info).

Returns

A URL.

property auth_headers

HTTP Header(s) necessary for authentication, to be passed with every request.

Note

By default, this supplies the Authorization header with the value of oauth_token as the Bearer value.

Returns

A dict of headers.

class pyhanko.sign.signers.csc_signer.CSCCredentialInfo(signing_cert: asn1crypto.x509.Certificate, chain: List[asn1crypto.x509.Certificate], supported_mechanisms: FrozenSet[str], max_batch_size: int, hash_pinning_required: bool, response_data: dict)

Bases: object

Information about a CSC credential, typically fetched using a credentials/info call. See also fetch_certs_in_csc_credential().

signing_cert: asn1crypto.x509.Certificate

The signer’s certificate.

chain: List[asn1crypto.x509.Certificate]

Other relevant CA certificates.

supported_mechanisms: FrozenSet[str]

Signature mechanisms supported by the credential.

max_batch_size: int

The maximal batch size that can be used with this credential.

hash_pinning_required: bool

Flag controlling whether SAD must be tied to specific hashes.

response_data: dict

The JSON response data from the server as an otherwise unparsed dict.

as_cert_store() pyhanko_certvalidator.registry.CertificateStore

Register the relevant certificates into a CertificateStore and return it.

Returns

A CertificateStore.

async pyhanko.sign.signers.csc_signer.fetch_certs_in_csc_credential(session: aiohttp.client.ClientSession, csc_session_info: pyhanko.sign.signers.csc_signer.CSCServiceSessionInfo, timeout: int = 30) pyhanko.sign.signers.csc_signer.CSCCredentialInfo

Call the credentials/info endpoint of the CSC service for a specific credential, and encode the result into a CSCCredentialInfo object.

Parameters
  • session – The aiohttp session to use when performing queries.

  • csc_session_info – General information about the CSC service and the credential.

  • timeout – How many seconds to allow before time-out.

Returns

A CSCCredentialInfo object with the processed response.

class pyhanko.sign.signers.csc_signer.CSCAuthorizationInfo(sad: str, expires_at: Optional[datetime.datetime] = None)

Bases: object

Authorization data to make a signing request. This is the result of a call to credentials/authorize.

sad: str

Signature activation data; opaque to the client.

expires_at: Optional[datetime.datetime] = None

Expiry date of the signature activation data.

class pyhanko.sign.signers.csc_signer.CSCAuthorizationManager(csc_session_info: pyhanko.sign.signers.csc_signer.CSCServiceSessionInfo, credential_info: pyhanko.sign.signers.csc_signer.CSCCredentialInfo)

Bases: abc.ABC

Abstract class that handles authorisation requests for the CSC signing client.

Note

Implementations may wish to make use of the format_csc_auth_request() convenience method to format requests to the credentials/authorize endpoint.

Parameters
  • csc_session_info – General information about the CSC service and the credential.

  • credential_info – Details about the credential.

async authorize_signature(hash_b64s: List[str]) pyhanko.sign.signers.csc_signer.CSCAuthorizationInfo

Request a SAD token from the signing service, either freshly or to extend the current transaction.

Depending on the lifecycle of this object, pre-fetched SAD values may be used. All authorization transaction management is left to implementing subclasses.

Parameters

hash_b64s – Base64-encoded hash values about to be signed.

Returns

Authorization data.

format_csc_auth_request(num_signatures: int = 1, pin: Optional[str] = None, otp: Optional[str] = None, hash_b64s: Optional[List[str]] = None, description: Optional[str] = None, client_data: Optional[str] = None) dict

Format the parameters for a call to credentials/authorize.

Parameters
  • num_signatures – The number of signatures to request authorisation for.

  • pin – The user’s PIN (if applicable).

  • otp – The current value of an OTP token, provided by the user (if applicable).

  • hash_b64s – An explicit list of base64-encoded hashes to be tied to the SAD. Is optional if the service’s SCAL value is 1, i.e. when hash_pinning_required is false.

  • description – A free-form description of the authorisation request (optional).

  • client_data – Custom vendor-specific data (if applicable).

Returns

A dict that, when encoded as a JSON object, be used as the request body for a call to credentials/authorize.

static parse_csc_auth_response(response_data: dict) pyhanko.sign.signers.csc_signer.CSCAuthorizationInfo

Parse the response from a credentials/authorize call into a CSCAuthorizationInfo object.

Parameters

response_data – The decoded response JSON.

Returns

A CSCAuthorizationInfo object.

property auth_headers

HTTP Header(s) necessary for authentication, to be passed with every request. By default, this delegates to CSCServiceSessionInfo.auth_headers.

Returns

A dict of headers.

class pyhanko.sign.signers.csc_signer.PrefetchedSADAuthorizationManager(csc_session_info: pyhanko.sign.signers.csc_signer.CSCServiceSessionInfo, credential_info: pyhanko.sign.signers.csc_signer.CSCCredentialInfo, csc_auth_info: pyhanko.sign.signers.csc_signer.CSCAuthorizationInfo)

Bases: pyhanko.sign.signers.csc_signer.CSCAuthorizationManager

Simplistic CSCAuthorizationManager for use with pre-fetched signature activation data.

This class is effectively only useful for CSC services that do not require SAD to be pinned to specific document hashes. It allows you to use a SAD that was fetched before starting the signing process, for a one-shot signature.

This can simplify resource management in cases where obtaining a SAD is time-consuming, but the caller still wants the conveniences of pyHanko’s high-level API without having to keep too many pyHanko objects in memory while waiting for a credentials/authorize call to go through.

Legitimate uses are limited, but the implementation is trivial, so we provide it here.

Parameters
  • csc_session_info – General information about the CSC service and the credential.

  • credential_info – Details about the credential.

  • csc_auth_info – The pre-fetched signature activation data.

async authorize_signature(hash_b64s: List[str]) pyhanko.sign.signers.csc_signer.CSCAuthorizationInfo

Return the prefetched SAD, or raise an error if called twice.

Parameters

hash_b64s – List of hashes to be signed; ignored.

Returns

The prefetched authorisation data.