├── .gitignore └── python ├── .gitignore ├── LICENSE.txt ├── MANIFEST.in ├── README.rst ├── __init__.py ├── digicert_client ├── __init__.py ├── api │ ├── __init__.py │ ├── commands │ │ ├── __init__.py │ │ ├── v1.py │ │ └── v2.py │ └── queries │ │ ├── __init__.py │ │ ├── v1.py │ │ └── v2.py ├── https │ ├── DigiCertRoots.pem │ └── __init__.py └── tests │ ├── TestClient.py │ ├── TestDownloadCertificate.py │ ├── TestDownloadCertificateQuery.py │ ├── TestOrderCertificate.py │ ├── TestOrderCertificateCommand.py │ ├── TestPeerVerifier.py │ ├── TestViewOrder.py │ ├── TestViewOrderQuery.py │ ├── VerifiedConnectionTestClient.py │ ├── __init__.py │ ├── test-tls.py │ └── test.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .apikeys 3 | *.pyc 4 | -------------------------------------------------------------------------------- /python/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | /build/ 3 | /dist/ 4 | /*.egg-info 5 | -------------------------------------------------------------------------------- /python/LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 DigiCert, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /python/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | include LICENSE.txt 3 | include digicert_client/https/DigiCertRoots.pem 4 | recursive-exclude . *.pyc 5 | -------------------------------------------------------------------------------- /python/README.rst: -------------------------------------------------------------------------------- 1 | digicert_client 2 | --------------- 3 | 4 | This package facilitates programmatic access to the DigiCert, Inc. web APIs for DigiCert customers. 5 | Currently supported actions include: 6 | 7 | * Ordering of certificates 8 | * Checking on order status 9 | * Retrieving approved certificates 10 | 11 | Customers must enable API access for their DigiCert account and generate an API key in order to use this package. 12 | Contact `DigiCert Customer Support`_ to get started. 13 | 14 | .. _DigiCert Customer Support: https://www.digicert.com/support/ 15 | -------------------------------------------------------------------------------- /python/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digicert/digicert_client/174c39954213f0c0f7f007d81db360ae52faa7fe/python/__init__.py -------------------------------------------------------------------------------- /python/digicert_client/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from .https import VerifiedHTTPSConnection 4 | from .api import Request 5 | from .api.commands.v1 import OrderCertificateCommand as OrderCertificateCommandV1 6 | from .api.commands.v2 import OrderCertificateCommand as OrderCertificateCommandV2 7 | from .api.commands.v2 import UploadCSRCommand as UploadCSRCommandV2 8 | from .api.queries.v1 import ViewOrderDetailsQuery as ViewOrderDetailsQueryV1 9 | from .api.queries.v2 import ViewOrderDetailsQuery as ViewOrderDetailsQueryV2 10 | from .api.queries.v2 import ViewOrdersQuery as ViewOrdersQueryV2 11 | from .api.queries.v1 import DownloadCertificateQuery as DownloadCertificateQueryV1 12 | from .api.queries.v2 import DownloadCertificateQuery as DownloadCertificateQueryV2 13 | from .api.queries.v2 import MyUserQuery, OrganizationByContainerIdQuery, DomainByContainerIdQuery 14 | from .api.queries.v2 import CertificateDuplicateListQuery, DownloadDuplicateQuery 15 | from .api.commands.v2 import OrderDuplicateCommand as OrderDuplicateCommandV2 16 | 17 | 18 | class CertificateType(object): 19 | """Contains supported values for the 'certificate_type' property of OrderCertificateCommand.""" 20 | 21 | SSLPLUS = 'sslplus' 22 | UC = 'uc' 23 | WILDCARD = 'wildcard' 24 | EVSSL = 'evssl' 25 | EVMULTI = 'evmulti' 26 | 27 | def __iter__(self): 28 | for certtype in [self.SSLPLUS, self.UC, self.WILDCARD, self.EVSSL, self.EVMULTI, ]: 29 | yield certtype 30 | 31 | 32 | class Validity(object): 33 | """Contains supported values for the 'validity' property of OrderCertificateCommand.""" 34 | ONE_YEAR = 1 35 | TWO_YEARS = 2 36 | THREE_YEARS = 3 37 | 38 | def __iter__(self): 39 | for period in [self.ONE_YEAR, self.TWO_YEARS, self.THREE_YEARS, ]: 40 | yield period 41 | 42 | 43 | class CertificateOrder(object): 44 | """High-level representation of a certificate order, for placing new orders or working with existing orders.""" 45 | 46 | def __init__(self, host, customer_api_key, customer_name=None, conn=None): 47 | """ 48 | Constructor for CertificateOrder. 49 | 50 | :param host: Host of the web service APIs 51 | :param customer_api_key: Customer's API key for use in authorizing requests 52 | :param customer_name: Optional customer account ID. If left blank, the V2 API will be used; 53 | if not, the V1 API will be used. 54 | :param conn: Optional connection class instance, defaults to VerifiedHTTPSConnection to the provided host 55 | :return: 56 | """ 57 | self.host = host 58 | self.customer_api_key = customer_api_key 59 | self.customer_name = customer_name if customer_name and len(customer_name.strip()) else None 60 | self.conn = conn if conn else VerifiedHTTPSConnection(self.host) 61 | 62 | def _get_container_id_for_active_user(self): 63 | cmd = MyUserQuery(customer_api_key=self.customer_api_key) 64 | me = Request(cmd, self.host, self.conn).send() 65 | return me['container']['id'] 66 | 67 | def _get_matching_organization_id(self, container_id, **kwargs): 68 | cmd = OrganizationByContainerIdQuery(customer_api_key=self.customer_api_key, container_id=container_id) 69 | orgs = Request(cmd, self.host, self.conn).send() 70 | matching_org = None 71 | for org in orgs: 72 | if org['name'] != kwargs['org_name'] or \ 73 | org['address'] != kwargs['org_addr1'] or \ 74 | org['city'] != kwargs['org_city'] or \ 75 | org['state'].lower() != kwargs['org_state'].lower() or \ 76 | org['zip'] != kwargs['org_zip'] or \ 77 | org['country'].lower() != kwargs['org_country'].lower(): 78 | continue 79 | elif 'org_addr2' in kwargs and (not 'address2' in org or org['address2'] != kwargs['org_addr2']): 80 | continue 81 | elif 'org_unit' in kwargs and (not 'unit' in org or org['unit'] != kwargs['org_unit']): 82 | continue 83 | elif 'organization_contact' in org: 84 | org_contact = org['organization_contact'] 85 | if org_contact['first_name'] != kwargs['org_contact_firstname'] or \ 86 | org_contact['last_name'] != kwargs['org_contact_lastname'] or \ 87 | org_contact['email'] != kwargs['org_contact_email'] or \ 88 | org_contact['telephone'] != kwargs['org_contact_telephone']: 89 | continue 90 | elif 'org_contact_job_title' in kwargs and \ 91 | (not 'job_title' in org['organization_contact'] or 92 | org['organization_contact']['job_title'] != kwargs['org_contact_job_title']): 93 | continue 94 | elif 'org_contact_telephone_ext' in kwargs and \ 95 | (not 'telephone_ext' in org['organization_contact'] or 96 | org['organization_contact']['telephone_ext'] != kwargs['org_contact_telephone_ext']): 97 | continue 98 | matching_org = org 99 | return matching_org['id'] if matching_org else None 100 | 101 | def _has_matching_domain(self, container_id, organization_id, common_name): 102 | cmd = DomainByContainerIdQuery(customer_api_key=self.customer_api_key, container_id=container_id) 103 | domains = Request(cmd, self.host, self.conn).send() 104 | for domain in domains: 105 | if domain['organization']['id'] == organization_id and domain['name'] == common_name: 106 | return True 107 | return False 108 | 109 | def place(self, **kwargs): 110 | """Place this order.""" 111 | if self.customer_name: 112 | cmd = OrderCertificateCommandV1(customer_api_key=self.customer_api_key, 113 | customer_name=self.customer_name, 114 | **kwargs) 115 | response = Request(action=cmd, host=self.host, conn=self.conn).send() 116 | return response 117 | else: 118 | # This is a multi-request interaction 119 | container_id = self._get_container_id_for_active_user() 120 | org_id = self._get_matching_organization_id(container_id, **kwargs) 121 | if org_id is None: 122 | return {'status': 404, 'reason': 'Not Found', 'response': 'No matching organization found'} 123 | if not self._has_matching_domain(container_id=container_id, 124 | organization_id=org_id, 125 | common_name=kwargs['common_name']): 126 | return {'status': 404, 'reason': 'Not Found', 'response': 'No matching domain found'} 127 | cmd = OrderCertificateCommandV2(customer_api_key=self.customer_api_key, organization_id=org_id, **kwargs) 128 | response = Request(action=cmd, host=self.host, conn=self.conn).send() 129 | return response 130 | 131 | def view(self, digicert_order_id=None, **kwargs): 132 | """Get details about an existing order.""" 133 | if digicert_order_id: 134 | kwargs['order_id'] = digicert_order_id 135 | if self.customer_name: 136 | cmd = ViewOrderDetailsQueryV1(customer_api_key=self.customer_api_key, 137 | customer_name=self.customer_name, 138 | **kwargs) 139 | else: 140 | cmd = ViewOrderDetailsQueryV2(customer_api_key=self.customer_api_key, **kwargs) 141 | return Request(action=cmd, host=self.host, conn=self.conn).send() 142 | 143 | def view_all(self): 144 | cmd = ViewOrdersQueryV2(customer_api_key=self.customer_api_key) 145 | return Request(action=cmd, host=self.host, conn=self.conn).send() 146 | 147 | def upload_csr(self, digicert_order_id=None, csr_text=None, **kwargs): 148 | if digicert_order_id: 149 | kwargs['order_id'] = digicert_order_id 150 | if csr_text: 151 | kwargs['csr'] = csr_text 152 | cmd = UploadCSRCommandV2(customer_api_key=self.customer_api_key, **kwargs) 153 | return Request(action=cmd, host=self.host, conn=self.conn).send() 154 | 155 | def download(self, digicert_order_id=None, digicert_certificate_id=None, **kwargs): 156 | """Retrieve an issued certificate represented by this order.""" 157 | if digicert_order_id: 158 | if 'order_id' not in kwargs: 159 | kwargs['order_id'] = digicert_order_id 160 | if digicert_certificate_id: 161 | kwargs['certificate_id'] = digicert_certificate_id 162 | if self.customer_name: 163 | cmd = DownloadCertificateQueryV1(customer_api_key=self.customer_api_key, 164 | customer_name=self.customer_name, 165 | **kwargs) 166 | else: 167 | if not 'certificate_id' in kwargs and 'order_id' in kwargs: 168 | cmd = ViewOrderDetailsQueryV2(customer_api_key=self.customer_api_key, **kwargs) 169 | order_details_rsp = Request(action=cmd, host=self.host, conn=self.conn).send() 170 | if 'certificate' in order_details_rsp and 'id' in order_details_rsp['certificate']: 171 | kwargs['certificate_id'] = order_details_rsp['certificate']['id'] 172 | cmd = DownloadCertificateQueryV2(customer_api_key=self.customer_api_key, **kwargs) 173 | return Request(action=cmd, host=self.host, conn=self.conn).send() 174 | 175 | def list_duplicates(self, digicert_order_id=None, **kwargs): 176 | query = CertificateDuplicateListQuery(customer_api_key=self.customer_api_key, order_id=digicert_order_id) 177 | return Request(action=query, host=self.host, conn=self.conn).send() 178 | 179 | def download_duplicate(self, digicert_order_id=None, sub_id=None, **kwargs): 180 | query = DownloadDuplicateQuery(customer_api_key=self.customer_api_key, order_id=digicert_order_id, sub_id=sub_id) 181 | return Request(action=query, host=self.host, conn=self.conn).send() 182 | 183 | def create_duplicate(self, digicert_order_id=None, **kwargs): 184 | cmd = OrderDuplicateCommandV2(customer_api_key=self.customer_api_key, digicert_order_id=digicert_order_id, **kwargs) 185 | return Request(action=cmd, host=self.host, conn=self.conn).send() 186 | 187 | 188 | if __name__ == '__main__': 189 | pass 190 | -------------------------------------------------------------------------------- /python/digicert_client/api/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import json 4 | from urllib import urlencode 5 | 6 | from ..https import VerifiedHTTPSConnection 7 | 8 | 9 | class Request(object): 10 | """ 11 | Abstraction of a REST request. A Request object uses the provided 12 | connection to issue the request represented by the provided action 13 | to the provided host. The action and host are provided via the constructor; 14 | the connection is also optionally provided via the constructor. 15 | """ 16 | 17 | def __init__(self, action, host, conn=None): 18 | """ 19 | Constructs a Request with the provided Action, host, and connection. 20 | Connection is optional but assumes the same interface as HTTPConnection. 21 | If not provided, the default connection is of type VerifiedHTTPSConnection 22 | which is a subclass of HTTPSConnection that also performs peer verification. 23 | 24 | :param action: The Action to initiate. Probably a subclass of Action. 25 | :param host: The host to send the request to. 26 | :param conn: The optional HTTPConnection-style connection to use, defaults 27 | to VerifiedHTTPSConnection. 28 | """ 29 | self.action = action 30 | self.host = host 31 | self.conn = conn if conn is not None else VerifiedHTTPSConnection(host) 32 | 33 | def send(self): 34 | """ 35 | Issues the request represented by this object, obtains the response, extracts the 36 | response data (converting it from JSON if it is in JSON format), sends all the 37 | response data to the Action object for processing, and returns the result of the 38 | response processing. 39 | """ 40 | self.conn.request(self.action.get_method(), 41 | self.action.get_path(), 42 | self.action.get_params(), 43 | self.action.get_headers()) 44 | conn_rsp = self.conn.getresponse() 45 | response_data = conn_rsp.read() 46 | try: 47 | payload = json.loads(response_data) 48 | except ValueError: 49 | payload = response_data 50 | response = self.action.process_response(conn_rsp.status, conn_rsp.reason, payload) 51 | self.conn.close() 52 | return response 53 | 54 | 55 | class Action(object): 56 | """ 57 | Base class for all Commands or Queries. 58 | """ 59 | _headers = {'Accept': 'application/json'} 60 | 61 | def __init__(self, customer_api_key, customer_name=None, **kwargs): 62 | """ 63 | Constructor for an Action. 64 | 65 | :param customer_api_key: the customer's DigiCert API key 66 | :param customer_name: the customer's DigiCert account number, e.g. '012345' Required for 67 | V1-style actions, not required for V2-style actions. 68 | :param kwargs: 69 | :return: 70 | """ 71 | self._customer_api_key = customer_api_key 72 | if customer_name is not None: 73 | self._customer_name = customer_name 74 | for key, value in kwargs.items(): 75 | if not self._process_special(key, value): 76 | setattr(self, key, value) 77 | 78 | def _process_special(self, key, value): 79 | pass 80 | 81 | def set_header(self, key, value): 82 | self._headers[key] = value 83 | 84 | def get_params(self): 85 | params = {} 86 | for param, value in self.__dict__.items(): 87 | if not param.startswith('_'): 88 | params[param] = value 89 | return urlencode(params) 90 | 91 | def get_headers(self): 92 | return self._headers 93 | 94 | def get_method(self): 95 | raise NotImplementedError 96 | 97 | def _subprocess_response(self, status, reason, response): 98 | raise NotImplementedError 99 | 100 | def _is_failure_response(self, response): 101 | return response['response']['result'] == 'failure' 102 | 103 | def _make_response(self, status, reason, response): 104 | if len(response) == 0: 105 | return dict({'http_status': status, 'http_reason': reason}.items()) 106 | else: 107 | if isinstance(response, dict): 108 | return dict({'http_status': status, 'http_reason': reason}.items() + response.items()) 109 | else: 110 | return dict({'http_status': status, 'http_reason': reason, 'response': response}.items()) 111 | 112 | def process_response(self, status, reason, response): 113 | if status >= 300: 114 | return self._make_response(status, reason, response) 115 | try: 116 | return self._subprocess_response(status, reason, response) 117 | except KeyError: 118 | return self._make_response(status, reason, {'result': 'unknown failure', 'response': str(response)}) 119 | 120 | 121 | if __name__ == '__main__': 122 | pass 123 | -------------------------------------------------------------------------------- /python/digicert_client/api/commands/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from ...api import Action 4 | 5 | 6 | class Command(Action): 7 | """Base class for CQRS-style Command objects.""" 8 | 9 | def __init__(self, customer_api_key, customer_name=None, **kwargs): 10 | """ 11 | Command constructor 12 | 13 | :param customer_api_key: the customer's DigiCert API key 14 | :param customer_name: the customer's DigiCert account number, e.g. '012345' 15 | :param kwargs: 16 | :return: 17 | """ 18 | super(Command, self).__init__(customer_api_key=customer_api_key, customer_name=customer_name, **kwargs) 19 | 20 | def get_method(self): 21 | return 'POST' 22 | 23 | 24 | if __name__ == '__main__': 25 | pass -------------------------------------------------------------------------------- /python/digicert_client/api/commands/v1.py: -------------------------------------------------------------------------------- 1 | from base64 import b64encode 2 | 3 | from . import Command 4 | 5 | 6 | class V1Command(Command): 7 | def __init__(self, customer_api_key, customer_name, **kwargs): 8 | super(V1Command, self).__init__(customer_api_key=customer_api_key, customer_name=customer_name, **kwargs) 9 | self.set_header('Authorization', b64encode(':'.join([self.customer_name, self.customer_api_key]))) 10 | self.set_header('Content-Type', 'application/x-www-form-urlencoded') 11 | 12 | 13 | class OrderCertificateCommand(V1Command): 14 | def __init__(self, 15 | customer_api_key, 16 | customer_name, 17 | **kwargs): 18 | """ 19 | Constructs an OrderCertificateCommand, a CQRS-style Command object for ordering certificates. 20 | This is for ordering certificates through DigiCert's V1 API. 21 | 22 | :param customer_api_key: the customer's DigiCert API key 23 | :param customer_name: the customer's DigiCert account number, e.g. '012345' 24 | :param kwargs: The following properties should be included in the kwargs: 25 | - certificate_type 26 | - csr 27 | - validity 28 | - common_name 29 | - org_name 30 | - org_addr1 31 | - org_city 32 | - org_state (2-character code - US state, Canadian Province, etc.) 33 | - org_zip 34 | - org_country (2-character code) 35 | - org_contact_firstname 36 | - org_contact_lastname 37 | - org_contact_email 38 | - org_contact_telephone 39 | 40 | Supported optional properties include: 41 | - server_type 42 | - org_unit 43 | - sans (array of strings) 44 | - org_addr2 45 | - telephone 46 | - org_contact_job_title 47 | - org_contact_telephone_ext 48 | - custom_expiration_date 49 | - comments 50 | 51 | :return: 52 | """ 53 | super(OrderCertificateCommand, self).__init__(customer_api_key, customer_name, **kwargs) 54 | 55 | # certificate_types: 'sslplus', 'uc', 'wildcard', 'evssl' or 'evmulti' 56 | 57 | for field in ['certificate_type', 'csr', 'validity', 'common_name', 58 | 'org_name', 'org_addr1', 'org_city', 'org_state', 'org_zip', 'org_country', 59 | 'org_contact_firstname', 'org_contact_lastname', 'org_contact_email', 'org_contact_telephone']: 60 | if not field in self.__dict__: 61 | raise KeyError('No value provided for required property "%s"' % field) 62 | 63 | def get_path(self): 64 | return '/clients/retail/api/?action=order_certificate' 65 | 66 | def _process_special(self, key, value): 67 | if 'server_type' == key: 68 | self.server_type = int(value) 69 | return True 70 | elif 'validity' == key: 71 | self.validity = int(value) 72 | return True 73 | return False 74 | 75 | def _subprocess_response(self, status, reason, response): 76 | if response['response']['result'] == 'failure': 77 | return self._make_response(status, reason, response['response']['error_codes']) 78 | order_id = response['response']['return']['order_id'] 79 | return self._make_response(status, reason, {'id': order_id}) 80 | 81 | 82 | if __name__ == '__main__': 83 | pass -------------------------------------------------------------------------------- /python/digicert_client/api/commands/v2.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from . import Command 4 | 5 | 6 | class V2Command(Command): 7 | def __init__(self, customer_api_key, **kwargs): 8 | super(V2Command, self).__init__(customer_api_key=customer_api_key, customer_name=None, **kwargs) 9 | self.set_header('X-DC-DEVKEY', customer_api_key) 10 | self.set_header('Content-Type', 'application/json') 11 | 12 | def _is_failure_response(self, response): 13 | return 'errors' in response 14 | 15 | def get_params(self): 16 | return json.dumps(self.__dict__) 17 | 18 | 19 | class OrderCertificateCommand(V2Command): 20 | def __init__(self, customer_api_key, **kwargs): 21 | """ 22 | Constructs an OrderCertificateCommand, a CQRS-style Command object for ordering certificates. 23 | This is for ordering certificates through DigiCert's V2 API. 24 | 25 | :param customer_api_key: the customer's DigiCert API key 26 | :param kwargs: The following properties should be included in the kwargs: 27 | - certificate_type 28 | - csr 29 | - validity 30 | - common_name 31 | - org_name 32 | - org_addr1 33 | - org_city 34 | - org_state (2-character code - US state, Canadian Province, etc.) 35 | - org_zip 36 | - org_country (2-character code) 37 | - org_contact_firstname 38 | - org_contact_lastname 39 | - org_contact_email 40 | - org_contact_telephone 41 | 42 | Supported optional properties include: 43 | - server_type 44 | - org_unit 45 | - sans (array of strings) 46 | - org_addr2 47 | - telephone 48 | - org_contact_job_title 49 | - org_contact_telephone_ext 50 | - custom_expiration_date 51 | - comments 52 | 53 | :return: 54 | """ 55 | super(OrderCertificateCommand, self).__init__(customer_api_key=customer_api_key, **kwargs) 56 | self.certificate =\ 57 | { 58 | 'common_name': kwargs['common_name'], 59 | 'csr': kwargs['csr'], 60 | 'organization_units': [] if not 'organization_units' in kwargs else kwargs['organization_units'], 61 | 'server_platform': {'id': -1} if not 'server_platform' in kwargs else {'id': kwargs['server_platform']}, 62 | 'signature_hash': 'sha256' if not 'signature_hash' in kwargs else kwargs['signature_hash'], 63 | } 64 | self.organization = {'id': kwargs['organization_id']} 65 | self.validity_years = kwargs['validity'] 66 | 67 | def _process_special(self, key, value): 68 | if 'server_type' == key: 69 | self.server_type = int(value) 70 | return True 71 | elif 'validity' == key: 72 | self.validity = int(value) 73 | return True 74 | return False 75 | 76 | def get_path(self): 77 | return '/services/v2/order/certificate/%s' % self._get_cert_type() 78 | 79 | def _get_cert_type(self): 80 | v1_cert_type_to_v2_cert_type = {'sslplus': 'ssl_plus', 'sslmultidomain': 'ssl_multi_domain', 'sslwildcard': 'ssl_wildcard', 'sslevplus': 'ssl_ev_plus', 'sslevmultidomain': 'ssl_ev_multi_domain'} 81 | # SSLPLUS = 'sslplus' UC = 'uc' WILDCARD = 'wildcard' EVSSL = 'evssl' EVMULTI = 'evmulti' 82 | if self.certificate_type in v1_cert_type_to_v2_cert_type: 83 | return v1_cert_type_to_v2_cert_type[self.certificate_type] 84 | return self.certificate_type 85 | 86 | def __str__(self): 87 | return json.dumps(self.__dict__, indent=2, separators=(',', ': ')) 88 | 89 | def _subprocess_response(self, status, reason, response): 90 | return self._make_response(status, reason, response) 91 | 92 | 93 | class UploadCSRCommand(V2Command): 94 | 95 | def __init__(self, customer_api_key, **kwargs): 96 | """ 97 | Constructs an OrderCertificateCommand, a CQRS-style Command object for ordering certificates. 98 | This is for ordering certificates through DigiCert's V2 API. 99 | 100 | :param customer_api_key: the customer's DigiCert API key 101 | :param kwargs: The following properties should be included in the kwargs: 102 | - order_id 103 | - csr 104 | 105 | :return: 106 | """ 107 | super(UploadCSRCommand, self).__init__(customer_api_key=customer_api_key, **kwargs) 108 | self.order_id = kwargs['order_id'] 109 | 110 | def get_path(self): 111 | return '/services/v2/order/certificate/%s/csr' % self.order_id 112 | 113 | def _subprocess_response(self, status, reason, response): 114 | return self._make_response(status, reason, response) 115 | 116 | 117 | class OrderDuplicateCommand(V2Command): 118 | 119 | def __init__(self, customer_api_key, digicert_order_id=None, **kwargs): 120 | """ 121 | 122 | :param customer_api_key: 123 | :param kwargs: 124 | :return: 125 | """ 126 | super(OrderDuplicateCommand, self).__init__(customer_api_key=customer_api_key, **kwargs) 127 | self._order_id = digicert_order_id 128 | 129 | def get_path(self): 130 | return '/services/v2/order/certificate/%s/duplicate' % self._order_id 131 | 132 | def _process_special(self, key, value): 133 | if '_order_id' == key: 134 | return True 135 | 136 | def _subprocess_response(self, status, reason, response): 137 | return response 138 | 139 | def get_params(self): 140 | d = self.__dict__ 141 | if '_order_id' in d: 142 | del d['_order_id'] 143 | if '_customer_api_key' in d: 144 | del d['_customer_api_key'] 145 | return json.dumps(d) 146 | 147 | 148 | if __name__ == '__main__': 149 | pass 150 | -------------------------------------------------------------------------------- /python/digicert_client/api/queries/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from ...api import Action 4 | 5 | 6 | class Query(Action): 7 | """ 8 | Base class for CQRS-style Query objects. 9 | 10 | 11 | :param customer_api_key: the customer's DigiCert API key 12 | :param customer_name: the customer's DigiCert account number, e.g. '012345' 13 | :param kwargs: 14 | """ 15 | def __init__(self, customer_api_key, customer_name=None, **kwargs): 16 | super(Query, self).__init__(customer_api_key=customer_api_key, customer_name=customer_name, **kwargs) 17 | 18 | def get_method(self): 19 | return 'GET' 20 | 21 | 22 | if __name__ == '__main__': 23 | pass -------------------------------------------------------------------------------- /python/digicert_client/api/queries/v1.py: -------------------------------------------------------------------------------- 1 | from base64 import b64encode 2 | 3 | from ..queries import Query 4 | 5 | 6 | class V1Query(Query): 7 | order_id = None 8 | 9 | def __init__(self, customer_api_key, customer_name, **kwargs): 10 | super(V1Query, self).__init__(customer_api_key, customer_name=customer_name, **kwargs) 11 | self.set_header('Authorization', b64encode(':'.join([self.customer_name, self.customer_api_key]))) 12 | self.set_header('Content-Type', 'application/x-www-form-urlencoded') 13 | if self.order_id is None: 14 | raise KeyError('No value provided for required property "order_id"') 15 | 16 | def get_method(self): 17 | return 'POST' 18 | 19 | 20 | class ViewOrderDetailsQuery(V1Query): 21 | def __init__(self, customer_api_key, customer_name, **kwargs): 22 | """ 23 | Construct a ViewOrderDetailsQuery, a CQRS-style query object representing a request 24 | to view the details of an already-placed certificate order. This the V1 version of 25 | the query. 26 | 27 | 28 | :param customer_api_key: the customer's DigiCert API key 29 | :param customer_name: the customer's DigiCert account number, e.g. '012345' 30 | :param kwargs: 31 | :return: 32 | """ 33 | super(ViewOrderDetailsQuery, self).__init__(customer_api_key, customer_name=customer_name, **kwargs) 34 | 35 | def get_path(self): 36 | return '/clients/retail/api/?action=order_view_details' 37 | 38 | def _subprocess_response(self, status, reason, response): 39 | cert_details = response['response']['return']['certificate_details'] 40 | response = { 41 | 'id': cert_details['order_id'], 42 | 'status': cert_details['status'], 43 | 'certificate': { 44 | 'common_name': cert_details['common_name'], 45 | }, 46 | 'product': { 47 | 'name': cert_details['product_name'] 48 | }, 49 | } 50 | if 'validity' in cert_details: 51 | response['certificate']['validity'] = cert_details['validity'] 52 | if 'sans' in cert_details: 53 | response['certificate']['dns_names'] = cert_details['sans'] 54 | if 'valid_from' in cert_details: 55 | response['certificate']['date_created'] = cert_details['valid_from'] 56 | if 'org_unit' in cert_details: 57 | response['certificate']['organization_units'] = [cert_details['org_unit']] 58 | if 'server_type' in cert_details or 'server_type_name' in cert_details: 59 | response['certificate']['server_platform'] = {} 60 | if 'server_type' in cert_details: 61 | response['certificate']['server_platform']['id'] = cert_details['server_type'] 62 | if 'server_type_name' in cert_details: 63 | response['certificate']['server_platform']['name'] = cert_details['server_type_name'] 64 | return self._make_response(status, reason, response) 65 | 66 | 67 | class DownloadCertificateQuery(V1Query): 68 | def __init__(self, customer_api_key, customer_name, **kwargs): 69 | """ 70 | Construct a DownloadCertificateQuery, a CQRS-style query object representing a request 71 | to download the certificate resulting from a certificate order. This is the V1 version 72 | of the query. 73 | 74 | :param customer_api_key: the customer's DigiCert API key 75 | :param customer_name: the customer's DigiCert account number, e.g. '012345' 76 | :param kwargs: 77 | :return: 78 | """ 79 | super(DownloadCertificateQuery, self).__init__(customer_api_key, customer_name=customer_name, **kwargs) 80 | 81 | def get_path(self): 82 | return '/clients/retail/api/?action=retrieve_certificate' 83 | 84 | def _subprocess_response(self, status, reason, response): 85 | certs = response['response']['return']['certs'] 86 | rsp = { 87 | 'certificates': { 88 | 'certificate': certs['certificate'].strip(), 89 | 'intermediate': certs['intermediate'].strip(), 90 | } 91 | } 92 | if 'root' in certs: 93 | rsp['certificates']['root'] = certs['root'].strip() 94 | if 'pkcs7' in certs: 95 | rsp['certificates']['pkcs7'] = certs['pkcs7'].strip() 96 | return self._make_response(status, reason, rsp) 97 | 98 | if __name__ == '__main__': 99 | pass -------------------------------------------------------------------------------- /python/digicert_client/api/queries/v2.py: -------------------------------------------------------------------------------- 1 | from ..queries import Query 2 | 3 | 4 | class V2Query(Query): 5 | _base_path = '/services/v2' 6 | 7 | def __init__(self, customer_api_key, **kwargs): 8 | super(V2Query, self).__init__(customer_api_key=customer_api_key, customer_name=None) 9 | self.set_header('X-DC-DEVKEY', customer_api_key) 10 | self.set_header('Content-Type', 'application/json') 11 | 12 | def get_method(self): 13 | return 'GET' 14 | 15 | def _is_failure_response(self, response): 16 | return 'errors' in response 17 | 18 | 19 | class ViewOrderDetailsQuery(V2Query): 20 | order_id = None 21 | 22 | def __init__(self, customer_api_key, **kwargs): 23 | """ 24 | Construct a ViewOrderDetailsQuery, a CQRS-style query object representing a request 25 | to view the details of an already-placed certificate order. This the V2 version of 26 | the query. 27 | 28 | 29 | :param customer_api_key: the customer's DigiCert API key 30 | :param kwargs: 31 | :return: 32 | """ 33 | super(ViewOrderDetailsQuery, self).__init__(customer_api_key=customer_api_key, **kwargs) 34 | if 'order_id' in kwargs: 35 | self.order_id = kwargs['order_id'] 36 | if self.order_id is None: 37 | raise KeyError('No value provided for required property "order_id"') 38 | 39 | def get_path(self): 40 | return '%s/order/certificate/%s' % (self._base_path, self.order_id) 41 | 42 | def _subprocess_response(self, status, reason, response): 43 | return self._make_response(status, reason, response) 44 | 45 | 46 | class ViewOrdersQuery(V2Query): 47 | 48 | def __init__(self, customer_api_key): 49 | super(ViewOrdersQuery, self).__init__(customer_api_key=customer_api_key) 50 | 51 | def get_path(self): 52 | return '%s/order/certificate' % self._base_path 53 | 54 | def _subprocess_response(self, status, reason, response): 55 | return self._make_response(status, reason, response) 56 | 57 | 58 | class DownloadCertificateQuery(V2Query): 59 | order_id = None 60 | certificate_id = None 61 | 62 | def __init__(self, customer_api_key, **kwargs): 63 | """ 64 | Construct a DownloadCertificateQuery, a CQRS-style query object representing a request 65 | to download the certificate resulting from a certificate order. This is the V2 version 66 | of the query. 67 | 68 | :param customer_api_key: the customer's DigiCert API key 69 | :param kwargs: 70 | :return: 71 | """ 72 | super(DownloadCertificateQuery, self).__init__(customer_api_key=customer_api_key, **kwargs) 73 | if 'order_id' in kwargs: 74 | self.order_id = kwargs['order_id'] 75 | if 'certificate_id' in kwargs: 76 | self.certificate_id = kwargs['certificate_id'] 77 | if self.certificate_id is None and self.order_id is None: 78 | raise KeyError('No value provided for required properties "certificate_id", "order_id" (at least one is required)') 79 | 80 | def get_path(self): 81 | # if we don't have a certificate ID, then we know it's a retail account, and we call the download by order ID endpoint 82 | if self.certificate_id: 83 | url = '%s/certificate/%s/download/format/pem_all' % (self._base_path, self.certificate_id) 84 | else: 85 | url = '%s/certificate/download/order/%s' % (self._base_path, self.order_id) 86 | return url 87 | 88 | def _subprocess_response(self, status, reason, response): 89 | certs = [] 90 | if '-----' in response: 91 | for cert in response.split('-----'): 92 | cert = cert.strip() 93 | if len(cert) and not cert.startswith('BEGIN ') and not cert.startswith('END '): 94 | certs.append(cert) 95 | if 3 != len(certs): 96 | raise RuntimeError('Unexpected number of certificates in certificate chain') 97 | return self._make_response(status, reason, { 98 | 'certificates': { 99 | 'certificate': '-----BEGIN CERTIFICATE-----\r\n' + certs[0] + '\r\n-----END CERTIFICATE-----', 100 | 'intermediate': '-----BEGIN CERTIFICATE-----\r\n' + certs[1] + '\r\n-----END CERTIFICATE-----', 101 | 'root': '-----BEGIN CERTIFICATE-----\r\n' + certs[2] + '\r\n-----END CERTIFICATE-----' 102 | } 103 | }) 104 | else: 105 | # this must be a zip file containing certs 106 | return response # this is a zip file 107 | 108 | 109 | class MyUserQuery(V2Query): 110 | def __init__(self, customer_api_key): 111 | """ 112 | Construct a MyUserQuery, a CQRS-style query object to get details about the 113 | user represented by the provided customer api key. 114 | 115 | :param customer_api_key: the customer's DigiCert API key 116 | :return: 117 | """ 118 | super(MyUserQuery, self).__init__(customer_api_key=customer_api_key) 119 | 120 | def get_path(self): 121 | return '%s/user/me' % self._base_path 122 | 123 | def _subprocess_response(self, status, reason, response): 124 | return response 125 | 126 | 127 | class OrganizationByContainerIdQuery(V2Query): 128 | def __init__(self, customer_api_key, container_id): 129 | """ 130 | Construct an OrganizationByContainerIdQuery, a CQRS-style query object to obtain 131 | a list of organizations associated with the supplied container id. 132 | 133 | :param customer_api_key: the customer's DigiCert API key 134 | :param container_id: the container id for the organizations 135 | :return: 136 | """ 137 | super(OrganizationByContainerIdQuery, self).__init__(customer_api_key=customer_api_key) 138 | self.container_id = container_id 139 | 140 | def get_path(self): 141 | return '%s/organization?container_id=%s' % (self._base_path, self.container_id) 142 | 143 | def _subprocess_response(self, status, reason, response): 144 | orgs = [] 145 | for entry in response['organizations']: 146 | orgs.append(entry) 147 | return orgs 148 | 149 | 150 | class DomainByContainerIdQuery(V2Query): 151 | def __init__(self, customer_api_key, container_id): 152 | """ 153 | Construct a DomainByContainerIdQuery, a CQRS-style query object to obtain 154 | a list of domains associated with the supplied container id. 155 | 156 | :param customer_api_key: the customer's DigiCert API key 157 | :param container_id: the container id for the domains 158 | :return: 159 | """ 160 | super(DomainByContainerIdQuery, self).__init__(customer_api_key=customer_api_key) 161 | self.container_id = container_id 162 | 163 | def get_path(self): 164 | return '%s/domain?container_id=%s' % (self._base_path, self.container_id) 165 | 166 | def _subprocess_response(self, status, reason, response): 167 | domains = [] 168 | for entry in response['domains']: 169 | domains.append(entry) 170 | return domains 171 | 172 | 173 | class CertificateDuplicateListQuery(V2Query): 174 | def __init__(self, customer_api_key, order_id): 175 | """ 176 | :param customer_api_key: the customer's DigiCert API key 177 | :param order_id: the order_id attached to the duplicate certificate 178 | :return: 179 | """ 180 | super(CertificateDuplicateListQuery, self).__init__(customer_api_key=customer_api_key) 181 | self.order_id = order_id 182 | 183 | def get_path(self): 184 | return '%s/order/certificate/%s/duplicate' % (self._base_path, self.order_id) 185 | 186 | def _subprocess_response(self, status, reason, response): 187 | return response.get('certificates') 188 | 189 | 190 | class DownloadDuplicateQuery(V2Query): 191 | def __init__(self, customer_api_key, order_id, sub_id): 192 | """ 193 | :param customer_api_key: the customer's DigiCert API key 194 | :param order_id: the order_id attached to the duplicate certificate 195 | :param sub_id: the duplicate id 196 | :return: 197 | """ 198 | super(DownloadDuplicateQuery, self).__init__(customer_api_key=customer_api_key) 199 | self.order_id = order_id 200 | self.sub_id = sub_id 201 | 202 | def get_path(self): 203 | return '%s/certificate/download/order/%s?subId=%s&formatType=pem_all' % (self._base_path, self.order_id, self.sub_id) 204 | 205 | def _subprocess_response(self, status, reason, response): 206 | if isinstance(response, type('str')): 207 | duplicates = response.split('-----END CERTIFICATE-----\r\n') 208 | duplicates = [x for x in duplicates if x] 209 | duplicates = [x + '-----END CERTIFICATE-----\r\n' for x in duplicates] 210 | return duplicates 211 | else: 212 | return response 213 | 214 | if __name__ == '__main__': 215 | pass -------------------------------------------------------------------------------- /python/digicert_client/https/DigiCertRoots.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl 3 | MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 4 | d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv 5 | b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG 6 | EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl 7 | cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi 8 | MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c 9 | JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP 10 | mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+ 11 | wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4 12 | VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/ 13 | AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB 14 | AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW 15 | BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun 16 | pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC 17 | dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf 18 | fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm 19 | NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx 20 | H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe 21 | +o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== 22 | -----END CERTIFICATE----- 23 | -----BEGIN CERTIFICATE----- 24 | MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh 25 | MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 26 | d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD 27 | QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT 28 | MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j 29 | b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG 30 | 9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB 31 | CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 32 | nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt 33 | 43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P 34 | T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 35 | gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO 36 | BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR 37 | TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw 38 | DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr 39 | hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg 40 | 06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF 41 | PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls 42 | YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk 43 | CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= 44 | -----END CERTIFICATE----- 45 | -----BEGIN CERTIFICATE----- 46 | MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs 47 | MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 48 | d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j 49 | ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL 50 | MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 51 | LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug 52 | RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm 53 | +9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW 54 | PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM 55 | xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB 56 | Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 57 | hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg 58 | EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF 59 | MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA 60 | FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec 61 | nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z 62 | eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF 63 | hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 64 | Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe 65 | vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep 66 | +OkuE6N36B9K 67 | -----END CERTIFICATE----- 68 | -------------------------------------------------------------------------------- /python/digicert_client/https/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import socket 4 | import ssl 5 | import os 6 | import sys 7 | from fnmatch import fnmatch 8 | from httplib import HTTPSConnection 9 | 10 | 11 | class VerifiedHTTPSConnection(HTTPSConnection): 12 | """ 13 | VerifiedHTTPSConnection - an HTTPSConnection that performs name and server cert verification 14 | when a connection is created. 15 | """ 16 | 17 | # This code is based very closely on https://gist.github.com/Caligatio/3399114. 18 | 19 | ca_file = None 20 | 21 | def __init__(self, 22 | host, 23 | port=None, 24 | ca_file=None, 25 | **kwargs): 26 | HTTPSConnection.__init__(self, 27 | host=host, 28 | port=port, 29 | **kwargs) 30 | 31 | if ca_file: 32 | self.ca_file = ca_file 33 | else: 34 | self.ca_file = os.path.join(os.path.dirname(__file__), 'DigiCertRoots.pem') 35 | 36 | def connect(self): 37 | if self.ca_file and os.path.exists(self.ca_file): 38 | # TODO: is there a better way to do this? 2.6 doesn't support source_address. 39 | if sys.version_info < (2, 7, 0): 40 | sock = socket.create_connection( 41 | (self.host, self.port), 42 | self.timeout 43 | ) 44 | else: 45 | sock = socket.create_connection( 46 | (self.host, self.port), 47 | self.timeout, self.source_address 48 | ) 49 | 50 | if self._tunnel_host: 51 | self.sock = sock 52 | self._tunnel() 53 | 54 | # Wrap the socket using verification with the root certs, note the hardcoded path 55 | self.sock = ssl.wrap_socket(sock, 56 | self.key_file, 57 | self.cert_file, 58 | cert_reqs=ssl.CERT_REQUIRED, 59 | ca_certs=self.ca_file) 60 | verify_peer(self.host, self.sock.getpeercert()) 61 | else: 62 | raise RuntimeError('No CA file configured for VerifiedHTTPSConnection') 63 | 64 | 65 | def verify_peer(remote_host, peer_certificate): 66 | """ 67 | check_hostname() 68 | 69 | Checks the hostname being accessed against the various hostnames present 70 | in the remote certificate 71 | """ 72 | hostnames = set() 73 | wildcard_hostnames = set() 74 | 75 | for subject in peer_certificate['subject']: 76 | if 'commonName' == subject[0] and len(subject) > 1: 77 | hostname = subject[1].encode('utf-8') 78 | wch_tuple = tuple(hostname.split('.')) 79 | if -1 != wch_tuple[0].find('*'): 80 | wildcard_hostnames.add(wch_tuple) 81 | else: 82 | hostnames.add(hostname) 83 | 84 | # Get the subject alternative names out of the certificate 85 | try: 86 | sans = (x for x in peer_certificate['subjectAltName'] if x[0] == 'DNS') 87 | for san in sans: 88 | if len(san) > 1: 89 | wch_tuple = tuple(san[1].split('.')) 90 | if -1 != wch_tuple[0].find('*'): 91 | wildcard_hostnames.add(wch_tuple) 92 | else: 93 | hostnames.add(san[1]) 94 | except KeyError: 95 | pass 96 | 97 | if remote_host not in hostnames: 98 | wildcard_match = False 99 | rh_tuple = tuple(remote_host.split('.')) 100 | for wch_tuple in wildcard_hostnames: 101 | l = len(wch_tuple) 102 | if len(rh_tuple) == l: 103 | l -= 1 104 | rhparts_match = True 105 | while l < 0: 106 | if rh_tuple[l] != wch_tuple[l]: 107 | rhparts_match = False 108 | break 109 | if rhparts_match and fnmatch(rh_tuple[0], wch_tuple[0]): 110 | wildcard_match = True 111 | if not wildcard_match: 112 | raise ssl.SSLError('hostname "%s" doesn\'t match certificate name(s) "%s"' % 113 | (remote_host, ', '.join(hostnames))) 114 | 115 | 116 | if __name__ == '__main__': 117 | pass 118 | -------------------------------------------------------------------------------- /python/digicert_client/tests/TestClient.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from sys import argv, exit 4 | from os import makedirs 5 | from os.path import exists, dirname, expanduser, splitext 6 | from httplib import HTTPSConnection 7 | 8 | from .. import Validity, CertificateType, CertificateOrder 9 | 10 | use_verified_http = False 11 | 12 | 13 | def usage(): 14 | print 'TestClient.py order|details |download ' 15 | exit(1) 16 | 17 | 18 | def read_default_properties(path): 19 | p = {} 20 | if exists(path): 21 | with open(path) as pf: 22 | for line in pf.readlines(): 23 | line = line.strip() 24 | if not line.startswith('#'): 25 | key, value = line.split(':', 1) 26 | key = key.strip() 27 | value = value.strip() 28 | p[key] = value 29 | return p 30 | 31 | 32 | def validate_certificate_validity(validity, properties): 33 | if Validity.THREE_YEARS == validity and \ 34 | (properties['certificate_type'] == CertificateType.EVSSL or 35 | properties['certificate_type'] == CertificateType.EVMULTI): 36 | return 'Select only 1 or 2 years for certificate type "%s"' % properties['certificate_type'] 37 | return None 38 | 39 | 40 | def validate_two_chars(p, properties): 41 | if 2 != len(p): 42 | return 'Input must be a two-character code' 43 | return None 44 | 45 | 46 | def validate_zip(p, properties): 47 | if 5 != len(p) or not p.isdigit(): 48 | return 'Zip code must be five digits' 49 | return None 50 | 51 | 52 | def validate_telephone(p, properties): 53 | if 10 != len(p) or not p.isdigit(): 54 | return 'Telephone must be 10 digits' 55 | return None 56 | 57 | 58 | def validate_csr_path(p, properties): 59 | if not exists(p): 60 | return 'No such file "%s"' % p 61 | if '.csr' != splitext(p)[1]: 62 | return 'File "%s" does not have .csr extension' % p 63 | with open(p) as csr: 64 | lines = csr.readlines() 65 | if lines[0].strip() != '-----BEGIN CERTIFICATE REQUEST-----' or \ 66 | lines[len(lines)-1].strip() != '-----END CERTIFICATE REQUEST-----': 67 | return 'File "%s" does not appear to be a valid CSR' % p 68 | return None 69 | 70 | 71 | def get_property(properties, key, prompt, allowed_values=[], allow_empty=False, validator=None): 72 | default = properties.get(key, None) 73 | full_prompt = prompt 74 | if len(allowed_values): 75 | full_prompt = '%s (%s)' % (full_prompt, ','.join(allowed_values)) 76 | if default: 77 | full_prompt = '%s [%s]' % (full_prompt, default) 78 | full_prompt += ': ' 79 | p = None 80 | while not p: 81 | p = raw_input(full_prompt) 82 | if default and p == '': 83 | p = default 84 | if p == '' and allow_empty: 85 | break 86 | if len(allowed_values) and not p in allowed_values: 87 | print 'Illegal input "%s" - valid values are %s' % (p, ','.join(allowed_values)) 88 | p = None 89 | if p and validator: 90 | msg = validator(p, properties) 91 | if msg: 92 | print msg 93 | p = None 94 | return p 95 | 96 | 97 | def save_properties(properties, path): 98 | dir = dirname(path) 99 | if not exists(dir): 100 | makedirs(dir) 101 | with open(path, 'w') as pf: 102 | pf.writelines(['%s:%s\n' % (k, v) for k, v in properties.items()]) 103 | 104 | 105 | def get_properties(cmd): 106 | propsfile = '%s/.digicert/.digicert_procure_testclient.properties' % expanduser('~') 107 | properties = read_default_properties(propsfile) 108 | 109 | properties['customer_account_id'] = get_property(properties, 'customer_account_id', 'Customer Account Id', allow_empty=True) 110 | properties['customer_api_key'] = get_property(properties, 'customer_api_key', 'Customer API Key') 111 | properties['host'] = get_property(properties, 'host', 'Hostname') 112 | 113 | if 'order' == cmd: 114 | properties['certificate_type'] = get_property(properties, 115 | 'certificate_type', 116 | 'Certificate Type', 117 | [certtype for certtype in CertificateType()]) 118 | properties['validity'] = get_property(properties, 119 | 'validity', 120 | 'Validity Period', 121 | ['%d' % period for period in Validity()], 122 | validate_certificate_validity) 123 | properties['common_name'] = get_property(properties, 'common_name', 'Common Name') 124 | properties['org_name'] = get_property(properties, 'org_name', 'Organization Name') 125 | properties['org_addr1'] = get_property(properties, 'org_addr1', 'Organization Address') 126 | properties['org_city'] = get_property(properties, 'org_city', 'Organization City') 127 | properties['org_state'] = get_property(properties, 'org_state', 'Organization State', validator=validate_two_chars) 128 | properties['org_zip'] = get_property(properties, 'org_zip', 'Organization Zip Code', validator=validate_zip) 129 | properties['org_country'] = get_property(properties, 'org_country', 'Organization Country', validator=validate_two_chars) 130 | properties['org_contact_firstname'] = get_property(properties, 131 | 'org_contact_firstname', 132 | 'Organization Contact First Name') 133 | properties['org_contact_lastname'] = get_property(properties, 134 | 'org_contact_lastname', 135 | 'Organization Contact Last Name') 136 | properties['org_contact_email'] = get_property(properties, 137 | 'org_contact_email', 138 | 'Organization Contact Email') 139 | properties['org_contact_telephone'] = get_property(properties, 140 | 'org_contact_telephone', 141 | 'Organization Contact Telephone', 142 | validator=validate_telephone) 143 | properties['csr_path'] = get_property(properties, 'csr_path', 'Path to CSR file', validator=validate_csr_path) 144 | 145 | if properties['certificate_type'] == CertificateType.EVSSL or \ 146 | properties['certificate_type'] == CertificateType.EVMULTI: 147 | properties['telephone'] = properties['org_contact_telephone'] 148 | properties['org_contact_job_title'] = get_property(properties, 149 | 'org_contact_job_title', 150 | 'Organization Contact Job Title') 151 | 152 | save_properties(properties, propsfile) 153 | 154 | if 'customer_account_id' not in properties or 0 == len(properties['customer_account_id']): 155 | properties['customer_account_id'] = None 156 | 157 | return properties 158 | 159 | 160 | def order_certificate(properties): 161 | csr = None 162 | with open(properties['csr_path']) as csr_file: 163 | csr = ''.join(csr_file.readlines()[1:-1]).strip() 164 | 165 | order = CertificateOrder(host=properties['host'], 166 | customer_api_key=properties['customer_api_key'], 167 | customer_name=properties['customer_account_id'], 168 | conn=(None if use_verified_http else HTTPSConnection(properties['host']))) 169 | order_params = dict({'csr': csr}.items() + properties.items()) 170 | del order_params['customer_account_id'] 171 | del order_params['customer_api_key'] 172 | response = order.place(**order_params) 173 | print response 174 | 175 | 176 | def view_certificate(order_id, properties): 177 | order = CertificateOrder(host=properties['host'], 178 | customer_api_key=properties['customer_api_key'], 179 | customer_name=properties['customer_account_id'], 180 | conn=(None if use_verified_http else HTTPSConnection(properties['host']))) 181 | response = order.view(order_id=order_id) 182 | print response 183 | 184 | 185 | def download_certificate(order_id, properties): 186 | order = CertificateOrder(host=properties['host'], 187 | customer_api_key=properties['customer_api_key'], 188 | customer_name=properties['customer_account_id'], 189 | conn=(None if use_verified_http else HTTPSConnection(properties['host']))) 190 | response = order.download(order_id=order_id) 191 | print response 192 | 193 | 194 | if __name__ == '__main__': 195 | if len(argv) < 2: 196 | usage() 197 | 198 | if argv[1] == 'order': 199 | order_certificate(get_properties(argv[1])) 200 | elif argv[1] == 'view': 201 | if len(argv) < 3: 202 | usage() 203 | view_certificate(argv[2], get_properties(argv[1])) 204 | elif argv[1] == 'download': 205 | if len(argv) < 3: 206 | usage() 207 | download_certificate(argv[2], get_properties(argv[1])) 208 | else: 209 | usage() 210 | -------------------------------------------------------------------------------- /python/digicert_client/tests/TestDownloadCertificate.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from . import MockConnection 4 | from .. import CertificateOrder 5 | 6 | 7 | class TestDownloadCertificate(unittest.TestCase): 8 | cert = '-----BEGIN CERTIFICATE-----\r\n' + \ 9 | 'MIIFLzCCBBegAwIBAgIQDrB5sV1kcdaaq0JZlUyGMTANBgkqhkiG9w0BAQsFADBT\r\n' + \ 10 | 'I3gCIROT/MrrhQrVs6QbIxOGUIxhXs3FaZDcJVM46R8NUVI=\r\n' + \ 11 | '-----END CERTIFICATE-----\r\n' 12 | inter = '-----BEGIN CERTIFICATE-----\r\n' + \ 13 | 'MIIEtTCCA52gAwIBAgIQDIu2tdNzzmdKCjRKdwemlzANBgkqhkiG9w0BAQsFADBn\r\n' + \ 14 | 'am8+4SqHrmoq\r\n' + \ 15 | '-----END CERTIFICATE-----\r\n' 16 | root = '-----BEGIN CERTIFICATE-----\r\n' + \ 17 | 'MIIDuzCCAqOgAwIBAgIQD2u/YBwj+V0fclDibEkALjANBgkqhkiG9w0BAQUFADBn\r\n' + \ 18 | 'ORWAxdScvkQdqON/Mlcstd/j8biZXvO2doaxo+zsVM94BKqdMnqnhRWh/9m/fEE=\r\n' + \ 19 | '-----END CERTIFICATE-----\r\n' 20 | pkcs7 = '-----BEGIN PKCS7-----\r\n' + \ 21 | 'MIIOGQYJKoZIhvcNAQcCoIIOCjCCDgYCAQExADALBgkqhkiG9w0BBwGggg3uMIIF\r\n' + \ 22 | '7gN6zBBxQCfQuMxAA==\r\n' + \ 23 | '-----END PKCS7-----\r\n' 24 | cert_id = '990929' 25 | v1_download_order_response = (200, 'OK', { 26 | 'response': { 27 | 'result': 'success', 28 | 'return': { 29 | 'order_id': 'OID-223344', 30 | 'serial': '07C2EDE40FEEA2AA03C0615F32D3A26D', 31 | 'certs': { 32 | 'certificate': cert, 33 | 'intermediate': inter, 34 | 'root': root, 35 | 'pkcs7': pkcs7 36 | } 37 | } 38 | } 39 | }) 40 | v2_view_order_response = (200, 'OK', {'certificate': {'id': '990929'}}) 41 | v2_download_order_response = (200, 'OK', '%s%s%s' % (cert, inter, root)) 42 | v1_order = CertificateOrder(host='localhost', 43 | customer_api_key='abc123', 44 | customer_name='12345', 45 | conn=MockConnection('localhost', 46 | responses={ 47 | '/clients/retail/api/?action=retrieve_certificate': v1_download_order_response, 48 | })) 49 | v2_order = CertificateOrder(host='localhost', 50 | customer_api_key='abc123', 51 | conn=MockConnection('localhost', 52 | responses={ 53 | '/services/v2/order/certificate/OID-223344': v2_view_order_response, 54 | '/services/v2/certificate/990929/download/format/pem_all': v2_download_order_response, 55 | })) 56 | 57 | def verify_response(self, response): 58 | self.assertEqual(200, response['http_status']) 59 | self.assertEqual('OK', response['http_reason']) 60 | self.assertEqual(self.cert.strip(), response['certificates']['certificate']) 61 | self.assertEqual(self.inter.strip(), response['certificates']['intermediate']) 62 | self.assertEqual(self.root.strip(), response['certificates']['root']) 63 | if 'pkcs7' in response['certificates']: 64 | self.assertEqual(self.pkcs7.strip(), response['certificates']['pkcs7']) 65 | 66 | def test_download_v1_order(self): 67 | response = self.v1_order.download(**{'order_id': 'OID-223344'}) 68 | self.verify_response(response) 69 | 70 | def test_download_v2_order(self): 71 | response = self.v2_order.download(**{'order_id': 'OID-223344'}) 72 | self.verify_response(response) 73 | 74 | def test_download_v2_order_with_order_id_param(self): 75 | response = self.v2_order.download(digicert_order_id='OID-223344') 76 | self.verify_response(response) 77 | 78 | def test_download_v1_order_with_certificate_id_fails(self): 79 | try: 80 | self.v1_order.download(**{'certificate_id': self.cert_id}) 81 | self.fail('Expected exception but none thrown') 82 | except KeyError: 83 | pass 84 | 85 | def test_download_v2_order_with_certificate_id(self): 86 | response = self.v2_order.download(**{'certificate_id': self.cert_id}) 87 | self.verify_response(response) 88 | 89 | def test_download_v2_order_with_certificate_id_param(self): 90 | response = self.v2_order.download(digicert_certificate_id=self.cert_id) 91 | self.verify_response(response) 92 | 93 | def test_download_v1_order_without_order_id(self): 94 | try: 95 | self.v1_order.download(**{'not_order_id': 'fake'}) 96 | self.fail('Expected exception but none thrown') 97 | except KeyError: 98 | pass 99 | 100 | def test_download_v2_order_without_order_id(self): 101 | try: 102 | self.v2_order.download(**{'not_order_id': 'fake'}) 103 | self.fail('Expected exception but none thrown') 104 | except KeyError: 105 | pass 106 | 107 | 108 | if __name__ == '__main__': 109 | unittest.main() -------------------------------------------------------------------------------- /python/digicert_client/tests/TestDownloadCertificateQuery.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import unittest 4 | 5 | from ..api.queries.v1 import DownloadCertificateQuery as DownloadCertificateQueryV1 6 | from ..api.queries.v2 import DownloadCertificateQuery as DownloadCertificateQueryV2 7 | 8 | 9 | class BaseTestDownloadCertificateQuery(object): 10 | _customer_name = '12345' 11 | _customer_api_key = 'abapsdrtaewrh89249sbs89as0d' 12 | _api_host = 'www.digicert.com' 13 | _api_path = '/clients/retail/api/?action=retrieve_certificate' 14 | 15 | def get_dcq(self): 16 | raise NotImplementedError 17 | 18 | def verify_dcq(self, dcq): 19 | self.assertEqual(self._customer_api_key, dcq.customer_api_key) 20 | self.assertEqual('567890', dcq.order_id) 21 | 22 | def test_construct(self): 23 | dcq = self.get_dcq() 24 | self.verify_dcq(dcq) 25 | 26 | 27 | class TestDownloadCertificateQueryV1(BaseTestDownloadCertificateQuery, unittest.TestCase): 28 | def get_dcq(self): 29 | return DownloadCertificateQueryV1(customer_name=self._customer_name, 30 | customer_api_key=self._customer_api_key, 31 | order_id='567890') 32 | 33 | def verify_dcq(self, dcq): 34 | super(TestDownloadCertificateQueryV1, self).verify_dcq(dcq) 35 | self.assertEqual(self._customer_name, dcq.customer_name) 36 | 37 | 38 | class TestDownloadCertificateQueryV2(BaseTestDownloadCertificateQuery, unittest.TestCase): 39 | def get_dcq(self): 40 | return DownloadCertificateQueryV2(customer_api_key=self._customer_api_key, 41 | order_id='567890') 42 | 43 | def test_construct_with_certificate_id(self): 44 | dcq = DownloadCertificateQueryV2(customer_api_key=self._customer_api_key, 45 | certificate_id='990929') 46 | self.assertEqual(self._customer_api_key, dcq.customer_api_key) 47 | self.assertEqual('990929', dcq.certificate_id) 48 | 49 | 50 | if __name__ == '__main__': 51 | unittest.main() -------------------------------------------------------------------------------- /python/digicert_client/tests/TestOrderCertificate.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from . import MockConnection 4 | from .. import CertificateOrder 5 | 6 | 7 | class TestOrderCertificate(unittest.TestCase): 8 | v1_order_created_response = (201, 'Created', { 9 | 'response': { 10 | 'result': 'success', 11 | 'return': { 12 | 'order_id': 'OID-223344', 13 | }, 14 | }, 15 | },) 16 | v2_order_created_response = (201, 'Created', { 17 | 'id': 'OID-223344', 18 | 'requests': {'id': '288382'} 19 | },) 20 | me_response = (200, 'OK', { 21 | 'id': '102938', 22 | "container": { 23 | 'id': '987654', 24 | }, 25 | },) 26 | my_org_response = (200, 'OK', { 27 | 'organizations': 28 | [{ 29 | 'id': '564738', 30 | 'name': 'FakeCo', 31 | 'address': '123 Nowhere Lane', 32 | 'address2': 'Infinitieth Floor', 33 | 'zip': '12345', 34 | 'city': 'Nowhere', 35 | 'state': 'UT', 36 | 'country': 'US', 37 | 'unit': 'An Org', 38 | 'container': {'id': '987654'}, 39 | 'organization_contact': { 40 | 'first_name': 'William', 41 | 'last_name': 'Billson', 42 | 'email': 'bbillson@fakeco.biz', 43 | 'telephone': '2345556789', 44 | 'telephone_ext': '1001', 45 | 'job_title': 'Janitor', 46 | }, 47 | }, ], 48 | }, ) 49 | my_domain_response = (200, 'OK', { 50 | 'domains': 51 | [{ 52 | 'id': '239487', 53 | 'name': 'fakeco.biz', 54 | 'organization': {'id': '564738'}, 55 | 'container': {'id': '987654'}, 56 | }, ], 57 | }, ) 58 | v1order = CertificateOrder(host='localhost', 59 | customer_api_key='abc123', 60 | customer_name='12345', 61 | conn=MockConnection('localhost', 62 | responses={ 63 | '/clients/retail/api/?action=order_certificate': v1_order_created_response, 64 | })) 65 | v2order = CertificateOrder(host='localhost', 66 | customer_api_key='abc123', 67 | conn=MockConnection('localhost', 68 | responses={ 69 | '/services/v2/user/me': me_response, 70 | '/services/v2/organization?container_id=987654': my_org_response, 71 | '/services/v2/domain?container_id=987654': my_domain_response, 72 | '/services/v2/order/certificate/ssl_plus': v2_order_created_response, 73 | '/services/v2/order/certificate/ssl_multi_domain': v2_order_created_response, 74 | '/services/v2/order/certificate/ssl_wildcard': v2_order_created_response, 75 | })) 76 | 77 | requireds = \ 78 | { 79 | 'common_name': 'fakeco.biz', 80 | 'certificate_type': 'sslplus', 81 | 'validity': 3, 82 | 'org_name': 'FakeCo', 83 | 'org_addr1': '123 Nowhere Lane', 84 | 'org_city': 'Nowhere', 85 | 'org_state': 'UT', 86 | 'org_zip': '12345', 87 | 'org_country': 'US', 88 | 'org_contact_firstname': 'William', 89 | 'org_contact_lastname': 'Billson', 90 | 'org_contact_email': 'bbillson@fakeco.biz', 91 | 'org_contact_telephone': '2345556789', 92 | 'csr': '---CSR---', 93 | } 94 | optionals = dict(requireds.items() + 95 | { 96 | 'server_type': 2, 97 | 'org_unit': 'An Org', 98 | 'sans': ['www.fakeco.biz', 'login.fakeco.biz', 'api.fakeco.biz', 'intranet.fakeco.biz',], 99 | 'org_addr2': 'Infinitieth Floor', 100 | 'telephone': '2345556789', 101 | 'org_contact_job_title': 'Janitor', 102 | 'org_contact_telephone_ext': '1001', 103 | 'custom_expiration_date': '2015-11-20', 104 | 'comments': 'His shirt is too tight.', 105 | 'not_a_field': 'nothing', 106 | }.items()) 107 | 108 | def verify_response(self, response): 109 | self.assertEqual(201, response['http_status']) 110 | self.assertEqual('Created', response['http_reason']) 111 | self.assertEqual('OID-223344', response['id']) 112 | 113 | def test_place_v1_order_with_required_parameters(self): 114 | response = self.v1order.place(**self.requireds) 115 | self.verify_response(response) 116 | 117 | def test_place_v2_order_with_required_parameters(self): 118 | response = self.v2order.place(**self.requireds) 119 | self.verify_response(response) 120 | 121 | def test_place_v2_order_with_required_parameters_uc(self): 122 | self.requireds['certificate_type'] = 'sslmultidomain' 123 | response = self.v2order.place(**self.requireds) 124 | self.verify_response(response) 125 | 126 | def test_place_v2_order_with_required_parameters_wildcard(self): 127 | self.requireds['certificate_type'] = 'sslwildcard' 128 | self.requireds['sans'] = ['www.fakeco.biz', 'login.fakeco.biz', 'api.fakeco.biz', 'intranet.fakeco.biz'] 129 | d = dict(self.requireds) 130 | response = self.v2order.place(**d) 131 | self.verify_response(response) 132 | 133 | ## test v2 order with non-matching org and non-matching domain 134 | def test_place_v2_order_with_non_matching_org(self): 135 | d = dict(self.requireds) 136 | d['org_name'] = 'Another Co' 137 | response = self.v2order.place(**d) 138 | self.assertEqual(404, response['status']) 139 | self.assertEqual('Not Found', response['reason']) 140 | self.assertEqual('No matching organization found', response['response']) 141 | 142 | def test_place_v2_order_with_non_matching_domain(self): 143 | d = dict(self.requireds) 144 | d['common_name'] = 'w3.fakeco.biz' 145 | response = self.v2order.place(**d) 146 | self.assertEqual(404, response['status']) 147 | self.assertEqual('Not Found', response['reason']) 148 | self.assertEqual('No matching domain found', response['response']) 149 | 150 | def test_place_v1_order_with_optional_parameters(self): 151 | response = self.v1order.place(**self.optionals) 152 | self.verify_response(response) 153 | 154 | def test_place_v2_order_with_optional_parameters(self): 155 | response = self.v2order.place(**self.optionals) 156 | self.verify_response(response) 157 | 158 | def test_place_v1_order_with_missing_parameters(self): 159 | d = dict(self.requireds) 160 | del d['validity'] 161 | try: 162 | self.v1order.place(**d) 163 | self.fail('Expected exception but none thrown') 164 | except KeyError: 165 | pass 166 | 167 | def test_place_v2_order_with_missing_parameters(self): 168 | d = dict(self.requireds) 169 | del d['csr'] 170 | try: 171 | self.v2order.place(**d) 172 | self.fail('Expected exception but none thrown') 173 | except KeyError: 174 | pass 175 | 176 | 177 | if __name__ == '__main__': 178 | unittest.main() -------------------------------------------------------------------------------- /python/digicert_client/tests/TestOrderCertificateCommand.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import unittest 4 | import json 5 | from urlparse import parse_qs 6 | 7 | from .. import CertificateType, Validity 8 | from ..api.commands.v1 import OrderCertificateCommand as OrderCertificateCommandV1 9 | from ..api.commands.v2 import OrderCertificateCommand as OrderCertificateCommandV2 10 | 11 | 12 | class BaseTestOrderCertificateCommand(object): 13 | _customer_name = '12345' 14 | _customer_api_key = 'abapsdrtaewrh89249sbs89as0d' 15 | _api_host = 'localhost' 16 | _api_path = '/clients/retail/api/?action=order_certificate' 17 | _requireds_dict = { 18 | 'certificate_type': 'sslplus', 19 | 'csr': 'fakecsr', 20 | 'validity': '1', 21 | 'common_name': 'fakeco.biz', 22 | 'org_name': 'Fake Co', 23 | 'org_addr1': '123 Nowhere Lane', 24 | 'org_city': 'Nowhere', 25 | 'org_state': 'UT', 26 | 'org_zip': '12345', 27 | 'org_country': 'US', 28 | 'org_contact_firstname': 'William', 29 | 'org_contact_lastname': 'Billson', 30 | 'org_contact_email': 'bbillson@fakeco.biz', 31 | 'org_contact_telephone': '2345556789', 32 | } 33 | _optionals_dict = dict(_requireds_dict.items() + { 34 | 'org_addr2': 'Infinitieth Floor', 35 | 'telephone': '2345556789', 36 | 'org_contact_job_title': 'Janitor', 37 | 'org_contact_telephone_ext': '1001', 38 | 'server_type': '2', 39 | 'org_unit': 'FakeCo', 40 | 'sans': ['www.fakeco.biz', 'fake.co.uk']}.items()) 41 | 42 | def validate(self, o): 43 | raise NotImplementedError 44 | 45 | def validate_opt(self, o): 46 | self.assertEqual(o.server_type, 2) 47 | self.assertEqual(o.org_unit, 'FakeCo') 48 | self.assertEqual(o.sans, ['www.fakeco.biz', 'fake.co.uk']) 49 | self.assertEqual(o.org_addr2, 'Infinitieth Floor') 50 | self.assertEqual(o.telephone, '2345556789') 51 | self.assertEqual(o.org_contact_job_title, 'Janitor') 52 | self.assertEqual(o.org_contact_telephone_ext, '1001') 53 | 54 | def get_occ(self, additional_args): 55 | raise NotImplementedError 56 | 57 | def get_actuals(self): 58 | raise NotImplementedError 59 | 60 | def params_to_dict(self, params): 61 | raise NotImplementedError 62 | 63 | def verify_payload(self, actuals, params): 64 | raise NotImplementedError 65 | 66 | def test_get_headers(self): 67 | raise NotImplementedError 68 | 69 | def test_construct(self): 70 | self.validate(self.get_occ(self._requireds_dict)) 71 | 72 | def test_construct_optionals(self): 73 | occ = self.get_occ(self._optionals_dict) 74 | self.validate(occ) 75 | self.validate_opt(occ) 76 | 77 | def test_get_payload(self): 78 | actuals = self.get_actuals() 79 | occ = self.get_occ(self._requireds_dict) 80 | self.verify_payload(actuals, occ.get_params()) 81 | 82 | def test_get_payload_optionals(self): 83 | actuals = dict(self.get_actuals().items() + 84 | {'server_type': '2', 'org_unit': 'FakeCo', 'sans': "['www.fakeco.biz', 'fake.co.uk']", 85 | 'org_addr2': 'Infinitieth Floor', 'telephone': '2345556789', 86 | 'org_contact_job_title': 'Janitor', 'org_contact_telephone_ext': '1001'}.items()) 87 | occ = self.get_occ(self._optionals_dict) 88 | self.verify_payload(actuals, occ.get_params()) 89 | 90 | def test_enumerate_certificate_types(self): 91 | certtypes = [certtype for certtype in CertificateType()] 92 | self.assertNotEqual(-1, certtypes.index(CertificateType.SSLPLUS)) 93 | self.assertNotEqual(-1, certtypes.index(CertificateType.UC)) 94 | self.assertNotEqual(-1, certtypes.index(CertificateType.EVMULTI)) 95 | self.assertNotEqual(-1, certtypes.index(CertificateType.EVSSL)) 96 | self.assertNotEqual(-1, certtypes.index(CertificateType.WILDCARD)) 97 | 98 | def test_enumerate_validity(self): 99 | validities = [period for period in Validity()] 100 | self.assertNotEqual(-1, validities.index(Validity.ONE_YEAR)) 101 | self.assertNotEqual(-1, validities.index(Validity.TWO_YEARS)) 102 | self.assertNotEqual(-1, validities.index(Validity.THREE_YEARS)) 103 | 104 | 105 | class TestOrderCertificateCommandV1(BaseTestOrderCertificateCommand, unittest.TestCase): 106 | def get_occ(self, additional_args): 107 | return OrderCertificateCommandV1(customer_name=self._customer_name, 108 | customer_api_key=self._customer_api_key, 109 | **additional_args) 110 | 111 | def validate(self, o): 112 | self.assertEqual(o.customer_name, '12345') 113 | self.assertEqual(o.customer_api_key, 'abapsdrtaewrh89249sbs89as0d') 114 | self.assertEqual(o.certificate_type, 'sslplus') 115 | self.assertEqual(o.csr, 'fakecsr') 116 | self.assertEqual(o.validity, 1) 117 | self.assertEqual(o.common_name, 'fakeco.biz') 118 | self.assertEqual(o.org_name, 'Fake Co') 119 | self.assertEqual(o.org_addr1, '123 Nowhere Lane') 120 | self.assertEqual(o.org_city, 'Nowhere') 121 | self.assertEqual(o.org_state, 'UT') 122 | self.assertEqual(o.org_zip, '12345') 123 | self.assertEqual(o.org_country, 'US') 124 | self.assertEqual(o.org_contact_firstname, 'William') 125 | self.assertEqual(o.org_contact_lastname, 'Billson') 126 | self.assertEqual(o.org_contact_email, 'bbillson@fakeco.biz') 127 | self.assertEqual(o.org_contact_telephone, '2345556789') 128 | 129 | def test_get_auth(self): 130 | occ = self.get_occ(self._requireds_dict) 131 | self.assertEqual('MTIzNDU6YWJhcHNkcnRhZXdyaDg5MjQ5c2JzODlhczBk', occ.get_headers()['Authorization']) 132 | 133 | def get_actuals(self): 134 | return {'customer_api_key': self._customer_api_key, 'customer_name': self._customer_name, 135 | 'certificate_type': 'sslplus', 'csr': 'fakecsr', 'validity': '1', 'common_name': 'fakeco.biz', 136 | 'org_name': 'Fake Co', 'org_addr1': '123 Nowhere Lane', 'org_city': 'Nowhere', 137 | 'org_state': 'UT', 'org_zip': '12345', 'org_country': 'US', 138 | 'org_contact_firstname': 'William', 'org_contact_lastname': 'Billson', 139 | 'org_contact_email': 'bbillson@fakeco.biz', 'org_contact_telephone': '2345556789'} 140 | 141 | def params_to_dict(self, params): 142 | return parse_qs(params) 143 | 144 | def verify_payload(self, actuals, payload): 145 | self.assertTrue(len(payload) > 0) 146 | d = self.params_to_dict(payload) 147 | for k in d.keys(): 148 | self.assertEqual([actuals[k]], d[k], 'Nonmatching values for key %s' % k) 149 | self.assertEqual(len(actuals), len(d)) 150 | 151 | def test_get_headers(self): 152 | occ = self.get_occ(self._requireds_dict) 153 | self.assertEqual('application/x-www-form-urlencoded', occ.get_headers()['Content-Type']) 154 | self.assertEqual('application/json', occ.get_headers()['Accept']) 155 | 156 | 157 | class TestOrderCertificateCommandV2(BaseTestOrderCertificateCommand, unittest.TestCase): 158 | def get_occ(self, additional_args): 159 | additional_args['organization_id'] = '654321' 160 | return OrderCertificateCommandV2(customer_api_key=self._customer_api_key, 161 | **additional_args) 162 | 163 | def validate(self, o): 164 | self.assertEqual(o.customer_api_key, 'abapsdrtaewrh89249sbs89as0d') 165 | self.assertEqual(o.certificate_type, 'sslplus') 166 | self.assertEqual(o.csr, 'fakecsr') 167 | self.assertEqual(o.validity, 1) 168 | self.assertEqual(o.common_name, 'fakeco.biz') 169 | self.assertEqual(o.org_name, 'Fake Co') 170 | self.assertEqual(o.org_addr1, '123 Nowhere Lane') 171 | self.assertEqual(o.org_city, 'Nowhere') 172 | self.assertEqual(o.org_state, 'UT') 173 | self.assertEqual(o.org_zip, '12345') 174 | self.assertEqual(o.org_country, 'US') 175 | self.assertEqual(o.org_contact_firstname, 'William') 176 | self.assertEqual(o.org_contact_lastname, 'Billson') 177 | self.assertEqual(o.org_contact_email, 'bbillson@fakeco.biz') 178 | self.assertEqual(o.org_contact_telephone, '2345556789') 179 | self.assertEqual(o.organization_id, '654321') 180 | 181 | def test_get_auth(self): 182 | occ = self.get_occ(self._requireds_dict) 183 | self.assertEqual('abapsdrtaewrh89249sbs89as0d', occ.get_headers()['X-DC-DEVKEY']) 184 | 185 | def get_actuals(self): 186 | return {'customer_api_key': self._customer_api_key, 'organization_id': '654321', 187 | 'certificate_type': 'sslplus', 'csr': 'fakecsr', 'validity': 1, 'common_name': 'fakeco.biz', 188 | 'org_name': 'Fake Co', 'org_addr1': '123 Nowhere Lane', 'org_city': 'Nowhere', 189 | 'org_state': 'UT', 'org_zip': '12345', 'org_country': 'US', 190 | 'org_contact_firstname': 'William', 'org_contact_lastname': 'Billson', 191 | 'org_contact_email': 'bbillson@fakeco.biz', 'org_contact_telephone': '2345556789'} 192 | 193 | def params_to_dict(self, params): 194 | return json.loads(params) 195 | 196 | def verify_payload(self, actuals, payload): 197 | self.assertTrue(len(payload) > 0) 198 | d = json.loads(payload) 199 | actuals['certificate'] = { 200 | 'common_name': d['common_name'], 201 | 'csr': d['csr'], 202 | 'organization_units': [] if not 'organization_units' in d else d['organization_units'], 203 | 'server_platform': {'id': -1} if not 'server_platform' in d else {'id': d['server_platform']}, 204 | 'signature_hash': 'sha256' if not 'signature_hash' in d else d['signature_hash'], 205 | } 206 | actuals['organization'] = {'id': d['organization_id']} 207 | actuals['validity_years'] = '1' 208 | if 'server_type' in d: 209 | actuals['server_type'] = d['server_type'] 210 | 211 | for k in d.keys(): 212 | if k != 'sans': 213 | self.assertEqual(actuals[k], d[k], 'Nonmatching values for key %s' % k) 214 | if 'sans' in d: 215 | for san in d['sans']: 216 | self.assertTrue(san in actuals['sans']) 217 | self.assertEqual(len(actuals), len(d)) 218 | 219 | def test_get_headers(self): 220 | occ = self.get_occ(self._requireds_dict) 221 | self.assertEqual('application/json', occ.get_headers()['Content-Type']) 222 | self.assertEqual('application/json', occ.get_headers()['Accept']) 223 | 224 | 225 | if __name__ == '__main__': 226 | unittest.main() -------------------------------------------------------------------------------- /python/digicert_client/tests/TestPeerVerifier.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import unittest 4 | import ssl 5 | 6 | from ..https import verify_peer 7 | 8 | 9 | class TestPeerVerifier(unittest.TestCase): 10 | test_cert_info = { 11 | 'notAfter': 'Jan 01 12:00:00 2020 GMT', 12 | 'subjectAltName': ( 13 | ('DNS', 'www.example.com'), 14 | ('DNS', 'example.com'), 15 | ('DNS', 'www.subhost.example.com'), 16 | ('DNS', 'login.example.com') 17 | ), 18 | 'subject': ( 19 | (('businessCategory', u'Private Organization')), 20 | (('1.3.6.1.4.1.311.60.2.1.3', u'US')), 21 | (('1.3.6.1.4.1.311.60.2.1.2', u'Utah')), 22 | (('serialNumber', u'1213145-9876')), 23 | (('streetAddress', u'123 Nowhere Lane')), 24 | (('postalCode', u'84321')), 25 | (('countryName', u'US')), 26 | (('stateOrProvinceName', u'Utah')), 27 | (('localityName', u'Nowhere')), 28 | (('organizationName', u'Example, Inc.')), 29 | (('commonName', u'www.example.com')) 30 | ) 31 | } 32 | 33 | wildcard_cert_info = { 34 | 'notAfter': 'Jan 01 12:00:00 2020 GMT', 35 | 'subject': ( 36 | (('businessCategory', u'Private Organization')), 37 | (('1.3.6.1.4.1.311.60.2.1.3', u'US')), 38 | (('1.3.6.1.4.1.311.60.2.1.2', u'Utah')), 39 | (('serialNumber', u'1213145-9876')), 40 | (('streetAddress', u'123 Nowhere Lane')), 41 | (('postalCode', u'84321')), 42 | (('countryName', u'US')), 43 | (('stateOrProvinceName', u'Utah')), 44 | (('localityName', u'Nowhere')), 45 | (('organizationName', u'Example, Inc.')), 46 | (('commonName', u'*.example.com')) 47 | ) 48 | } 49 | 50 | partial_wildcard_cert_info = { 51 | 'notAfter': 'Jan 01 12:00:00 2020 GMT', 52 | 'subject': ( 53 | (('businessCategory', u'Private Organization')), 54 | (('1.3.6.1.4.1.311.60.2.1.3', u'US')), 55 | (('1.3.6.1.4.1.311.60.2.1.2', u'Utah')), 56 | (('serialNumber', u'1213145-9876')), 57 | (('streetAddress', u'123 Nowhere Lane')), 58 | (('postalCode', u'84321')), 59 | (('countryName', u'US')), 60 | (('stateOrProvinceName', u'Utah')), 61 | (('localityName', u'Nowhere')), 62 | (('organizationName', u'Example, Inc.')), 63 | (('commonName', u'log*.example.com')) 64 | ) 65 | } 66 | 67 | wildcard_san_cert_info = { 68 | 'notAfter': 'Jan 01 12:00:00 2020 GMT', 69 | 'subjectAltName': ( 70 | ('DNS', '*.example.com'), 71 | ), 72 | 'subject': ( 73 | (('businessCategory', u'Private Organization')), 74 | (('1.3.6.1.4.1.311.60.2.1.3', u'US')), 75 | (('1.3.6.1.4.1.311.60.2.1.2', u'Utah')), 76 | (('serialNumber', u'1213145-9876')), 77 | (('streetAddress', u'123 Nowhere Lane')), 78 | (('postalCode', u'84321')), 79 | (('countryName', u'US')), 80 | (('stateOrProvinceName', u'Utah')), 81 | (('localityName', u'Nowhere')), 82 | (('organizationName', u'Example, Inc.')), 83 | (('commonName', u'www.example.com')) 84 | ) 85 | } 86 | 87 | def test_common_name(self): 88 | try: 89 | verify_peer('www.example.com', self.test_cert_info) 90 | except ssl.SSLError as e: 91 | self.fail(e) 92 | 93 | def test_san(self): 94 | try: 95 | verify_peer('www.example.com', self.test_cert_info) 96 | verify_peer('example.com', self.test_cert_info) 97 | verify_peer('www.subhost.example.com', self.test_cert_info) 98 | verify_peer('login.example.com', self.test_cert_info) 99 | except ssl.SSLError as e: 100 | self.fail(e) 101 | 102 | def test_no_match(self): 103 | for host in ['ww.example.com', 'logout.example.com', 'www.exapmle.com', 'www.example.con']: 104 | try: 105 | verify_peer(host, self.test_cert_info) 106 | self.fail('Host "%s" matched' % host) 107 | except ssl.SSLError: 108 | pass 109 | 110 | def test_wildcard(self): 111 | try: 112 | verify_peer('www.example.com', self.wildcard_cert_info) 113 | verify_peer('login.example.com', self.wildcard_cert_info) 114 | except ssl.SSLError as e: 115 | self.fail(e) 116 | 117 | def test_wildcard_san(self): 118 | try: 119 | verify_peer('www.example.com', self.wildcard_san_cert_info) 120 | verify_peer('login.example.com', self.wildcard_san_cert_info) 121 | except ssl.SSLError as e: 122 | self.fail(e) 123 | 124 | def test_wildcard_dont_match_shorter_dns_name(self): 125 | try: 126 | verify_peer('example.com', self.wildcard_cert_info) 127 | self.fail('Wildcard "*.example.com" matched "example.com"') 128 | except ssl.SSLError: 129 | pass 130 | try: 131 | verify_peer('example.com', self.wildcard_san_cert_info) 132 | self.fail('Wildcard "*.example.com" matched "example.com"') 133 | except ssl.SSLError: 134 | pass 135 | 136 | def test_wildcard_dont_match_longer_dns_name(self): 137 | try: 138 | verify_peer('www.subhost.example.com', self.wildcard_cert_info) 139 | self.fail('Wildcard "*.example.com" matched "www.subhost.example.com"') 140 | except ssl.SSLError: 141 | pass 142 | try: 143 | verify_peer('www.subhost.example.com', self.wildcard_san_cert_info) 144 | self.fail('Wildcard "*.example.com" matched "www.subhost.example.com"') 145 | except ssl.SSLError: 146 | pass 147 | 148 | def test_wildcard_matches_partial_on_lhs_component(self): 149 | try: 150 | verify_peer('login.example.com', self.partial_wildcard_cert_info) 151 | verify_peer('logout.example.com', self.partial_wildcard_cert_info) 152 | except ssl.SSLError as e: 153 | self.fail(e) 154 | 155 | if __name__ == '__main__': 156 | unittest.main() -------------------------------------------------------------------------------- /python/digicert_client/tests/TestViewOrder.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from .. import CertificateOrder 4 | from . import MockConnection 5 | 6 | 7 | class TestViewOrder(unittest.TestCase): 8 | v1_view_order_response = (200, 'OK', { 9 | 'response': { 10 | 'result': 'success', 11 | 'return': { 12 | 'certificate_details': { 13 | 'order_id': 'OID-223344', 14 | 'status': 'issued', 15 | 'product_name': 'SSL Plus', 16 | 'validity': '1 Year(s)', 17 | 'org_unit': 'FakeCo', 18 | 'common_name': 'fakeco.biz', 19 | 'sans': [ 20 | 'www.fakeco.biz', 21 | 'www.fake.co.uk' 22 | ], 23 | 'order_date': '2014-08-18T18:16:07+00:00', 24 | 'valid_from': '2014-08-19T18:16:07+00:00', 25 | 'valid_till': '2015-08-19T18:16:07+00:00', 26 | 'server_type': 2, 27 | 'server_type_name': 'Apache' 28 | }, 29 | 'pending_reissue': { 30 | 'common_name': 'fakeco.biz', 31 | 'sans': [ 32 | 'api.fakeco.biz', 33 | 'intranet.fakeco.biz' 34 | ] 35 | } 36 | } 37 | } 38 | }) 39 | v2_view_order_response = (200, 'OK', { 40 | 'id': 'OID-223344', 41 | 'certificate': { 42 | 'id': 112358, 43 | 'thumbprint': '7D236B54D19D5EACF0881FAF24D51DFE5D23E945', 44 | 'serial_number': '0669D46CAE79EF684A69777490602485', 45 | 'common_name': 'fakeco.biz', 46 | 'dns_names': [ 47 | 'www.fakeco.biz', 48 | 'www.fake.co.uk', 49 | ], 50 | 'date_created': '2014-08-19T18:16:07+00:00', 51 | 'csr': '------fakecsr-----', 52 | 'organization': { 53 | 'id': 117483 54 | }, 55 | 'organization_units': [ 56 | 'FakeCo' 57 | ], 58 | 'server_platform': { 59 | 'id': 2, 60 | 'name': 'Apache', 61 | 'install_url': 'http:\/\/www.digicert.com\/ssl-certificate-installation-nginx.htm', 62 | 'csr_url': 'http:\/\/www.digicert.com\/csr-creation-nginx.htm' 63 | }, 64 | 'signature_hash': 'sha256', 65 | 'ca_cert': { 66 | 'id': 'f7slk4shv9s2wr3', 67 | 'name': 'DCert Private CA' 68 | } 69 | }, 70 | 'status': 'issued', 71 | 'date_created': '2014-08-19T18:16:07+00:00', 72 | 'organization': { 73 | 'name': 'Fake Co', 74 | 'city': 'Nowhere', 75 | 'state': 'UT', 76 | 'country': 'US' 77 | }, 78 | 'auto_renew': 10, 79 | 'container': { 80 | 'id': 5, 81 | 'name': 'A Container' 82 | }, 83 | 'product': { 84 | 'name_id': 'ssl_plus', 85 | 'name': 'SSL Plus', 86 | 'type': 'ssl_certificate' 87 | }, 88 | 'organization_contact': { 89 | 'first_name': 'William', 90 | 'last_name': 'Billson', 91 | 'email': 'bbillson@fakeco.biz', 92 | 'telephone': '2345556789' 93 | }, 94 | 'technical_contact': { 95 | 'first_name': 'William', 96 | 'last_name': 'Billson', 97 | 'email': 'bbillson@fakeco.biz', 98 | 'telephone': '2345556789' 99 | }, 100 | 'user': { 101 | 'id': 153208, 102 | 'first_name': 'Robert', 103 | 'last_name': 'Bobson', 104 | 'email': 'bbobson@fakeco.biz' 105 | }, 106 | 'requests': [{ 107 | 'id': 1 108 | }] 109 | }) 110 | v1order = CertificateOrder(host='localhost', 111 | customer_api_key='abc123', 112 | customer_name='12345', 113 | conn=MockConnection('localhost', 114 | responses={ 115 | '/clients/retail/api/?action=order_view_details': v1_view_order_response, 116 | })) 117 | v2order = CertificateOrder(host='localhost', 118 | customer_api_key='abc123', 119 | conn=MockConnection('localhost', 120 | responses={ 121 | '/services/v2/order/certificate/OID-223344': v2_view_order_response, 122 | })) 123 | order_kwargs = {'order_id': 'OID-223344'} 124 | 125 | def verify_response(self, response): 126 | self.assertEqual(200, response['http_status']) 127 | self.assertEqual('OK', response['http_reason']) 128 | self.assertEqual('fakeco.biz', response['certificate']['common_name']) 129 | self.assertEqual('2014-08-19T18:16:07+00:00', response['certificate']['date_created']) 130 | self.assertEqual('www.fakeco.biz', response['certificate']['dns_names'][0]) 131 | self.assertEqual('www.fake.co.uk', response['certificate']['dns_names'][1]) 132 | self.assertEqual('FakeCo', response['certificate']['organization_units'][0]) 133 | self.assertEqual(2, response['certificate']['server_platform']['id']) 134 | self.assertEqual('Apache', response['certificate']['server_platform']['name']) 135 | self.assertEqual('SSL Plus', response['product']['name']) 136 | 137 | def test_view_v1_order(self): 138 | response = self.v1order.view(digicert_order_id='OID-223344') 139 | self.verify_response(response) 140 | 141 | def test_view_v2_order(self): 142 | response = self.v2order.view(digicert_order_id='OID-223344') 143 | self.verify_response(response) 144 | 145 | def test_view_v1_order_with_order_in_kwargs(self): 146 | response = self.v1order.view(**self.order_kwargs) 147 | self.verify_response(response) 148 | 149 | def test_view_v2_order_with_order_in_kwargs(self): 150 | response = self.v2order.view(**self.order_kwargs) 151 | self.verify_response(response) 152 | 153 | def test_view_v1_order_without_order_id(self): 154 | d = {'not_order_id': 'hi'} 155 | try: 156 | self.v1order.view(**d) 157 | self.fail('Expected exception but none caught') 158 | except KeyError: 159 | pass 160 | 161 | def test_view_v2_order_without_order_id(self): 162 | d = {'not_order_id': 'hi'} 163 | try: 164 | self.v2order.view(**d) 165 | self.fail('Expected exception but none caught') 166 | except KeyError: 167 | pass 168 | 169 | 170 | if __name__ == '__main__': 171 | unittest.main() -------------------------------------------------------------------------------- /python/digicert_client/tests/TestViewOrderQuery.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import unittest 4 | 5 | from ..api.queries.v1 import ViewOrderDetailsQuery as ViewOrderDetailsQueryV1 6 | from ..api.queries.v2 import ViewOrderDetailsQuery as ViewOrderDetailsQueryV2 7 | 8 | 9 | class BaseTestViewOrderQuery(object): 10 | _customer_name = '12345' 11 | _customer_api_key = 'abapsdrtaewrh89249sbs89as0d' 12 | _api_host = 'www.digicert.com' 13 | _api_path = '/clients/retail/api/?action=order_view_details' 14 | 15 | def get_odq(self): 16 | raise NotImplementedError 17 | 18 | def verify_odq(self, odq): 19 | self.assertEqual(self._customer_api_key, odq.customer_api_key) 20 | self.assertEqual('567890', odq.order_id) 21 | 22 | def test_construct(self): 23 | odq = self.get_odq() 24 | self.verify_odq(odq) 25 | 26 | 27 | class TestViewOrderQueryV1(BaseTestViewOrderQuery, unittest.TestCase): 28 | def get_odq(self): 29 | return ViewOrderDetailsQueryV1(customer_name=self._customer_name, 30 | customer_api_key=self._customer_api_key, 31 | order_id='567890') 32 | 33 | def verify_odq(self, odq): 34 | super(TestViewOrderQueryV1, self).verify_odq(odq) 35 | self.assertEqual(self._customer_name, odq.customer_name) 36 | 37 | 38 | class TestViewOrderQueryV2(BaseTestViewOrderQuery, unittest.TestCase): 39 | def get_odq(self): 40 | return ViewOrderDetailsQueryV2(customer_api_key=self._customer_api_key, 41 | order_id='567890') 42 | 43 | 44 | if __name__ == '__main__': 45 | unittest.main() -------------------------------------------------------------------------------- /python/digicert_client/tests/VerifiedConnectionTestClient.py: -------------------------------------------------------------------------------- 1 | from ssl import SSLError 2 | from sys import exit 3 | 4 | from ..https import VerifiedHTTPSConnection 5 | 6 | 7 | def fail_and_exit(host, method, path, status): 8 | print 'Connection request failed: %s %s%s' % (method, host, path) 9 | print 'Response status: %d' % status 10 | exit(1) 11 | 12 | 13 | def test_conn(host, method, path, expect_status=200, expect_ssl_error=False, verify_no_text_match=None): 14 | try: 15 | conn = VerifiedHTTPSConnection(host=host) 16 | conn.request(method, path) 17 | response = conn.getresponse() 18 | if expect_ssl_error: 19 | raise RuntimeError('Got response status %d but expected SSLError', response.status) 20 | if expect_status != response.status: 21 | fail_and_exit(host, method, path, response.status) 22 | if verify_no_text_match: 23 | if -1 != response.read().find(verify_no_text_match): 24 | print 'Bad text "%s" found in response' % verify_no_text_match 25 | exit(1) 26 | except SSLError, ex: 27 | if not expect_ssl_error: 28 | print ex 29 | exit(1) 30 | 31 | 32 | if __name__ == '__main__': 33 | test_conn('www.digicert.com', 'GET', '/css/bv.css') 34 | test_conn('64.78.193.234', 'GET', '/css/bv.css', expect_ssl_error=True) 35 | test_conn('chain-demos.digicert.com', 'GET', '/') 36 | test_conn('ev-root.digicert.com', 'GET', '/', verify_no_text_match='This is not the SSL test site you are trying to visit, because your SSL client did not include a Server Name Indicator (SNI) extension in its SSL handshake.') 37 | -------------------------------------------------------------------------------- /python/digicert_client/tests/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import json 4 | 5 | 6 | class MockResponse: 7 | def __init__(self, status, reason, payload): 8 | self.status = status 9 | self.reason = reason 10 | self.payload = payload 11 | 12 | def read(self): 13 | return json.dumps(self.payload) 14 | 15 | 16 | class MockConnection: 17 | host = None 18 | responses = None 19 | method = None 20 | path = None 21 | params = None 22 | headers = None 23 | 24 | def __init__(self, host, responses=None): 25 | self.host = host 26 | self.responses = responses 27 | 28 | def request(self, method, path, params, headers): 29 | self.method = method 30 | self.path = path 31 | self.params = params 32 | self.headers = headers 33 | 34 | def getresponse(self): 35 | if self.responses and self.path in self.responses: 36 | status, reason, response = self.responses[self.path] 37 | else: 38 | status = 200 39 | reason = 'OK' 40 | response = {'response': {'result': 'success', 'return': {'order_id': 'OID-223344'}}} 41 | return MockResponse(status, reason, response) 42 | 43 | def close(self): 44 | pass 45 | 46 | 47 | if __name__ == '__main__': 48 | pass 49 | -------------------------------------------------------------------------------- /python/digicert_client/tests/test-tls.py: -------------------------------------------------------------------------------- 1 | __author__ = 'jfischer' 2 | 3 | import ssl 4 | import socket 5 | from sys import argv 6 | 7 | if __name__ == "__main__": 8 | """Simple tool to test for TLS connectivity. If TLS has been properly configured on the 9 | provided hostname, the phrase 'TLS negotiation successful' will be displayed; otherwise 10 | an exception will be thrown indicating a problem completing the handshake. 11 | """ 12 | 13 | if 2 > len(argv): 14 | print '%s usage:\n\t%s ' % (argv[0], argv[0]) 15 | else: 16 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 17 | ssl_sock = ssl.wrap_socket(sock, ssl_version=ssl.PROTOCOL_SSLv23) 18 | ssl_sock.connect((argv[1], 443)) 19 | print 'TLS negotiation successful' 20 | 21 | -------------------------------------------------------------------------------- /python/digicert_client/tests/test.py: -------------------------------------------------------------------------------- 1 | __author__ = 'jfischer' 2 | 3 | from digicert_client import CertificateOrder 4 | 5 | 6 | def list_duplicates(order_id): 7 | order = CertificateOrder(host='localhost.digicert.com', 8 | customer_api_key='CJYBV43GXF4JEXNCWKIDQ4H62DDOZJEU3IP4S44D6EZIL64R3E65YZHQX5BH6T62BIB25FXVGWR3ND4S6', 9 | customer_name='') 10 | return order.list_duplicates(digicert_order_id=order_id) 11 | 12 | 13 | def view_duplicate(order_id, sub_id): 14 | order = CertificateOrder(host='localhost.digicert.com', 15 | customer_api_key='CEVA53TWXN2I3HI3XEUXNPGHAWXUIRI3GEHY533KNCOZTOH3HBSCWMX4QZY4CPCG6PQ4WBGREPUZSI3BN') 16 | return order.download_duplicate(digicert_order_id=order_id, sub_id=sub_id) 17 | 18 | 19 | def create_duplicate(order_id, properties): 20 | order = CertificateOrder(host='localhost.digicert.com', 21 | customer_api_key='CJYBV43GXF4JEXNCWKIDQ4H62DDOZJEU3IP4S44D6EZIL64R3E65YZHQX5BH6T62BIB25FXVGWR3ND4S6') 22 | return order.create_duplicate(digicert_order_id=order_id, **properties) 23 | 24 | 25 | if __name__ == '__main__': 26 | # print list_duplicates('00687308') 27 | 28 | print view_duplicate('705658', '001') 29 | 30 | # properties = {"certificate":{"common_name":"test2.nocsr.com", "csr":"-----BEGIN CERTIFICATE REQUEST-----MIICnjCCAYYCAQAwWTELMAkGA1UEBhMCVVMxDTALBgNVBAgMBFV0YWgxDjAMBgNVBAcMBXByb3ZvMRUwEwYDVQQKDAxkaWdpY2VydCBpbmMxFDASBgNVBAMMC3lvdS5tYXguY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Y+FJGm4VrgVc1JUbVWiooM20TrqS+dmkabh80BPWFEfXUHbXeLAjXOgLworMnEFSLJiCSuZQndleHCUHW/3+hyDsSwhEmpdjUXHEqVhVEmSBLc478PMKGQNi2snpf3VSBdpNbnrADsZOfQxbkPnh7yy5yQYiLGv4ibFPT3rVKd2XzbHwz49mhAT8yF261/Kge7ES/N979554ftaLusSiN7Z1WKsmp/k1niA8b6AD2jmlfJ9FSwFd7yfcIexrNiXHNlp/qHq8vWs7jeknb2lrrLPejrwfLc4ZG6nwF6QUju69nC3ywjKfDM2UpDfqOYKL9jVM4kh2yKcNzS17DhaHQIDAQABoAAwDQYJKoZIhvcNAQEFBQADggEBAFJxj8AiDAL1Vda/N5eGNP3ZZ382Y8YwXcAlcvID8eQlUz7IjJLRgMRYURZBc9dsN04gyLDvGZapAvyQpQ0s8UHwTJpYhqEIuCOHJCcq4aVMHZFs/92r6I+tpo6VkpkyLR22tOPV+XJMKvYRoE1MZpP4suFpPRo+oCAQOl0i2/t+sHRzqig/JqRLC3DxypNmh3YnF3Q4W9jIoaNhmeMaeq815GMZj5hUFKHZdXdRGib2xi4i2Kv8gyExqrFw8B7WbYrlokC8ab+nWr+4VundLsetAq44TVoFZwty69i7RcXhpjzDpGqaF0CWIgj1YpjKvqXZtcTS8YabfcQVkaLXczQ=-----END CERTIFICATE request-----", "dns_names":["test2.nocsr.com"], "signature_hash":"sha256", "server_platform":{"id":-1}}} 31 | # result = create_duplicate('00687308', properties) 32 | # print result 33 | 34 | 35 | 36 | 37 | # { 38 | # u'certificates':[ 39 | # { 40 | # u'status':u'approved', 41 | # u'valid_till':u'2016-05-24', 42 | # u'valid_from':u'2015-05-20', 43 | # u'key_size':2048, 44 | # u'sub_id':u'001', 45 | # u'signature_hash':u'sha256', 46 | # u'server_platform':{ 47 | # u'csr_url': u'http://www.digicert.com/csr-creation.htm', 48 | # u'install_url': u'http://www.digicert.com/SSL-certificate-installation.htm', 49 | # u'id':u'-1', 50 | # u'name':u'OTHER' 51 | # }, 52 | # u'thumbprint':u'BBDAFC532FDAA4E42CFADE1C74759EA89621D68B', 53 | # u'date_created': u'2015-06-02T20:42:19+00:00 ', u' ca_cert_id':u'68', 54 | # u'dns_names':[ 55 | # u'*.nocsr.com', 56 | # u'nocsr.com' 57 | # ], 58 | # u'common_name':u'*.nocsr.com', 59 | # u'serial_number':u'0511CED6848DD49E02CBB8CE0D40C9E5', 60 | # u'csr':u'"""-----BEGIN CERTIFICATE REQUEST-----MIICnjCCAYYCAQAwWTELMAkGA1UEBhMCVVMxDTALBgNVBAgMBFV0YWgxDjAMBgNVBAcMBXByb3ZvMRUwEwYDVQQKDAxkaWdpY2VydCBpbmMxFDASBgNVBAMMC3lvdS5tYXguY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Y+FJGm4VrgVc1JUbVWiooM20TrqS+dmkabh80BPWFEfXUHbXeLAjXOgLworMnEFSLJiCSuZQndleHCUHW/3+hyDsSwhEmpdjUXHEqVhVEmSBLc478PMKGQNi2snpf3VSBdpNbnrADsZOfQxbkPnh7yy5yQYiLGv4ibFPT3rVKd2XzbHwz49mhAT8yF261/Kge7ES/N979554ftaLusSiN7Z1WKsmp/k1niA8b6AD2jmlfJ9FSwFd7yfcIexrNiXHNlp/qHq8vWs7jeknb2lrrLPejrwfLc4ZG6nwF6QUju69nC3ywjKfDM2UpDfqOYKL9jVM4kh2yKcNzS17DhaHQIDAQABoAAwDQYJKoZIhvcNAQEFBQADggEBAFJxj8AiDAL1Vda/N5eGNP3ZZ382Y8YwXcAlcvID8eQlUz7IjJLRgMRYURZBc9dsN04gyLDvGZapAvyQpQ0s8UHwTJpYhqEIuCOHJCcq4aVMHZFs/92r6I+tpo6VkpkyLR22tOPV+XJMKvYRoE1MZpP4suFpPRo+oCAQOl0i2/t+sHRzqig/JqRLC3DxypNmh3YnF3Q4W9jIoaNhmeMaeq815GMZj5hUFKHZdXdRGib2xi4i2Kv8gyExqrFw8B7WbYrlokC8ab+nWr+4VundLsetAq44TVoFZwty69i7RcXhpjzDpGqaF0CWIgj1YpjKvqXZtcTS8YabfcQVkaLXczQ=-----END CERTIFICATE yrequest-----"""' 61 | # } 62 | # ] 63 | # } 64 | # 65 | # 66 | # {u'certificates': [{u'status': u'approved', u'valid_till': u'2016-05-24', u'valid_from': u'2015-05-20', u'key_size': 2048, u'sub_id': u'001', u'signature_hash': u'sha256', u'server_platform': {u'csr_url': u'http://www.digicert.com/csr-creation.htm', u'install_url': u'http://www.digicert.com/SSL-certificate-installation.htm', u'id': u'-1', u'name': u'OTHER'}, u'thumbprint': u'BBDAFC532FDAA4E42CFADE1C74759EA89621D68B', u'date_created': u'2015-06-02T20:42:19+00:00', u'ca_cert_id': u'68', u'dns_names': [u'*.nocsr.com', u'nocsr.com'], u'common_name': u'*.nocsr.com', u'serial_number': u'0511CED6848DD49E02CBB8CE0D40C9E5', u'csr': u'"""-----BEGIN CERTIFICATE REQUEST-----MIICnjCCAYYCAQAwWTELMAkGA1UEBhMCVVMxDTALBgNVBAgMBFV0YWgxDjAMBgNVBAcMBXByb3ZvMRUwEwYDVQQKDAxkaWdpY2VydCBpbmMxFDASBgNVBAMMC3lvdS5tYXguY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Y+FJGm4VrgVc1JUbVWiooM20TrqS+dmkabh80BPWFEfXUHbXeLAjXOgLworMnEFSLJiCSuZQndleHCUHW/3+hyDsSwhEmpdjUXHEqVhVEmSBLc478PMKGQNi2snpf3VSBdpNbnrADsZOfQxbkPnh7yy5yQYiLGv4ibFPT3rVKd2XzbHwz49mhAT8yF261/Kge7ES/N979554ftaLusSiN7Z1WKsmp/k1niA8b6AD2jmlfJ9FSwFd7yfcIexrNiXHNlp/qHq8vWs7jeknb2lrrLPejrwfLc4ZG6nwF6QUju69nC3ywjKfDM2UpDfqOYKL9jVM4kh2yKcNzS17DhaHQIDAQABoAAwDQYJKoZIhvcNAQEFBQADggEBAFJxj8AiDAL1Vda/N5eGNP3ZZ382Y8YwXcAlcvID8eQlUz7IjJLRgMRYURZBc9dsN04gyLDvGZapAvyQpQ0s8UHwTJpYhqEIuCOHJCcq4aVMHZFs/92r6I+tpo6VkpkyLR22tOPV+XJMKvYRoE1MZpP4suFpPRo+oCAQOl0i2/t+sHRzqig/JqRLC3DxypNmh3YnF3Q4W9jIoaNhmeMaeq815GMZj5hUFKHZdXdRGib2xi4i2Kv8gyExqrFw8B7WbYrlokC8ab+nWr+4VundLsetAq44TVoFZwty69i7RcXhpjzDpGqaF0CWIgj1YpjKvqXZtcTS8YabfcQVkaLXczQ=-----END CERTIFICATE yrequest-----"""'}]} 67 | # None 68 | # None -------------------------------------------------------------------------------- /python/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | 4 | def readme(): 5 | with open('README.rst') as f: 6 | return f.read() 7 | 8 | setup( 9 | name='digicert_client', 10 | version='1.1.03', 11 | description='DigiCert, Inc. web API client library', 12 | long_description=readme(), 13 | classifiers=[ 14 | 'Development Status :: 5 - Production/Stable', 15 | 'Intended Audience :: Information Technology', 16 | 'License :: OSI Approved :: MIT License', 17 | 'Programming Language :: Python :: 2.7', 18 | 'Topic :: Security', 19 | ], 20 | url='https://github.com/digicert/digicert_client/python', 21 | author='DigiCert, Inc.', 22 | author_email='support@digicert.com', 23 | license='MIT', 24 | zip_safe=False, 25 | packages=find_packages(exclude=['tests.*', '*.tests.*', '*.tests', 'tests']), 26 | include_package_data=True, 27 | test_suite='nose.collector', 28 | tests_require=['nose'], 29 | ) 30 | 31 | --------------------------------------------------------------------------------