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: ClientSession, auth_manager: 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:
Signer
Implements the
Signer
interface for a remote CSC signing service. Requests are made asynchronously, usingaiohttp
.- 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
. IfFalse
, the caller has to triggercommit()
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: Certificate
The certificate that will be used to create the signature.
- cert_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 ofoauth_token
as theBearer
value.- Returns
A
dict
of headers.
- class pyhanko.sign.signers.csc_signer.CSCCredentialInfo(signing_cert: Certificate, chain: List[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 alsofetch_certs_in_csc_credential()
.- signing_cert: Certificate
The signer’s certificate.
- chain: List[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() CertificateStore
Register the relevant certificates into a
CertificateStore
and return it.- Returns
- async pyhanko.sign.signers.csc_signer.fetch_certs_in_csc_credential(session: ClientSession, csc_session_info: CSCServiceSessionInfo, timeout: int = 30) CSCCredentialInfo
Call the
credentials/info
endpoint of the CSC service for a specific credential, and encode the result into aCSCCredentialInfo
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] = 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] = None
Expiry date of the signature activation data.
- class pyhanko.sign.signers.csc_signer.CSCAuthorizationManager(csc_session_info: CSCServiceSessionInfo, credential_info: CSCCredentialInfo)
Bases:
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 thecredentials/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]) 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) CSCAuthorizationInfo
Parse the response from a
credentials/authorize
call into aCSCAuthorizationInfo
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: CSCServiceSessionInfo, credential_info: CSCCredentialInfo, csc_auth_info: CSCAuthorizationInfo)
Bases:
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]) 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.