PKI-PYTHON-CLIENT(1)

🌈 PKI-PYTHON-CLIENT(1) β€” Dogtag Python Client API 🌈

This is the Dogtag Certificate System page for pki-python-client, aka the Dogtag Python Client API. Bestie, we're going full group-chat mode, but every fact stays exactly where it belongs πŸ’–

🌸 NAME 🌸

pki-python-client β€” Dogtag Python Client API

Dogtag is an enterprise software system designed to manage enterprise Public Key Infrastructure (PKI) deployments. These pages document the Python client API that can be used to interact with Dogtag's REST API to request and issue certificates, store secrets in the KRA etc. ✨


πŸ’… PKI PACKAGE πŸ’…

🧚 pki Package 🧚

This module contains top-level classes and functions used by the Dogtag project.

πŸŽ€ class pki.Attribute(name, value) πŸŽ€

Bases: object

Class representing a key/value pair.

This object is the basis of the representation of a ResourceMessage. It's giving foundational, lowkey important. πŸ’…

🧺 class pki.AttributeList 🧺

Bases: object

Class representing a list of attributes.

This class is needed because of a JavaMapper used in the REST API.

πŸ’₯ exception pki.BadRequestException(message, exception=None, code=None, class_name=None) πŸ’₯

Bases: pki.PKIException

Bad Request Exception: return code = 400

πŸ¦‹ exception pki.CertNotFoundException(message, exception=None, code=None, class_name=None) πŸ¦‹

Bases: pki.ResourceNotFoundException

Cert Not Found Exception: return code = 404

βš”οΈ exception pki.ConflictingOperationException(message, exception=None, code=None, class_name=None) βš”οΈ

Bases: pki.PKIException

Conflicting Operation Exception: return code = 409

πŸ” class pki.FIPS πŸ”

Bases: object

static is_enabled()

🚫 exception pki.ForbiddenException(message, exception=None, code=None, class_name=None) 🚫

Bases: pki.PKIException

Forbidden Exception: return code = 403

πŸ‘₯ exception pki.GroupNotFoundException(message, exception=None, code=None, class_name=None) πŸ‘₯

Bases: pki.ResourceNotFoundException

Group Not Found Exception: return code = 404

πŸ’¨ exception pki.HTTPGoneException(message, exception=None, code=None, class_name=None) πŸ’¨

Bases: pki.PKIException

Gone Exception: return code = 410

πŸ”‘ exception pki.KeyNotFoundException(message, exception=None, code=None, class_name=None) πŸ”‘

Bases: pki.ResourceNotFoundException

Key Not Found Exception: return code 404

Bases: object

Stores the information of the resteasy's Link object sent by the server for a resource.

  • classmethod from_json(attr_list) β€” Generate Link from JSON
    • Parameters: attr_list (str) β€” JSON representation of Link
    • Returns: pki.Link

🧨 exception pki.PKIException(message, exception=None, code=None, class_name=None) 🧨

Bases: Exception, pki.ResourceMessage

Base exception class for REST Interface. Main character energy, honestly. πŸ‘‘

  • classmethod from_json(json_value) β€” Construct PKIException from JSON.
    • :param json_value: JSON representation of the exception.
    • :type json_value: str
    • :return: pki.PKIException

πŸͺͺ exception pki.ProfileNotFoundException(message, exception=None, code=None, class_name=None) πŸͺͺ

Bases: pki.ResourceNotFoundException

Profile Not Found Exception: return code = 404

πŸ“ class pki.PropertyFile(filename, delimiter='=') πŸ“

Bases: object

Class to manage property files. The contents of the property file are maintained internally as a list of properties.

Properties are strings of the format <name> <delimiter> <value> where '=' is the default delimiter.

  • get(name) β€” Get the value of the specified property.
    • Parameters: name (str) β€” name of property to be fetched.
    • Returns: str β€” value of property
  • index(name) β€” Find the index (position) of a property in a property file.
    • Parameters: name (str) β€” name of property
    • Returns: int β€” index of property.
  • insert_line(index, line) β€” Insert property into the list of properties maintained by this object at the specified location (index).
    • Parameters:
      • index (int) β€” point at which to insert value.
      • line (str) β€” value to be inserted.
    • Returns: None
  • read() β€” Read from property file into the list of properties maintained by this object.
    • Returns: None
  • remove(name) β€” Remove property from list of properties maintained by this object.
    • Parameters: name (str) β€” name of property to be removed.
    • Returns: None
  • remove_line(index) β€” Remove property at specified index from the properties list.
    • Parameters: index (int) β€” location of property to be removed.
    • Returns: None
  • set(name, value, index=None) β€” Set value of specified property.
    • Parameters:
      • name (str) β€” name of property to set.
      • value (str) β€” value to set
      • index (int) β€” (optional) position of property
    • Returns: None
  • show() β€” Print the contents of the list of properties maintained by this object to STDOUT.
    • Returns: None
  • write() β€” Write the list of properties maintained by this object to the property file.
    • Returns: None

πŸ“¨ exception pki.RequestNotFoundException(message, exception=None, code=None, class_name=None) πŸ“¨

Bases: pki.ResourceNotFoundException

Request Not Found Exception: return code = 404

πŸ—‚οΈ class pki.ResourceMessage(class_name) πŸ—‚οΈ

Bases: object

This class is the basis for the various types of key requests. It is essentially a list of attributes.

  • add_attribute(name, value) β€” Add an attribute to the list.
    • Parameters:
      • name (str) β€” name of attribute to add
      • value (str) β€” value to add
    • Returns: None
  • get_attribute_value(name) β€” Get the value of a given attribute.
    • Parameters: name (str) β€” name of attribute to retrieve
    • Returns: str β€” value of parameter

πŸ•³οΈ exception pki.ResourceNotFoundException(message, exception=None, code=None, class_name=None) πŸ•³οΈ

Bases: pki.PKIException

Not Found Exception: return code = 404

πŸ”’ exception pki.UnauthorizedException(message, exception=None, code=None, class_name=None) πŸ”’

Bases: pki.PKIException

Unauthorized Exception: return code = 401

πŸ‘€ exception pki.UserNotFoundException(message, exception=None, code=None, class_name=None) πŸ‘€

Bases: pki.ResourceNotFoundException

User Not Found Exception: return code = 404

πŸͺ„ pki.convert_x509_name_to_dn(name) πŸͺ„

Convert X.509 Name into NSS-style DN string.

See also:

πŸ“ pki.generate_password πŸ“

pki.generate_password(charset='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#*+,-./:;^_|~', length=12)

This function generates FIPS-compliant password.

See sftk_newPinCheck() in the following file: https://dxr.mozilla.org/nss/source/nss/lib/softoken/fipstokn.c

The minimum password length is FIPS_MIN_PIN Unicode characters.

The password must contain at least 3 character classes:

  • digits (string.digits)
  • ASCII lowercase letters (string.ascii_lowercase)
  • ASCII uppercase letters (string.ascii_uppercase)
  • ASCII non-alphanumeric characters (PUNCTUATIONS)
  • non-ASCII characters

If an ASCII uppercase letter is the first character of the password, the uppercase letter is not counted toward its character class.

If a digit is the last character of the password, the digit is not counted toward its character class.

The FIPS_MIN_PIN is defined in the following file: https://dxr.mozilla.org/nss/source/nss/lib/softoken/pkcs11i.h

#define FIPS_MIN_PIN 7

🧾 pki.get_info(name) 🧾

πŸ›‘οΈ pki.handle_exceptions() πŸ›‘οΈ

Decorator handling exceptions from REST methods.

🏷️ pki.implementation_version() 🏷️

Return implementation version.

  • Returns: str β€” implementation version

πŸ“œ pki.specification_version() πŸ“œ

Return specification version.

  • Returns: str β€” specification version

🫢 account Module 🫢

class pki.account.AccountClient(connection, subsystem=None)

Bases: object

Class used to associate an authentication session variable with a connection.

To use this class:

  • set the authentication credentials with the connection,
  • create an AccountClient and then call login().
  • further operations in this session will use the same authentication credentials without re-authentication.
  • call logout() to invalidate the session.
  • login() β€” Login to account REST interface. If login is successful, an authentication session variable is associated with the connection.
    • Returns: None
  • logout() β€” Logs out of the session. Authentication session variables are invalidated for the connection
    • Returns: None

πŸ¦‹ cert Module πŸ¦‹

class pki.cert.CertClient(connection)

Bases: object

okay so this class is literally the mirror image of the CertResource Java interface, the one that defines the REST API for Certificate resources πŸ’… everything you can do to a cert over REST, you can do from here.

  • approve_request(request_id, cert_review_response=None) β€” approves a certificate enrollment request ✨ if cert_review_response is None, it does a review request operation to go fetch the CertReviewResponse object for you. needs agent-level authentication, no cap.
  • assign_request(request_id, cert_review_response) β€” assigns a certificate enrollment request. same deal: if cert_review_response is None, a review request operation happens to fetch the CertReviewResponse object. agent-level auth required 🫢
  • cancel_request(request_id, cert_review_response=None) β€” cancels a certificate enrollment request. cert_review_response being None again triggers the review request operation to fetch the CertReviewResponse. agent-level authentication, always 🌸
  • create_enrollment_request(profile_id, inputs) β€” fetches the enrollment request object for the profile you passed and sets values on its attributes using the values in the inputs dictionary. returns the CertEnrollmentRequest object, which you can then submit to enroll a cert πŸ’–
  • enroll_cert(profile_id, inputs, authority=None) β€” the convenience queen πŸ‘‘ enrolls a cert for a given profile id. inputs should be a dictionary with values for the profile attributes of an enrollment request.

call it with valid arguments and it creates an enrollment request, submits it to the server, approves the certificate requests generated for the enrollment, and returns a list of CertData objects for all the certificates generated as part of this enrollment. literally the whole pipeline in one call.

⚠️ Note: this method supports only certificate enrollment where only one agent approval is sufficient. so if your setup needs more than one, this isn't the one bestie 😭

requires agent-level authentication. returns a list of CertEnrollmentResult objects.

  • get_cert(cert_serial_number) β€” returns a CertData object for a particular certificate πŸ”₯
  • get_enrollment_template(profile_id) β€” fetches the enrollment template for the given profile id. first time around, the request actually goes to the server; the retrieved CertEnrollmentRequest object then gets cached locally for future requests. returns a CerEnrollmentRequest object (yes, that's how it's spelled on the page, i'm not fixing it πŸ™ˆ).
  • get_request(request_id) β€” gets info on a certificate request with the given request ID. returns a CertRequestInfo object ✨
  • hold_cert(cert_serial_number, comments=None, authority=None) β€” puts a certificate on-hold. it calls the revoke_cert method with reason CertRevokeRequest.REASON_CERTIFICATE_HOLD. returns a CertRequestInfo object. this one needs an agent's authentication cert in the connection object πŸŽ€
  • list_certs(max_results=None, max_time=None, start=None, size=None, **cert_search_params) β€” returns a CertDataInfoCollection object with information about all the certificates that satisfy the search criteria. if cert_search_request=None, you get all the certificates. everything. the whole shelf πŸ’€
  • list_enrollment_templates(start=None, size=None) β€” gets the list of profile templates the CA supports. start and size decide where the list starts and how long it is. returns a ProfileDataInfoCollection object πŸ¦‹
  • list_requests(request_status=None, request_type=None, from_request_id=None, size=None, max_results=None, max_time=None) β€” queries for a list of certificates using the arguments you passed. returns a CertRequestInfoCollection object 🌸
  • reject_request(request_id, cert_review_response=None) β€” rejects a certificate enrollment request. cert_review_response=None β†’ review request operation to fetch the CertReviewResponse object. agent-level auth required πŸ’œ
  • review_cert(cert_serial_number) β€” reviews a certificate. returns a CertData object with a nonce. needs an agent's authentication cert in the connection object ✨
  • review_request(request_id) β€” reviews a certificate enrollment request. returns a CertReviewResponse object, which has the nonce from the server that you need to actually perform an action on the request πŸ”‘
  • revoke_ca_cert(cert_serial_number, revocation_reason=None, invalidity_date=None, comments=None, nonce=None, authority=None) β€” revokes a CA certificate. returns a CertRequestInfo object with info about the request. requires an agent's authentication cert in the connection object 🫢
  • revoke_cert(cert_serial_number, revocation_reason=None, invalidity_date=None, comments=None, nonce=None, authority=None) β€” revokes a certificate. returns a CertRequestInfo object with info about the request. same requirement: an agent's authentication cert in the connection object πŸ”₯
  • submit_enrollment_request(enrollment_request, authority=None) β€” submits the CertEnrollmentRequest object to the server. returns a CertRequestInfoCollection object with info about the certificate requests enrolled at the CA πŸ“¬
  • unassign_request(request_id, cert_review_response) β€” un-assigns a certificate enrollment request. cert_review_response=None β†’ review request operation to fetch the CertReviewResponse object. agent-level authentication required 🌈
  • unrevoke_cert(cert_serial_number, authority=None) β€” un-revokes a revoked certificate. returns a CertRequestInfo object. needs an agent's authentication cert in the connection object πŸ’…
  • update_request(request_id, cert_review_response) β€” updates a certificate enrollment request. cert_review_response=None β†’ review request operation to fetch the CertReviewResponse object. agent-level auth, as always πŸŽ€
  • validate_request(request_id, cert_review_response) β€” validates a certificate enrollment request. and yeah, cert_review_response=None β†’ review request operation to fetch the CertReviewResponse object. agent-level authentication ✨

πŸ¦‹ class pki.cert.CertData

Bases: object

the class holding certificate data, exactly as it comes back from getCert() πŸ‘‘

  • classmethod from_json(attr_list) β€” returns a CertData object from a JSON dict.
  • the attribute map, verbatim because this is load-bearing:
json_attribute_names = {'Encoded': 'encoded', 'IssuerDN':
'issuer_dn', 'Link': 'link', 'Nonce': 'nonce', 'NotAfter':
'not_after', 'NotBefore': 'not_before', 'PKCS7CertChain':
'pkcs7_cert_chain', 'PrettyPrint': 'pretty_repr', 'Status':
'status', 'SubjectDN': 'subject_dn', 'id': 'serial_number'}

πŸ“ class pki.cert.CertDataInfo

Bases: object

the class with the info that lives inside a CertRecord on the CA β€” this is the stuff you get back when you search or list certificate records πŸ”

  • classmethod from_json(attr_list) β€” returns a CertDataInfo object from a JSON dict.
  • the attribute map, again verbatim:
json_attribute_names = {'IssuedBy': 'issued_by', 'IssuedOn':
'issued_on', 'KeyAlgorithmOID': 'key_algorithm_oid', 'KeyLength':
'key_length', 'Link': 'link', 'NotValidAfter': 'not_valid_after',
'NotValidBefore': 'not_valid_before', 'Status': 'status',
'SubjectDN': 'subject_dn', 'Type': 'type', 'Version': 'version',
'id': 'serial_number'}

🧚 class pki.cert.CertDataInfoCollection

Bases: object

a class holding a list of CertDataInfo objects plus their respective link objects 🫢 this is what comes back when you search/list certificate records in the CA.

  • classmethod from_json(json_value) β€” populates the object from JSON input ✨

πŸ‘‘ class pki.cert.CertEnrollmentRequest(profile_id=None, renewal=False, serial_number=None, remote_host=None, remote_address=None, inputs=None, outputs=None)

Bases: object

this class encapsulates the parameters required for a certificate enrollment request. all the knobs, one box πŸŽ€

  • add_input(profile_input) β€” the page just drops this one on the list, no blurb, iykyk.
  • add_output(profile_output) β€” same energy, listed with no description.
  • classmethod from_json(attr_list) β€” listed, no description on the page.
  • get_input(profile_input_name) β€” listed, no description.
  • get_output(profile_output_name) β€” listed, no description.
  • json_attribute_names β€” the mapping, verbatim:
json_attribute_names = {'Input': 'inputs', 'Output': 'outputs',
'ProfileID': 'profile_id', 'RemoteAddress': 'remote_address',
'RemoteHost': 'remote_host', 'Renewal': 'renewal',
'SerialNumber': 'serial_number'}
  • remove_input(profile_input_name) β€” listed, no description.
  • remove_output(profile_output_name) β€” listed, no description.

πŸ’– class pki.cert.CertEnrollmentResult(request, cert)

Bases: object

the class containing results of an enrollment request 🌸 this structure holds info about the cert request that got generated plus any certificates that got issued.


🌈 class pki.cert.CertRequestInfo

Bases: object

an object of this class represents a certificate request, periodt.

  • classmethod from_json(attr_list) β€” listed, no description.
  • the attribute map, verbatim:
json_attribute_names = {'certId': 'cert_id', 'certRequestType':
'cert_request_type', 'certURL': 'cert_url', 'errorMessage':
'error_message', 'operationResult': 'operation_result',
'requestStatus': 'request_status', 'requestType': 'request_type',
'requestURL': 'request_url'}

πŸ’€ class pki.cert.CertRequestInfoCollection

Bases: object

a class holding a list of CertRequestInfo objects πŸ“š this is what comes back when you list certificate request records in the CA.

  • classmethod from_json(json_value) β€” populates the object from JSON input.

⭐ class pki.cert.CertRequestStatus

Bases: object

the class containing all the valid cert statuses βœ… that's it, that's the whole class. iconic minimalism.

CANCELED = 'canceled'

COMPLETE = 'complete'

PENDING = 'pending'

REJECTED = 'rejected'

okay so those four are literally just the status strings, lowercase, that's the whole vibe πŸ’… no cap, that's it.


class pki.cert.CertReviewResponse(profile_id=None, renewal=False, serial_number=None, remote_host=None, remote_address=None, inputs=None, outputs=None, nonce=None, request_id=None, request_type=None, request_status=None, request_owner=None, request_creation_time=None, request_modification_time=None, request_notes=None, profile_approval_by=None, profile_set_id=None, profile_is_visible=None, profile_name=None, profile_description=None, profile_remote_host=None, profile_remote_address=None, policy_sets=None)

Bases: pki.cert.CertEnrollmentRequest

So an object of this class is literally the response the server hands you back when you're reviewing a certificate enrollment request πŸŽ€ and it carries a nonce with it β€” and bestie, you need that nonce to actually perform an action on the request. No nonce, no slay.

classmethod from_json(attr_list) β€” yeah, it builds one of these out of a JSON attribute list, iconic.

json_attribute_names = {'Input': 'inputs', 'Output': 'outputs',
'ProfileID': 'profile_id', 'ProfilePolicySet': 'policy_sets',
'RemoteAddress': 'remote_address', 'RemoteHost': 'remote_host',
'Renewal': 'renewal', 'SerialNumber': 'serial_number',
'profileApprovedBy': 'profile_approved_by', 'profileDescription':
'profile_description', 'profileIsVisible': 'profile_is_visible',
'profileName': 'profile_name', 'profileRemoteAddr':
'profile_remote_address', 'profileRemoteHost':
'profile_remote_host', 'profileSetId': 'profile_set_id',
'requestCreationTime': 'request_creation_time', 'requestId':
'request_id', 'requestModificationTime':
'request_modification_time', 'requestNotes': 'request_notes',
'requestOwner': 'request_owner', 'requestStatus':
'request_status', 'requestType': 'request_type'}

That's the whole server-name-to-python-name translation map, fr fr β€” capital letters on the left, snake_case on the right, every single pair present and accounted for πŸ’–


class pki.cert.CertRevokeRequest(nonce, reason=None, invalidity_date=None, comments=None)

Bases: object

This one's the little container for every parameter you need when you're revoking a certificate 🫢 like it holds all the stuff the server needs so you can yeet that cert into the void.

Valid values for the reasons for revoking a request are:

'Unspecified', 'Key_Compromise', 'CA_Compromise',
'Affiliation_Changed', 'Superseded',
'Cessation_of_Operation', 'Certificate_Hold',
'Remove_from_CRL', 'Privilege_Withdrawn', 'AA_Compromise'

And here's the same list as the actual attribute, so you can peek at reasons programmatically instead of typing them from memory like a menace:

reasons = ['Unspecified', 'Key_Compromise', 'CA_Compromise',
'Affiliation_Changed', 'Superseded', 'Cessation_of_Operation',
'Certificate_Hold', 'Remove_from_CRL', 'Privilege_Withdrawn',
'AA_Compromise']

note that the underscores are real β€” Key_Compromise not Key Compromise, Cessation_of_Operation not Cessation of Operation β€” type it wrong and the server will simply not be having it πŸ™ˆ


class pki.cert.CertSearchRequest(**cert_search_params)

Bases: object

An object of this class is where you stash your search parameters before shipping them off to the server πŸ” so it's basically the little search-query bag you carry around.

search_params = {'cert_type_secure_email': 'certTypeSecureEmail',
'cert_type_ssl_client': 'certTypeSSLClient',
'cert_type_ssl_server': 'certTypeSSLServer',
'cert_type_sub_email_ca': 'certTypeSubEmailCA',
'cert_type_sub_ssl_ca': 'certTypeSubSSLCA', 'common_name':
'commonName', 'country': 'country', 'email': 'eMail',
'issued_by': 'issuedBy', 'issued_on_from': 'issuedOnFrom',
'issued_on_to': 'issuedOnTo', 'locality': 'locality',
'match_exactly': 'matchExactly', 'org': 'org', 'org_unit':
'orgUnit', 'revocation_reason': 'revocationReason', 'revoked_by':
'revokedBy', 'revoked_on_from': 'revokedOnFrom', 'revoked_on_to':
'revokedOnTo', 'serial_from': 'serialFrom', 'serial_to':
'serialTo', 'state': 'state', 'status': 'status', 'user_id':
'userID', 'valid_not_after_from': 'validNotAfterFrom',
'valid_not_after_to': 'validNotAfterTo', 'valid_not_before_from':
'validNotBeforeFrom', 'valid_not_before_to': 'validNotBeforeTo',
'validity_count': 'validityCount', 'validity_operation':
'validityOperation', 'validity_unit': 'validityUnit'}

and yes β€” 'email': 'eMail' β€” that capital M is not a typo, it's the actual wire name, please don't come for me 😭 also 'user_id': 'userID', 'match_exactly': 'matchExactly' β€” the camelCase to snake_case pipeline is doing the absolute most here and we respect it 🧚

pki.cert.main() β€” that's the module's entry point, right there.


πŸ¦‹ client Module πŸ¦‹

class pki.client.PKIConnection(protocol='http', hostname='localhost', port='8080', subsystem=None, accept='application/json', trust_env=None, verify=True, cert_paths=None)

Bases: object

This is the class that wraps up the whole entire connection between you, the client, and a Dogtag subsystem 🌈 like it's the friend who handles all the talking to the server so you don't have to.

authenticate(username=None, password=None)

Sets the parameters used for authentication if you're going the username/password route. Both username and password must not be None β€” one without the other is giving nothing, bestie πŸ’€ Also super important: this method only sets the parameters. The actual authentication happens when the connection is attempted. So don't panic if nothing seems to happen right away.

Parameters:

  • username β€” username to authenticate the connection
  • password β€” password to authenticate the connection

Returns: None

delete(path, headers=None, use_root_uri=False)

Uses python-requests to fire off a DEL request at the server. bye-bye πŸ‘‹

Parameters:

  • path (str) β€” path URI for the DEL request
  • headers (dict) β€” headers for the DEL request
  • use_root_uri (boolean) β€” use the root URI instead of the subsystem URI as the base

Returns: request.response β€” the response from the server

Raises: Exception from python-requests in case the DEL was not successful, or returns an error code.

get(path, headers=None, params=None, payload=None, use_root_uri=False, timeout=None)

Uses python-requests to fire off a GET request at the server πŸŽ€

Parameters:

  • path (str) β€” path URI for the GET request
  • headers (dict) β€” headers for the GET request
  • params (dict or bytes) β€” query parameters for the GET request
  • payload (dict, bytes, file-like object) β€” data to be sent in the body of the request
  • use_root_uri (boolean) β€” use the root URI instead of the subsystem URI as the base

Returns: request.response β€” the response from the server

Raises: Exception from python-requests in case the GET was not successful, or returns an error code.

heads up: timeout is right there in the signature, bestie β€” it's in the original signature so it's in mine, don't come for me about it ✨

post(path, payload, headers=None, params=None, use_root_uri=False)

Uses python-requests to fire off a POST request at the server πŸ”₯ note that payload is the second positional here β€” not headers, not params β€” payload. Do not get it twisted.

Parameters:

  • path (str) β€” path URI for the POST request
  • payload (dict, bytes, file-like object) β€” data to be sent in the body of the request
  • headers (dict) β€” headers for the POST request
  • params (dict or bytes) β€” query parameters for the POST request
  • use_root_uri (boolean) β€” use the root URI instead of the subsystem URI as the base

Returns: request.response β€” the response from the server

Raises: Exception from python-requests in case the POST was not successful, or returns an error code.

put(path, payload, headers=None, use_root_uri=False)

Uses python-requests to fire off a PUT request at the server πŸ’œ

Parameters:

  • path (str) β€” path URI for the PUT request
  • payload (dict, bytes, file-like object) β€” data to be sent in the body of the request
  • headers (dict) β€” headers for the PUT request
  • use_root_uri (boolean) β€” use the root URI instead of the subsystem URI as the base

Returns: request.response β€” the response from the server

Raises: Exception from python-requests in case the PUT was not successful, or returns an error code.

set_authentication_cert(pem_cert_path, pem_key_path=None)

Points this thing at the PEM file holding the certificate and the private key for the client certificate you're using to authenticate to the server β€” this is for when client certificate auth is what's required πŸ” The private key can optionally live at a completely different path, which is why pem_key_path exists. You're welcome.

Parameters:

  • pem_cert_path (str) β€” path to the PEM file
  • pem_key_path (str) β€” path to the PEM-formatted private key file

Returns: None

Raises: Exception if the path is empty or None β€” so like, actually give it a path, bestie 🫠


class pki.client.SSLContextAdapter(pool_connections=10, pool_maxsize=10, max_retries=0, pool_block=False, verify=True, cert_paths=None)

Bases: requests.adapters.HTTPAdapter

A custom SSLContext Adapter for requests ✨ the upgrade you didn't know you needed.

init_poolmanager(connections, maxsize, block=False, **pool_kwargs)

Initializes a urllib3 PoolManager 🌸

Fair warning, bestie: this method should not be called from user code β€” it's only exposed for use when you're subclassing HTTPAdapter. So unless you're out here doing the subclass thing, hands off 😌

Parameters:

  • connections β€” the number of urllib3 connection pools to cache.
  • maxsize β€” the maximum number of connections to save in the pool.
  • block β€” block when no free connections are available.
  • pool_kwargs β€” extra keyword arguments used to initialize the Pool Manager.

pki.client.catch_insecure_warning(func)

Temporarily silences InsecureRequestWarning πŸ™ˆ

The deal here: PKIConnection is not able to verify HTTPS connections yet, so this decorator catches that warning for you. It's a whole thing.

See https://fedorahosted.org/pki/ticket/1253

pki.client.main()

Test code for the PKIConnection class. :return: None


πŸ’œ crypto Module πŸ’œ

Module containing crypto classes.

class pki.crypto.CryptoProvider

Bases: object

An abstract class containing methods to do cryptographic operations πŸ”₯ like it's the blueprint, not the building.

abstract asymmetric_wrap(data, wrapping_cert, mechanism=None)

Encrypts a symmetric key using the public key of a transport cert πŸŽ€ and the mechanism here is the type of symmetric key β€” which defaults to a 56 bit DES3 key. Fifty-six. Bit. DES3. That's the default, no cap ⭐

and the abstract methods keep coming, bestie:

  • abstract generate_nonce_iv(mechanism) β€” Create a random initialization vector
  • abstract generate_session_key() β€” Generate a session key to be used for wrapping data to the DRM This must return a 3DES 168 bit key
  • abstract generate_symmetric_key(mechanism=None, size=0) β€” Generate and return a symmetric key
  • abstract get_cert(cert_nick) β€” Get the certificate for the specified cert_nick.
  • abstract get_supported_algorithm_keyset() β€” returns highest supported algorithm keyset
  • abstract initialize() β€” Initialization code
  • abstract key_unwrap(mechanism, data, wrapping_key, nonce_iv) β€” Unwrap data that has been key wrapped using AES KeyWrap
  • abstract set_algorithm_keyset(level) β€” sets required keyset
  • abstract symmetric_unwrap(data, wrapping_key, mechanism=None, nonce_iv=None) β€” decrypt data originally encrypted with symmetric key (wrapping key). We expect the data and nonce_iv values to be base64 encoded. The mechanism is the type of key used to do the wrapping. It defaults to a 56 bit DES3 key.
  • abstract symmetric_wrap(data, wrapping_key, mechanism=None, nonce_iv=None) β€” encrypt data using a symmetric key (wrapping key)

πŸ’– pki.crypto.CryptographyCryptoProvider πŸ’–

class pki.crypto.CryptographyCryptoProvider(transport_cert_nick, transport_cert, backend=<OpenSSLBackend(version: OpenSSL 3.0.1 14 Dec 2021, FIPS: False)>)

Bases: pki.crypto.CryptoProvider

Class that defines python-cryptography implementation of CryptoProvider. Requires a PEM file containing the agent cert to be initialized.

Note that all inputs and outputs are unencoded.

  • asymmetric_wrap(data, wrapping_cert, mechanism=None) β€” Wrap (encrypt) data using the supplied asymmetric key
    • data β€” Data to be wrapped
    • wrapping_cert β€” Public key to wrap data
    • mechanism β€” algorithm of symmetric key to be wrapped
  • generate_nonce_iv(mechanism='AES') β€” Create a random initialization vector
  • generate_session_key() β€” Returns a session key to be used when wrapping secrets for the DRM.
  • generate_symmetric_key(mechanism=None, size=0) β€” Returns a symmetric key.
  • get_cert(cert_nick) β€”
    • cert_nick β€” Nickname for the certificate to be returned.
  • get_supported_algorithm_keyset() β€” returns highest supported algorithm keyset
  • initialize() β€” Any operations here that need to be performed before crypto operations.
  • key_unwrap(mechanism, data, wrapping_key, nonce_iv) β€” Unwrap the encrypted data which has been wrapped using a KeyWrap mechanism.
    • mechanism β€” key wrapping mechanism
    • data β€” data to unwrap
    • wrapping_key β€” AES key used to wrap data
    • nonce_iv β€” Nonce data
    • return β€” unwrapped data
  • set_algorithm_keyset(level) β€” sets required keyset
  • symmetric_unwrap(data, wrapping_key, mechanism=None, nonce_iv=None) β€” Unwrap (decrypt) data using the supplied symmetric key
    • data β€” Data to be unwrapped
    • wrapping_key β€” Symmetric key to unwrap data
    • mechanism β€” Mechanism to use when unwrapping
    • nonce_iv β€” iv data
  • symmetric_wrap(data, wrapping_key, mechanism=None, nonce_iv=None) β€” Wrap (encrypt) data using the supplied symmetric key
    • data β€” Data to be wrapped
    • wrapping_key β€” Symmetric key to wrap data
    • mechanism β€” Mechanism to use for wrapping key
    • nonce_iv β€” Nonce for initialization vector

πŸŽ€ encoder Module πŸŽ€

  • pki.encoder.CustomTypeDecoder(dct) β€” just a little decoder helper, no extra description given

🌸 pki.encoder.CustomTypeEncoder 🌸

class pki.encoder.CustomTypeEncoder(*, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False, indent=None, separators=None, default=None)

Bases: json.encoder.JSONEncoder

A custom JSONEncoder class that knows how to encode core custom objects.

Custom objects are encoded as JSON object literals (ie, dicts) with one key, 'TypeName' where 'TypeName' is the actual name of the type to which the object belongs. That single key maps to another object literal which is just the __dict__ of the object encoded.

Reason for ignoring the error: E0202 - An attribute affected in json.encoder line 157 hide this method reported by pylint: The error is in json.encoder.JSONEncoder class. There is a default method (which is overridden here) and also a class attribute self.default initialized in the init method of the class. The intention of such usage being that a custom default method object can be passed to init when creating an instance of JSONEncoder, which is then assigned to class's default method. (which is valid) But pylint raises an issue due to the usage of same name for a method and an attribute in which case the attribute definition hides the method. The reason and example for the issue: (top rated comment) http://stackoverflow.com/questions/12949064/python-what-happens-when-instance-variable-name-is-same-as-method-name
  • static attr_name_conversion(attr_dict, object_class) β€” no description, it's just vibing here
  • default(o) β€” Implement this method in a subclass such that it returns a serializable object for o, or calls the base implementation (to raise a TypeError).

For example, to support arbitrary iterators, you could implement default like this:

def default(self, o):
    try:
        iterable = iter(o)
    except TypeError:
        pass
    else:
        return list(iterable)
    # Let the base class default method raise the TypeError
    return JSONEncoder.default(self, o)
  • pki.encoder.decode_cert(data) β€” base64 decode X.509 certificate
    • Parameters: data (str, bytes) -- data as bytes or ASCII text
    • Return type: bytes
  • pki.encoder.encode_cert(data) β€” base64 encode X.509 certificate

Python 3's base64.b64encode() doesn't support ASCII text.

  • Parameters: data (str, bytes) -- data as bytes or ASCII text
  • Return type: bytes

πŸ”‘ key Module πŸ”‘

Module containing the Python client classes for the KeyClient and KeyRequestClient REST API on a DRM

πŸ’œ pki.key.AsymKeyGenerationRequest πŸ’œ

class pki.key.AsymKeyGenerationRequest(client_key_id=None, key_size=None, key_algorithm=None, key_usages=None, trans_wrapped_session_key=None, realm=None)

Bases: pki.ResourceMessage

Class representing the data sent to the DRM when generating and archiving asymmetric keys in the DRM.

  • DECRYPT_USAGE = 'decrypt'
  • DERIVE_USAGE = 'derive'
  • ENCRYPT_USAGE = 'encrypt'
  • SIGN_RECOVER_USAGE = 'sign_recover'
  • SIGN_USAGE = 'sign'
  • UNWRAP_USAGE = 'unwrap'
  • VERIFY_RECOVER_USAGE = 'verify_recover'
  • VERIFY_USAGE = 'verify'
  • WRAP_USAGE = 'wrap'

πŸ¦‹ pki.key.Key πŸ¦‹

class pki.key.Key(key_data)

Bases: object

An instance of this class stores the decoded encrypted secret present in the KeyData object passed in the constructor. All the key retrieval requests return this object.

🌈 pki.key.KeyArchivalRequest 🌈

class pki.key.KeyArchivalRequest(client_key_id=None, data_type=None, wrapped_private_data=None, trans_wrapped_session_key=None, pki_archive_options=None, algorithm_oid=None, symkey_params=None, key_algorithm=None, key_size=None, realm=None)

Bases: pki.ResourceMessage

Class representing the object sent to the DRM when archiving a secret.

πŸ‘‘ pki.key.KeyClient πŸ‘‘

class pki.key.KeyClient(connection, crypto, transport_cert_nick=None, info_client=None)

Bases: object

Class that encapsulates and mirrors the functions in the KeyResource and KeyRequestResource Java classes in the DRM REST API.

  • AES_ALGORITHM = 'AES'
  • ASYMMETRIC_KEY_TYPE = 'asymmetricKey'
  • DES3_ALGORITHM = 'DES3'
  • DESEDE_ALGORITHM = 'DESede'
  • DES_ALGORITHM = 'DES'
  • DSA_ALGORITHM = 'DSA'
  • KEY_STATUS_ACTIVE = 'active'
  • KEY_STATUS_INACTIVE = 'inactive'
  • PASS_PHRASE_TYPE = 'passPhrase'
  • RC2_ALGORITHM = 'RC2'
  • RC4_ALGORITHM = 'RC4'
  • RSA_ALGORITHM = 'RSA'
  • SYMMETRIC_KEY_TYPE = 'symmetricKey'
  • approve_request(request_id) β€” Approve a secret recovery request
  • archive_encrypted_data(client_key_id, data_type, encrypted_data, wrapped_session_key, algorithm_oid=None, nonce_iv=None, key_algorithm=None, key_size=None, realm=None) β€” Archive a secret (symmetric key or passphrase) on the DRM.

Refer to archive_key() comments for a description of client_key_id, data_type, key_algorithm and key_size.

The following parameters are also required:

  • encrypted_data - which is the data encrypted by a session key (168 bit 3DES symmetric key)
  • wrapped_session_key - the above session key wrapped by the DRM transport certificate public key.
  • the algorithm_oid string for the symmetric key wrap
  • the nonce_iv for the symmetric key wrap

This function is useful if the caller wants to do their own wrapping of the secret, or if the secret was generated on a separate client machine and the wrapping was done there.

The function returns a KeyRequestResponse object containing a KeyRequestInfo object with details about the archival request and key archived.

archive_key(client_key_id, data_type, private_data, key_algorithm=None, key_size=None, realm=None) πŸ’–

Okay so this one archives a secret on the DRM β€” and by secret we mean a symmetric key or a passphrase, no biggie. πŸŽ€

It needs a user-supplied client ID, and here's the thing: there can be only one active key with a given client ID. So if a record for a duplicate active key already exists? Boom, BadRequestException gets thrown at you. Not cute, but that's the rule. 😭

data_type can be one of these three besties:

  • KeyClient.SYMMETRIC_KEY_TYPE
  • KeyClient.ASYMMETRIC_KEY_TYPE
  • KeyClient.PASS_PHRASE_TYPE

key_algorithm and key_size only apply to symmetric keys β€” like, only. If you're archiving a symmetric key, these params are required, periodt.

private_data is the raw secret being archived. It gets wrapped and yeeted off to the DRM. ✨

Returns a KeyRequestResponse object holding a KeyRequestInfo object with all the deets about the archival request and the key that got archived. 🫢


archive_pki_options(client_key_id, data_type, pki_archive_options, key_algorithm=None, key_size=None, realm=None) 🌸

Same vibe as above β€” archives a secret (symmetric key or passphrase) on the DRM. πŸ’…

For client_key_id, data_type, key_algorithm and key_size, just go read the archive_key() comments, bestie, they're right there.

pki_archive_options is the data to be archived, wrapped in a PKIArchiveOptions structure.

Returns a KeyRequestResponse object containing a KeyRequestInfo object with the details about the archival request and key archived. πŸ¦‹


cancel_request(request_id) 🚫

Cancels a secret recovery request. That's it. That's the whole thing. Short and iconic.


generate_asymmetric_key(client_key_id, algorithm=None, key_size=None, usages=None, trans_wrapped_session_key=None, realm=None) πŸ”‘

Generates and archives asymmetric keys in the DRM. Supports the algorithms RSA and DSA. πŸ‘‘

Valid key sizes:

  • RSA = 256 + (16 * n), where n: 0-496
  • DSA = 512, 768, 1024

Also, p,q,g params are not supported β€” don't come for me, that's just how it is. πŸ™ˆ

Returns a KeyRequestResponse which contains a KeyRequestInfo object describing the URL for the request and the generated keys. πŸ’–


generate_symmetric_key(client_key_id, algorithm=None, size=None, usages=None, trans_wrapped_session_key=None, realm=None) ✨

Generates and archives a symmetric key on the DRM. Simple, clean, slays.

Returns a KeyRequestResponse which contains a KeyRequestInfo object describing the URL for the request and the generated key. πŸŽ€


get_active_key_info(client_key_id) πŸ”

Gets the info in the KeyRecord for the active secret in the DRM. That's the tea. β˜•


get_client_keyset() 🧚

Yep, that's it β€” no description in the original, so I'm not gonna make one up. It's giving mysterious main character energy. πŸ’œ


get_key_info(key_id) πŸ”Ž

Gets the info in the KeyRecord for a specific secret in the DRM. Specificity is key, iykyk. πŸ’…


get_request_info(request_id) πŸ“‹

Returns a KeyRequestInfo object for a specific request. Clean, useful, we love her.


get_server_keyset() 🌈

Again β€” no description given. She's enigmatic, she's lowkey, she's not telling us anything. ⭐


list_keys(client_key_id=None, status=None, max_results=None, max_time=None, start=None, size=None, realm=None) πŸ“

Lists/searches archived secrets in the DRM. πŸ¦‹

For the valid values of status, check out KRAClient.list_keys. Returns a KeyInfoCollection object. πŸ’–


list_requests(request_state=None, request_type=None, client_key_id=None, start=None, page_size=None, max_results=None, max_time=None, realm=None) πŸ—‚οΈ

Lists/searches key requests in the DRM.

For the valid values of request_state and request_type, look at KRAClient.list_requests. Returns a KeyRequestInfoCollection object. ✨


modify_key_status(key_id, status) πŸ”„

Modifies the status of a key. Short, sweet, does what it says on the tin. πŸŽ€


process_returned_key(key, session_key) πŸ”

Decrypts the returned key and places it in key.data. πŸ’«

So the data will either be encrypted using an encryption algorithm β€” in which case the key data will contain an encryption algorithm OID β€” or it will be key wrapped, in which case the key data contains a key wrap mechanism name.

Only one of those should be present at a time, ngl. And if we're talking to an older server and neither is present? We assume encryption. That's the fallback, don't @ me. 😭


recover_key(key_id, request_id=None, session_wrapped_passphrase=None, trans_wrapped_session_key=None, b64certificate=None, nonce_data=None) πŸ—οΈ

Creates a request to recover a secret. Let's get that key back, bestie. πŸ’…

To retrieve a symmetric key or passphrase, the only parameter required is the keyId. It's possible (but not required!) to pass in the session keys/passphrase and nonceData for the retrieval at this time β€” those params are documented in the docstring for retrieve_key below. πŸ‘€

To retrieve an asymmetric key, you need the keyId and the base-64 encoded certificate. Both. No skipping. πŸŽ€


reject_request(request_id) ❌

Rejects a secret recovery request. Not today, request. πŸ™…β€β™€οΈ


retrieve_key(key_id=None, trans_wrapped_session_key=None, request_id=None) πŸ”“

Retrieves a secret (passphrase or symmetric key) from the DRM. The moment we've all been waiting for. ✨

This method grabs a key from the KRA given the key_id or request_id β€” and yep, one of those must be specified. The data comes back as a KeyData object (which gets recast to a Key object).

If request_id is specified, then the value of key_id is straight-up ignored. Exceptions will be thrown if the caller isn't the originator of the request, or if the request isn't approved. So like, behave. 😭

If key_id is specified instead, here's the behavior:

  • If the key can be retrieved synchronously β€” i.e. only one agent's approval is required β€” then the KeyData will include the secret. πŸŽ‰
  • If the key cannot be retrieved synchronously β€” i.e. if more than one approval is needed β€” then the KeyData object will include the request ID for a recovery request that was created on the server. When that request gets approved, callers can retrieve the key using retrieve_key() and setting the request_id. 🫢

To ensure data security in transit, the data is returned encrypted by a session key (a 168 bit 3DES symmetric key) β€” which is first wrapped (encrypted) by the public key of the DRM transport certificate before being sent to the DRM. The parameter trans_wrapped_session_key refers to this wrapped session key. πŸ”

If trans_wrapped_session_key is not provided by the caller, the method calls CryptoProvider methods to generate and wrap the session key. The function returns the KeyData object with a private_data attribute which stores the unwrapped key information. πŸ’–

If trans_wrapped_session_key is provided by the caller, the method will just pass the data straight to the KRA, and returns the secret wrapped in the session key. The secret still needs to be unwrapped by the caller. The function returns the KeyData object, where the KeyData structure includes the wrapped secret and some nonce data to be used as a salt when unwrapping. πŸ§‚


retrieve_key_by_passphrase(key_id=None, request_id=None, passphrase=None, trans_wrapped_session_key=None, session_wrapped_passphrase=None, nonce_data=None) πŸ”‘

Retrieves a secret (passphrase or symmetric key) from the DRM using a passphrase. Fancy, right? πŸ’…

This function generates a key recovery request, approves it, and retrieves the secret referred to by key_id. It assumes only one approval is required to authorize the recovery. ✨

The secret is secured in transit by wrapping the secret with a passphrase using PBE encryption. πŸŽ€

There are two ways to use this function:

  1. A passphrase is provided by the caller.

In this case, CryptoProvider methods get called to create the data to securely send the passphrase to the DRM. Basically, three pieces of data get sent:

  • the passphrase wrapped by a 168 bit 3DES symmetric key (the session key). This is referred to as the parameter session_wrapped_passphrase.
  • the session key wrapped with the public key in the DRM transport certificate. This is referred to as the trans_wrapped_session_key.
  • ivps nonce data, referred to as nonce_data.

The function returns the tuple (KeyData, unwrapped_secret). πŸ¦‹

  1. The caller provides the trans_wrapped_session_key, session_wrapped_passphrase and nonce_data.

In this case, the data simply gets passed to the DRM. The function returns the secret encrypted by the passphrase using PBE Encryption. The secret will still need to be decrypted by the caller.

The function returns the tuple (KeyData, None). 🌸


retrieve_key_by_pkcs12(key_id, certificate, passphrase) 🎁

Retrieves an asymmetric private key and returns it as PKCS12 data.

This function generates a key recovery request, approves it, and retrieves the secret referred to by key_id in a PKCS12 file. It assumes only one approval is required to authorize the recovery. πŸ’«

Required parameters:

  • key_id β€” the ID of the key
  • certificate β€” the certificate associated with the private key
  • passphrase β€” a passphrase for the pkcs12 file

Returns a KeyData object. πŸ‘‘


retrieve_key_data(data) πŸ“¦

Retrieves a secret from the DRM. That's the whole description β€” she's concise, she's efficient, she ate. πŸ’…βœ¨

@param: data β€” a KeyRecoveryRequest that's holding onto the keyId of the secret you're grabbing, the request_id of the approved recovery request, and a wrapping mechanism. Bestie, there are more details at KRAClient.retrieve_key, so go look πŸ‘€.

Returns a KeyData object that's carrying the wrapped secret. πŸŽ€

  • set_crypto_algorithms() β€” sets the crypto algorithms, that's it, that's the whole vibe.
  • set_transport_cert(transport_cert_nick) β€” set the transport certificate for crypto operations. πŸ’…
  • submit_request(request) β€” submit an archival, recovery or key generation request to the DRM.

@param: request is either a KeyArchivalRequest, KeyRecoverRequest, SymKeyGenerationRequest or AsymKeyGenerationRequest.

Returns a KeyRequestResponse object. ✨

class pki.key.KeyData

Bases: object

OK so this is the object that holds the encoded wrapped secret once that secret has actually been retrieved. The DRM uses it to send key info along in the key retrieval requests. It's giving very "delivery girl" energy πŸ“¦.

  • classmethod from_json(attr_list) β€” return a KeyData object from a JSON dict.
  • json_attribute_names β€” literally this exact mapping, do not touch:
{'encryptAlgorithmOID': 'encrypt_algorithm_oid', 'nonceData': 'nonce_data', 'publicKey': 'public_key', 'requestID': 'request_id', 'wrapAlgorithm': 'wrap_algorithm', 'wrappedPrivateData': 'wrapped_private_data'}

class pki.key.KeyInfo

Bases: object

This one contains the info stored in the database record for an archived secret. Read that again β€” it does not contain the secret itself. No secret here, just the metadata, iykyk πŸ™ˆ.

  • classmethod from_json(attr_list) β€” return KeyInfo from a JSON dict.
  • get_key_id() β€” return the key ID as parsed from the key URL. πŸ”‘
  • json_attribute_names β€” exact mapping, keep it character for character:
{'clientKeyID': 'client_key_id', 'keyURL': 'key_url', 'ownerName': 'owner_name', 'publicKey': 'public_key'}

class pki.key.KeyInfoCollection

Bases: object

This class represents the data you get back when you go searching the DRM for archived secrets. Essentially it's just a list of KeyInfo objects β€” a little squad of them 🫢.

  • classmethod from_json(json_value) β€” return a KeyInfoCollection object from its JSON representation.

class pki.key.KeyRecoveryRequest

KeyRecoveryRequest(key_id=None, request_id=None, trans_wrapped_session_key=None, session_wrapped_passphrase=None, nonce_data=None, certificate=None, passphrase=None, payload_wrapping_name=None, payload_encryption_oid=None)

Bases: pki.ResourceMessage

Class representing the data that gets sent to the DRM when you're either (a) creating a request for the recovery of a secret, or (b) retrieving the secret once that request is approved. Two moods, one class πŸ’«.

class pki.key.KeyRequestInfo

Bases: object

This class represents data about key requests in the DRM β€” archival, recovery, key generation, the whole lineup.

  • classmethod from_json(attr_list) β€” return a KeyRequestInfo object from a JSON dict.
  • get_key_id() β€” return the ID of the secret that this request is referring to. 🎯
  • get_request_id() β€” return the request ID by parsing the request URL.
  • json_attribute_names β€” exact mapping, no cap:
{'keyURL': 'key_url', 'requestStatus': 'request_status', 'requestType': 'request_type', 'requestURL': 'request_url'}

class pki.key.KeyRequestInfoCollection

Bases: object

This is the data returned when you search the key requests in the DRM. Essentially, it's a list of KeyRequestInfo objects. A little collection, a little roster, we love a list 🌸.

  • classmethod from_json(json_value) β€” return a KeyRequestInfoCollection object from its JSON representation.

class pki.key.KeyRequestResponse

Bases: object

This is what comes back when an archival, recovery or key generation request gets created. It's packing a KeyRequestInfo object with the deets about the created request, plus a KeyData structure holding the wrapped secret (but only if that operation is supported β€” vibe check on that one πŸ’€).

  • classmethod from_json(json_value) β€” return a KeyRequestResponse object from its JSON representation.
  • get_key_id() β€” return the id for the key that was archived, recovered or generated.
  • get_request_id() β€” return the id for the created request.

class pki.key.RequestId(req_id)

Bases: object

Class representing a Request ID. Short, sweet, iconic. πŸŽ€

class pki.key.SymKeyGenerationRequest

SymKeyGenerationRequest(client_key_id=None, key_size=None, key_algorithm=None, key_usages=None, trans_wrapped_session_key=None, realm=None)

Bases: pki.ResourceMessage

Class representing the data sent to the DRM when you're generating and archiving a symmetric key in the DRM. Double duty, main character energy πŸ‘‘.

The usage constants, exact values, no editing:

  • DECRYPT_USAGE = 'decrypt'
  • ENCRYPT_USAGE = 'encrypt'
  • SIGN_USAGE = 'sign'
  • UNWRAP_USAGE = 'unwrap'
  • VERIFY_USAGE = 'verify'
  • WRAP_USAGE = 'wrap'

pki.key.main()

Some unit tests β€” basically it just prints out different types of requests. Not deep, but it ate. πŸ’…


🌊 kra Module

Module containing the KRAClient class. Python clients should use this class to interact with the DRM to expose the functionality of the KeyClient and KeyRequestResource REST APIs. It's the little bridge between you and the DRM 🫢.

class pki.kra.KRAClient(connection, crypto, transport_cert_nick=None)

Bases: object

Client class that models interactions with a KRA using the Key and KeyRequest REST APIs. Yes bestie, this is the one that does the talking. ✨


πŸ¦‹ profile Module

class pki.profile.Descriptor(syntax=None, constraint=None, description=None, default_value=None)

Bases: object

This class represents the description of a ProfileAttribute. It stores the info like the syntax, constraint and default value of a profile attribute. Basically a little info card for your attribute πŸŽ€.

  • classmethod from_json(attr_list) β€” from JSON, as the name so obviously says.
  • json_attribute_names β€” exact mapping, copy it right:
{'Constraint': 'constraint', 'DefaultValue': 'default_value', 'Description': 'description', 'Syntax': 'syntax'}

class pki.profile.PolicyConstraint(name=None, description=None, class_id=None, policy_constraint_values=None)

Bases: object

An object of this class contains the policy constraints applied to a ProfileInput used by a certificate enrollment request. Very much giving "the rules" energy πŸ“‹.

  • add_constraint_value(policy_constraint_value) β€” add a PolicyConstraintValue to the policy_constraint_values list.
  • classmethod from_json(attr_list) β€” from JSON.
  • get_constraint_value(policy_constraint_value_name) β€” returns a PolicyConstraintValue object with the given name. Returns None if there's no match. 🫠
  • json_attribute_names β€” exact:
{'classId': 'class_id', 'constraint': 'policy_constraint_values', 'id': 'name'}
  • remove_constraint_value(policy_constraint_value_name) β€” removes a PolicyConstraintValue with the given name form the policy_constraint_values list. (Yes, "form" β€” that's the original's typo, I'm not fixing it, I'm just a girl 🫢.)

class pki.profile.PolicyConstraintValue(name=None, value=None, descriptor=None)

Bases: object

Represents a PolicyConstraintValue. That's the whole job description πŸ’….

  • classmethod from_json(attr_list) β€” from JSON.
  • property name β€” it's a property. That's... the name. 🌸

class pki.profile.PolicyDefault(name=None, class_id=None, description=None, policy_attributes=None, policy_params=None)

Bases: object

An object of this class contains info about the default usage of a specific ProfileInput. Like, the fallback vibe ✨.

  • add_attribute(policy_attribute) β€” add a policy attribute to the attribute list. @param: policy_attribute β€” a ProfileAttribute object.
  • add_parameter(policy_parameter) β€” add a profile parameter to the parameters list. @param: policy_parameter β€” a ProfileParameter object.
  • classmethod from_json(attr_list) β€” from JSON.
  • get_attribute(policy_attribute_name) β€” fetch the policy attribute with the given name from the attributes list.
  • get_parameter(profile_parameter_name) β€” fetch a profile parameter with the given name from the parameters list.
  • json_attribute_names β€” exact, bestie:
{'classId': 'class_id', 'id': 'name', 'params': 'policy_params', 'policyAttribute': 'policy_attributes'}
  • remove_attribute(policy_attribute_name) β€” remove a policy attribute with the given name from the attributes list.
  • remove_parameter(profile_parameter_name) β€” remove a profile parameter with the given name from the parameters list.

class pki.profile.PolicySet(name=None, policy_list=None)

Bases: object

An object of this class contains a name-value pair situation β€” the policy name, and the ProfilePolicy object. 🎯

  • add_policy(profile_policy) β€” add a ProfilePolicy object to the policy_list.
  • classmethod from_json(attr_list) β€” from JSON.
  • get_policy(policy_id) β€” returns a ProfilePolicy object with the given profile id.
  • json_attribute_names β€” exact:
{'id': 'name', 'value': 'policy_list'}
  • remove_policy(policy_id) β€” removes a ProfilePolicy with the given ID from the PolicySet.

class pki.profile.PolicySetList(policy_sets=None)

Bases: object

An object of this class stores a list of ProfileSet objects. (Yep, ProfileSet, that's what it says β€” hold that thought 🫢.)

  • add_policy_set(policy_set) β€” add a PolicySet object to the policy_sets list.
  • classmethod from_json(attr_list) β€” from JSON.
  • get_policy_set(policy_set_name) β€” fetch the PolicySet object for the given name. Returns None if it's not found. πŸ™ˆ
  • property policy_sets β€” the property itself, living its life.
  • remove_policy_set(policy_set_name) β€” remove a PolicySet object with the given name from the policy_sets list.

class pki.profile.Profile

Profile(profile_id=None, class_id=None, name=None, description=None, enabled=None, visible=None, enabled_by=None, authenticator_id=None, authorization_acl=None, renewal=None, xml_output=None, inputs=None, outputs=None, policy_set_list=None, link=None)

Bases: object

This class represents an enrollment profile. Main character of the profile module, honestly πŸ‘‘.

  • add_input(profile_input) β€” add a ProfileInput object to the inputs list of the Profile.
  • add_output(profile_output) β€” add a ProfileOutput object to the outputs list of the Profile.
  • add_policy_set(policy_set) β€” add a PolicySet object to the policy_sets list of the Profile.
  • classmethod from_json(attr_list) β€”

okay bestie, back to the Profile class methods β€” these are the ones that actually do stuff πŸ’…

  • get_input(profile_input_id) β€” Fetches a ProfileInput with the given ProfileInput id. Returns None if there’s no matching input. (so if it ghosts you, that’s why 😭)
  • get_output(profile_output_id) β€” Fetches a ProfileOutput with the given ProfileOutput id. Returns None if there’s no matching output.
  • get_policy_set(policy_set_name) β€” Fetches a ProfileInput with the given ProfileInput id. Returns None if there’s no matching input. (yes, the original says ProfileInput here β€” lowkey a copy-paste moment, but we’re keeping it fr fr 🫢)
  • static get_profile_data_from_file(path_to_file) β€” Reads the file for the serialized Profile object. Currently supports only data format in json. No cap, json only ✨

Then there’s this mapping of json attribute names β€” keep it close, it’s important:

json_attribute_names = {'Input': 'inputs', 'Output': 'outputs',
'PolicySets': 'policy_set_list', 'authenticatorId':
'authenticator_id', 'authzAcl': 'authorization_acl', 'classId':
'class_id', 'enabledBy': 'enabled_by', 'id': 'profile_id',
'xmlOutput': 'xml_output'}
  • remove_input(profile_input_id) β€” Remove a ProfileInput from the inputs list of the Profile.
  • remove_output(profile_output_id) β€” Remove a ProfileOutput from the outputs list of the Profile.
  • remove_policy_set(policy_set_name) β€” Remove a PolicySet from the policy_sets list of the Profile.

πŸŽ€ class pki.profile.ProfileAttribute(name=None, value=None, descriptor=None)

Bases: object

Represents a profile attribute of a ProfileInput. It’s giving β€œone little detail that matters” energy πŸ’–

  • classmethod from_json(attr_list)

The mapping:

json_attribute_names = {'Descriptor': 'descriptor', 'Value': 'value'}

πŸ‘‘ class pki.profile.ProfileClient(connection)

Bases: object

This class consists of methods for accessing the ProfileResource. Basically your main character for talking to profiles πŸ”₯

  • create_profile(profile_data) β€” Create a new profile for the given Profile object.
  • create_profile_from_file(path_to_file) β€” Reads the file for the serialized Profile object. Performs the profile create operation. Currently supports only data format in json.
  • delete_profile(profile_id) β€” Delete a profile with the given Profile Id.
  • disable_profile(profile_id) β€” Disables a profile.
  • enable_profile(profile_id) β€” Enables a profile.
  • get_profile(profile_id) β€” Fetches information for the profile for the given profile id. Returns a ProfileData object.
  • list_profiles(start=None, size=None) β€” Fetches the list of profiles. The start and size arguments provide pagination support. Returns a ProfileDataInfoCollection object.
  • modify_profile(profile_data) β€” Modify an existing profile with the given Profile object.
  • modify_profile_from_file(path_to_file) β€” Reads the file for the serialized Profile object. Performs the profile modify operation. Currently supports only data format in json.

🌸 class pki.profile.ProfileDataInfo

Bases: object

Stores information about a profile. Like a little info card, but make it secure πŸŽ€

  • classmethod from_json(attr_list)

The mapping:

json_attribute_names = {'profileDescription':
'profile_description', 'profileId': 'profile_id', 'profileName':
'profile_name', 'profileURL': 'profile_url'}

πŸ¦‹ class pki.profile.ProfileDataInfoCollection

Bases: object

Represents a collection of ProfileDataInfo objects. Also encapsulates the links for the list of the objects stored. It’s giving β€œorganized queen” energy πŸ‘‘

  • classmethod from_json(attr_list)

πŸ’œ class pki.profile.ProfileInput(profile_input_id=None, class_id=None, name=None, text=None, attributes=None, config_attributes=None)

Bases: object

This class encapsulates all the attributes of a profile to generate a specific property of a certificate. Ex. Subject name, Requestor Information etc. So it’s the blueprint for cert details ✨

  • add_attribute(profile_attribute) β€” Add a ProfileAttribute object to the attributes list.
  • add_config_attribute(profile_attribute) β€” Add a ProfileAttribute object to the config_attributes list.
  • classmethod from_json(attr_list)
  • get_attribute(profile_attribute_name) β€” Returns a ProfileAttribute object for the given name. None if no match.
  • get_config_attribute(config_attribute_name) β€” Returns a ProfileAttribute object with the given name. None if there is no match in the config_attributes list.

The mapping:

json_attribute_names = {'Attribute': 'attributes', 'ClassID':
'class_id', 'ConfigAttribute': 'config_attributes', 'Name':
'name', 'Text': 'text', 'id': 'profile_input_id'}
  • remove_attribute(profile_attribute_name) β€” Remove a ProfileAttribute object with the given name from the attributes list.
  • remove_config_attribute(config_attribute_name) β€” Remove a ProfileAttribute object with the given name from the config_attributes list.

🧚 class pki.profile.ProfileOutput(profile_output_id=None, name=None, text=None, class_id=None, attributes=None)

Bases: object

This class defines the output of a certificate enrollment request using a profile. It’s the β€œwhat you actually get” vibe πŸ’…

  • add_attribute(profile_attribute) β€” Add a ProfileAttribute object to the attributes list.
  • classmethod from_json(attr_list)
  • get_attribute(profile_attribute_name) β€” Returns a ProfileAttribute object for the given name. None if no match.

The mapping:

json_attribute_names = {'classId': 'class_id', 'id':
'profile_output_id'}
  • remove_attribute(profile_attribute_name) β€” Remove a ProfileAttribute object with the given name from the attributes list.

🌈 class pki.profile.ProfileParameter(name=None, value=None)

Bases: object

  • classmethod from_json(attr_list)

⭐ class pki.profile.ProfilePolicy(policy_id=None, policy_default=None, policy_constraint=None)

Bases: object

This class represents the policy a profile adheres to. An object of this class stores the default values for profile and the constraints present on the values of the attributes of the profile submitted for an enrollment request. It’s giving β€œrules but make them cute” πŸ“œβœ¨

  • classmethod from_json(attr_list)

The mapping:

json_attribute_names = {'constraint': 'policy_constraint', 'def':
'policy_default', 'id': 'policy_id'}

πŸ“ class pki.profile.ProfilePolicySet

Bases: object

Stores a list of ProfilePolicy objects. A little squad of policies, no cap 🫢

  • classmethod from_json(attr_list)

✨ pki.profile.main()


🌐 system Module

πŸ’» class pki.system.AdminSetupRequest

Bases: object

πŸ’» class pki.system.AdminSetupResponse

Bases: object

πŸ’» class pki.system.CertificateSetupRequest

Bases: object

πŸ’» class pki.system.CertificateSetupResponse

Bases: object

🌍 class pki.system.DomainInfo

Bases: object

Class representing the entire security domain. This is essentially a list of SecurityDomainSubsystem components. It’s the whole map, bestie πŸ—ΊοΈ

  • classmethod from_json(json_value) β€” Create a DomainInfo object from JSON.
    • Parameters: json_value (str) β€” JSON representation of a security domain.
    • Returns: DomainInfo
  • property systems

πŸ” class pki.system.InstallToken

Bases: object

  • classmethod from_json(json_value)

πŸ›‘οΈ class pki.system.SecurityDomainClient(connection)

Bases: object

Client used to get the security domain from a security domain CA. The connection details for the security domain CA are specified in a PKIConnection object used to construct this client. So you’re basically sliding into the CA’s DMs πŸ’Œ

  • get_domain_info() β€” Contact the security domain CA specified in the connection object used to construct this client and get the security domain using the REST API.
    • Returns: pki.system.DomainInfo
  • get_install_token(hostname, subsystem)
    • Returns: pki.system.InstallToken
  • get_old_domain_info() β€” Contact the security domain CA specified in the connection object used to construct this client and get the security domain using the old servlet-based interface. This method is useful when contacting old servers which do not provide the REST API. (for the vintage servers, we still love them πŸ’€)
    • Returns: pki.system.DomainInfo
  • get_security_domain_info()

🏠 class pki.system.SecurityDomainHost

Bases: object

Class representing a security domain host. Like a little address book entry but for security 🏑

  • classmethod from_json(json_value) β€” Constructs a SecurityDomainHost object from JSON.
    • Parameters: json_value (str) β€” JSON string representing a security domain host.
    • Returns: SecurityDomainHost

🧩 class pki.system.SecurityDomainSubsystem

Bases: object

Class representing a security domain subsystem. This is essentially a list of SecurityDomainHost objects of a particular subsystem type (ca, kra, tps, tks, ocsp). It’s the group chat of hosts πŸ’¬

  • classmethod from_json(json_value) β€” Constructs a SecurityDomainSubsystem from a JSON representation.
    • Parameters: json_value (str) β€” JSON representation of the Security Domain Subsystem
    • Returns: SecurityDomainSubsystem
  • get_host(hostname, secure_port)

πŸ“œ class pki.system.SystemCertData

Bases: object

Class used to represent the data for a system certificate, which is used in the data passed into and returned from the Java installation servlet during the execution of pkispawn.

This class is the python equivalent of the Java class: com.netscape.certsrv.system.SystemCertData

So it’s a little cross-language twin situation πŸ‘―β€β™€οΈ

βš™οΈ class pki.system.SystemConfigClient(connection, subsystem=None)

Bases: object

Client used to interact with the Java configuration servlet to configure a Dogtag subsystem during the execution of pkispawn.

The connection details for the system being configured are passed in the PKIConnection object used when constructing this object.

  • createCert(request) β€” Create certificate.

okay bestie, we’re picking up right in the middle of a method β€” the parameter tea is:

  • Parameters: request (CertificateSetupRequest) β€” Certificate setup request 🫢
  • Returns: SystemCertData ✨

then the next methods go:

  • createRequest(request) β€” Create certificate request. πŸ’…
    • Parameters: request (CertificateSetupRequest) β€” Certificate setup request
    • Returns: SystemCertData
  • createRequestID(request) β€” Create certificate request ID. πŸŽ€
    • Parameters: request (CertificateSetupRequest) β€” Certificate setup request
    • Returns: SystemCertData
  • initSubsystem(request) β€” Initialize subsystem. 🌸
    • Parameters: request (CertificateSetupRequest) β€” Certificate setup request

still in the system zone, we’ve got this absolute icon:

class pki.system.SystemStatusClient(connection, subsystem=None)

Bases: object

Client used to check the status of a Dogtag subsystem. πŸ‘‘

  • get_status(timeout=None) β€” Checks the status of the subsystem by calling the getStatus() servlet. This is used to determine if the server is up and ready to receive and process requests. πŸ”₯
    • Returns: str - getStatus response

πŸ¦‹ systemcert Module πŸ¦‹

Module containing the Python client classes for the SystemCert REST API ✨

class pki.systemcert.SystemCertClient(connection, subsystem=None)

Bases: object

Class encapsulating and mirroring the functionality in the SystemCertResource Java interface class defining the REST API for system certificate resources. no cap, that’s the whole vibe πŸ’œ

  • get_transport_cert() β€” Return transport certificate. 🎫
    • Returns: pki.cert.CertData -- transport certificate data

πŸ’… upgrade Module πŸ’…

class pki.upgrade.PKIUpgradeScriptlet

Bases: object

  • backup(path)
  • get_backup_dir()
  • upgrade_system()
class pki.upgrade.PKIUpgradeTracker(name, filename, delimiter='=', version_key='PKI_VERSION', index_key='PKI_UPGRADE_INDEX')

Bases: object

  • get_index()
  • get_version()
  • remove()
  • remove_index()
  • remove_version()
  • set(version)
  • set_index(index)
  • set_version(version)
  • show()
class pki.upgrade.PKIUpgrader(upgrade_dir='/usr/share/pki/upgrade')

Bases: object

  • all_versions()
  • backup(scriptlet, path)
  • copydirs(source, dest, force=False)
  • copyfile(source, dest, force=False)
  • get_current_version()
  • get_target_version()
  • get_tracker()
  • init_scriptlet(scriptlet)
  • is_complete()
  • makedirs(path, exist_ok=False)
  • record(scriptlet, path)
  • remove_tracker()
  • reset_tracker()
  • revert()
  • revert_scriptlet(scriptlet)
  • revert_version(version)
  • run_scriptlet(scriptlet)
  • scriptlets(version)
  • set_tracker(version)
  • show_tracker()
  • status()
  • touch(path)
  • update_tracker(scriptlet)
  • upgrade()
  • upgrade_version(version)
  • validate()
  • version_dir(version)
  • versions()

✨ util Module ✨

Module containing utility functions and classes for the Dogtag python code 🐍

class pki.util.Version(obj)

Bases: object

pki.util.chmod(path, mode) β€” Change permissions of a file, link, or folder recursively. πŸ”

pki.util.chown(path, uid, gid) β€” Change ownership of a file, link, or folder recursively. πŸ‘‘

pki.util.copy(source, dest, uid=- 1, gid=- 1, dir_mode=None, file_mode=None, force=False) β€” Copy a file or a folder and its contents. πŸ“

pki.util.copydirs(source, dest, uid=- 1, gid=- 1, mode=None, force=False) β€” Copy a folder and its parents (without the contents) while preserving their attributes. 🫢

pki.util.copyfile(source, dest, slots=None, params=None, uid=None, gid=None, mode=None, force=False) β€” Copy a file or link while preserving its attributes. πŸŽ€

pki.util.copytree(src, dst, symlinks=False, ignore=None)

Recursively copy a directory tree using copy2(). 🌳

PATCH: This code was copied from shutil.py and patched to allow β€œThe destination directory to already exist.”

If exception(s) occur, an Error is raised with a list of reasons. 😭

If the optional symlinks flag is true, symbolic links in the source tree result in symbolic links in the destination tree; if it is false, the contents of the files pointed to by symbolic links are copied.

The optional ignore argument is a callable. If given, it is called with the src parameter, which is the directory being visited by copytree(), and names which is the list of src contents, as returned by os.listdir():

callable(src, names) -> ignored_names

Since copytree() is called recursively, the callable will be called once for each directory that is copied. It returns a list of names relative to the src directory that should not be copied.

Consider this example code rather than the ultimate tool. πŸ’…

pki.util.customize_file(input_file, output_file, params) β€” Customize a file with specified parameters. ✨

pki.util.load_properties(filename, properties)

pki.util.makedirs(path, mode=511, exist_ok=False, uid=- 1, gid=- 1, force=False)

pki.util.read_environment_files(env_file_list=None)

pki.util.read_text(message, options=None, default=None, delimiter=':', case_sensitive=True, password=False, required=False)

Get an input from the user. This is used, for example, in pkispawn and pkidestroy to obtain user input. 🫢

Parameters

  • message (str) β€” prompt to display to the user
  • options (list) β€” list of possible inputs by the user.
  • default (str) β€” default value of parameter being prompted.
  • delimiter (str) β€” delimiter to be used at the end of the prompt.
  • case_sensitive (boolean -- True/False) β€” Allow input to be case sensitive.
  • password (boolean -- True/False) β€” Input is a password. Don't show the value.
  • required (boolean -- True/False) β€” Input must be non-empty.

Returns: str -- value obtained from user input.

pki.util.remove(path, force=False)

pki.util.replace_params(line, params=None) β€” Replace all occurrences of [param] in the line with the value of the parameter. πŸ”

pki.util.rmtree(path, force=False)

pki.util.set_property(properties, name, value)

pki.util.store_properties(filename, properties)

pki.util.symlink(source, dest, uid=- 1, gid=- 1, force=False)

pki.util.unlink(link, force=False)


and the lil index links are still here, iconic:

  • genindex
  • modindex
  • search

🫢 AUTHOR 🫢

Dogtag PKI Project Team


2022, Dogtag PKI Team


and that’s the whole page, bestie β€” footer tea is source/version @APPLICATION_VERSION@ and date Apr 18, 2022, all wrapped up for PKI-PYTHON-CLIENT(1) πŸ’–βœ¨

πŸ“¦ where this page comes from