certapi 0.4.1__tar.gz → 0.4.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. {certapi-0.4.1 → certapi-0.4.2}/PKG-INFO +1 -1
  2. {certapi-0.4.1 → certapi-0.4.2}/setup.py +1 -1
  3. certapi-0.4.2/src/certapi/__init__.py +23 -0
  4. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/acme/Acme.py +3 -2
  5. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/acme/Challenge.py +12 -15
  6. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/acme/Order.py +3 -2
  7. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/acme/__init__.py +1 -1
  8. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/acme/http.py +1 -2
  9. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/challenge_store/ChallengeStore.py +3 -1
  10. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/challenge_store/__init__.py +2 -3
  11. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/challenge_store/dns/__init__.py +1 -1
  12. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/challenge_store/dns/cloudflare/cloudflare_challenge_store.py +4 -4
  13. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/challenge_store/dns/cloudflare/cloudflare_client.py +1 -1
  14. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/challenge_store/dns/digitalocean/digitalocean_challenge_store.py +1 -1
  15. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/challenge_store/dns/digitalocean/digitalocean_client.py +1 -1
  16. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/client/cert_manager_client.py +29 -11
  17. certapi-0.4.2/src/certapi/crypto/__init__.py +2 -0
  18. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/crypto/crypto.py +4 -3
  19. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/crypto/crypto_classes.py +5 -5
  20. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/http/types.py +13 -6
  21. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/issuers/AcmeCertIssuer.py +28 -17
  22. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/issuers/SelfCertIssuer.py +3 -2
  23. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/issuers/__init__.py +1 -1
  24. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/issuers/abstract_certissuer.py +22 -14
  25. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/keystore/FileSystemKeystore.py +7 -7
  26. certapi-0.4.2/src/certapi/keystore/KeyStore.py +71 -0
  27. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/keystore/PostgresqlKeyStore.py +12 -5
  28. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/keystore/RemoteKeyStore.py +7 -3
  29. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/keystore/SqliteKeyStore.py +7 -4
  30. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/keystore/__init__.py +1 -1
  31. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/manager/acme_cert_manager.py +35 -29
  32. certapi-0.4.2/src/certapi/server/api.py +103 -0
  33. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/server/cert_api.py +48 -36
  34. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/server/key_api.py +25 -28
  35. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/utils.py +0 -1
  36. {certapi-0.4.1 → certapi-0.4.2}/src/certapi.egg-info/PKG-INFO +1 -1
  37. {certapi-0.4.1 → certapi-0.4.2}/src/certapi.egg-info/SOURCES.txt +0 -1
  38. certapi-0.4.1/src/certapi/__init__.py +0 -9
  39. certapi-0.4.1/src/certapi/challenge_store/FilesystemChallengeStore.py +0 -38
  40. certapi-0.4.1/src/certapi/crypto/__init__.py +0 -2
  41. certapi-0.4.1/src/certapi/keystore/KeyStore.py +0 -66
  42. certapi-0.4.1/src/certapi/server/api.py +0 -89
  43. {certapi-0.4.1 → certapi-0.4.2}/MANIFEST.in +0 -0
  44. {certapi-0.4.1 → certapi-0.4.2}/README.md +0 -0
  45. {certapi-0.4.1 → certapi-0.4.2}/pyproject.toml +0 -0
  46. {certapi-0.4.1 → certapi-0.4.2}/setup.cfg +0 -0
  47. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/acme/AcmeError.py +0 -0
  48. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/challenge_store/FileSystemChallengeStore.py +1 -1
  49. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/challenge_store/InmemoryChallengeStore.py +0 -0
  50. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/challenge_store/dns/cloudflare/__init__.py +0 -0
  51. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/challenge_store/dns/digitalocean/__init__.py +0 -0
  52. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/client/__init__.py +0 -0
  53. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/http/__init__.py +0 -0
  54. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/http/client.py +0 -0
  55. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/manager/__init__.py +0 -0
  56. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/server/__init__.py +0 -0
  57. {certapi-0.4.1 → certapi-0.4.2}/src/certapi/util.py +0 -0
  58. {certapi-0.4.1 → certapi-0.4.2}/src/certapi.egg-info/dependency_links.txt +0 -0
  59. {certapi-0.4.1 → certapi-0.4.2}/src/certapi.egg-info/requires.txt +0 -0
  60. {certapi-0.4.1 → certapi-0.4.2}/src/certapi.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: certapi
3
- Version: 0.4.1
3
+ Version: 0.4.2
4
4
  Summary: Python Package for managing keys, request SSL certificates from ACME.
5
5
  Home-page: https://github.com/mesudip/certapi
6
6
  Author: Sudip Bhattarai
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="certapi",
5
- version="0.4.1",
5
+ version="0.4.2",
6
6
  packages=find_packages(where="src"),
7
7
  package_dir={"": "src"},
8
8
  install_requires=[
@@ -0,0 +1,23 @@
1
+ from .acme.Acme import Acme, Order, AcmeNetworkError, AcmeHttpError, Challenge
2
+ from .manager.acme_cert_manager import AcmeCertManager
3
+ from .http.types import CertificateResponse, IssuedCert
4
+
5
+ from .crypto import (
6
+ Certificate,
7
+ CertificateSigningRequest,
8
+ CertificateSigningRequestBuilder,
9
+ Key,
10
+ Ed25519Key,
11
+ ECDSAKey,
12
+ Ed25519PrivateKey,
13
+ EllipticCurvePrivateKey,
14
+ )
15
+ from .keystore import FileSystemKeystore, SqliteKeyStore, PostgresKeyStore, KeyStore
16
+ from .challenge_store import (
17
+ ChallengeStore,
18
+ InMemoryChallengeStore,
19
+ FileSystemChallengeStore,
20
+ CloudflareChallengeStore,
21
+ DigitalOceanChallengeStore,
22
+ )
23
+ from .issuers import SelfCertIssuer
@@ -9,13 +9,14 @@ from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
9
9
  from cryptography.x509 import CertificateSigningRequest, Certificate
10
10
  from certapi.crypto import crypto
11
11
  import requests
12
- from certapi.crypto import sign, digest_sha256, csr_to_der, jwk, get_algorithm_name, sign_for_jws,Key
12
+ from certapi.crypto import sign, digest_sha256, csr_to_der, jwk, get_algorithm_name, sign_for_jws, Key
13
13
  from certapi.util import b64_encode, b64_string
14
14
  from .AcmeError import *
15
15
  from .http import *
16
16
  from .Challenge import Challenge
17
17
  from .Order import Order
18
18
 
19
+
19
20
  class Acme:
20
21
 
21
22
  URL_STAGING = "https://acme-staging-v02.api.letsencrypt.org/directory"
@@ -23,7 +24,7 @@ class Acme:
23
24
 
24
25
  def __init__(self, account_key: Union[RSAPrivateKey, EllipticCurvePrivateKey | Key], url=URL_STAGING):
25
26
  self.account_key = account_key.key if isinstance(account_key, Key) else account_key
26
-
27
+
27
28
  # json web key format for public key
28
29
  self.jwk = jwk(self.account_key)
29
30
  print(self.jwk)
@@ -1,4 +1,3 @@
1
-
2
1
  import json
3
2
  from typing import Literal, Union
4
3
 
@@ -7,7 +6,8 @@ import requests
7
6
  from certapi.util import b64_encode, b64_string
8
7
  from certapi.crypto import digest_sha256
9
8
  from .AcmeError import *
10
- from .http import post,get
9
+ from .http import post, get
10
+
11
11
 
12
12
  class Challenge:
13
13
  def __init__(self, auth_url, data, acme):
@@ -15,7 +15,7 @@ class Challenge:
15
15
  self._acme = acme
16
16
  self._data = data
17
17
  challenge = self.get_challenge()
18
- self.token:str = challenge["token"]
18
+ self.token: str = challenge["token"]
19
19
  self.verified = challenge["status"] == "valid"
20
20
  self.domain = data["identifier"]["value"] # Add domain attribute
21
21
 
@@ -25,24 +25,21 @@ class Challenge:
25
25
 
26
26
  self.url = "http://{0}/.well-known/acme-challenge/{1}".format(data["identifier"]["value"], self.token)
27
27
 
28
- def as_key_value(self,type:Literal["http-01", "dns-01", "tls-alpn-01"]=None):
29
- challenge=self.get_challenge(type)
28
+ def as_key_value(self, type: Literal["http-01", "dns-01", "tls-alpn-01"] = None):
29
+ challenge = self.get_challenge(type)
30
30
 
31
31
  if challenge["type"] == "dns-01":
32
32
  key = f"_acme-challenge.{self.domain}"
33
33
  value = b64_string(digest_sha256(self.authorization_key.encode("utf8")))
34
34
  else:
35
- key = self.token
36
- value = self.authorization_key
37
-
38
- return (key,value)
35
+ key = self.token
36
+ value = self.authorization_key
39
37
 
38
+ return (key, value)
40
39
 
41
- def verify(self, type:Literal["http-01", "dns-01", "tls-alpn-01"]=None) -> bool:
40
+ def verify(self, type: Literal["http-01", "dns-01", "tls-alpn-01"] = None) -> bool:
42
41
  if not self.verified:
43
- response = self._acme._signed_req(
44
- self.get_challenge(type)["url"], {}, step="Verify Challenge", throw=False
45
- )
42
+ response = self._acme._signed_req(self.get_challenge(type)["url"], {}, step="Verify Challenge", throw=False)
46
43
  if response.status_code == 200 and response.json()["status"] == "valid":
47
44
  self.verified = True
48
45
  return True
@@ -73,9 +70,9 @@ class Challenge:
73
70
  else:
74
71
  return False
75
72
 
76
- def get_challenge(self, type:Literal["http-01", "dns-01", "tls-alpn-01"]=None):
73
+ def get_challenge(self, type: Literal["http-01", "dns-01", "tls-alpn-01"] = None):
77
74
  challenges = self._data["challenges"]
78
- def_key= type if type is not None else "http-01"
75
+ def_key = type if type is not None else "http-01"
79
76
  for method in challenges:
80
77
  if method["type"] == def_key:
81
78
  return method
@@ -1,10 +1,11 @@
1
1
  from typing import List, Tuple
2
- from certapi.crypto import crypto,csr_to_der
2
+ from certapi.crypto import crypto, csr_to_der
3
3
  from certapi.util import b64_string
4
- from cryptography.x509 import CertificateSigningRequest,Certificate
4
+ from cryptography.x509 import CertificateSigningRequest, Certificate
5
5
  from .Challenge import *
6
6
  from .http import get
7
7
 
8
+
8
9
  class Order:
9
10
  def __init__(self, url, data, challenges, acme):
10
11
  self.url = url
@@ -1,4 +1,4 @@
1
1
  from .Challenge import Challenge
2
2
  from .AcmeError import *
3
3
  from .Acme import Acme
4
- from .Order import Order
4
+ from .Order import Order
@@ -1,7 +1,7 @@
1
-
2
1
  import requests
3
2
  from .AcmeError import *
4
3
 
4
+
5
5
  def request(method, step: str, url: str, json=None, headers=None, throw=True) -> requests.Response:
6
6
  res = None
7
7
  try:
@@ -53,4 +53,3 @@ def post(step: str, url: str, json=None, headers=None, throw=True) -> requests.R
53
53
 
54
54
  def get(step: str, url) -> requests.Response:
55
55
  return request("GET", step, url)
56
-
@@ -7,6 +7,7 @@ class ChallengeStore(ABC):
7
7
  """
8
8
  Abstract base class for a challenge store.
9
9
  """
10
+
10
11
  @abstractmethod
11
12
  def supports_domain(self, domain: str) -> bool:
12
13
  pass
@@ -22,6 +23,7 @@ class ChallengeStore(ABC):
22
23
  @abstractmethod
23
24
  def delete_challenge(self, key: str, domain: str = None):
24
25
  pass
26
+
25
27
  def store_details():
26
28
  return {
27
29
  "name": "Abstract Challenge Store",
@@ -31,4 +33,4 @@ class ChallengeStore(ABC):
31
33
  raise NotImplementedError("Must implement `__iter__` method.")
32
34
 
33
35
  def __len__(self):
34
- raise NotImplementedError("Must implement `__len__` method.")
36
+ raise NotImplementedError("Must implement `__len__` method.")
@@ -2,7 +2,8 @@ from .ChallengeStore import ChallengeStore
2
2
  from .InmemoryChallengeStore import InMemoryChallengeStore
3
3
  import os
4
4
  from .FileSystemChallengeStore import FileSystemChallengeStore
5
- from .dns import CloudflareChallengeStore,DigitalOceanChallengeStore
5
+ from .dns import CloudflareChallengeStore, DigitalOceanChallengeStore
6
+
6
7
 
7
8
  def get_challenge_store():
8
9
  """
@@ -21,5 +22,3 @@ def get_challenge_store():
21
22
  return FileSystemChallengeStore(directory)
22
23
  else:
23
24
  raise ValueError(f"Unknown CHALLENGE_STORE_TYPE: {store_type}")
24
-
25
-
@@ -1,2 +1,2 @@
1
1
  from .digitalocean.digitalocean_challenge_store import DigitalOceanChallengeStore
2
- from .cloudflare.cloudflare_challenge_store import CloudflareChallengeStore
2
+ from .cloudflare.cloudflare_challenge_store import CloudflareChallengeStore
@@ -6,10 +6,10 @@ from .cloudflare_client import Cloudflare
6
6
 
7
7
 
8
8
  class CloudflareChallengeStore(ChallengeStore):
9
- def __init__(self,api_key:str=None):
9
+ def __init__(self, api_key: str = None):
10
10
  self.cloudflare = Cloudflare(api_key)
11
- self.challenges_map = {}
12
-
11
+ self.challenges_map = {}
12
+
13
13
  def supports_domain(self, domain: str) -> bool:
14
14
  """
15
15
  Checks if the Cloudflare account has access to the given domain (or its base domain)
@@ -29,7 +29,7 @@ class CloudflareChallengeStore(ChallengeStore):
29
29
  record_id = self.cloudflare.create_record(name=key, data=value, domain=base_domain)
30
30
  self.challenges_map[key] = record_id
31
31
  print(f"CloudflareChallengeStore: Saved challenge for {key} with record ID {record_id}")
32
- time.sleep(10) # wait for dns propagation. it may not be immediate
32
+ time.sleep(10) # wait for dns propagation. it may not be immediate
33
33
 
34
34
  def get_challenge(self, key: str, domain: str) -> str:
35
35
  base_domain = self.cloudflare.determine_registered_domain(domain)
@@ -7,7 +7,7 @@ from urllib.request import urlopen, Request
7
7
  class Cloudflare(object):
8
8
  name = "cloudflare"
9
9
 
10
- def __init__(self,api_key:str):
10
+ def __init__(self, api_key: str):
11
11
  self.token = api_key
12
12
  self.api = "https://api.cloudflare.com/client/v4"
13
13
  if not self.token:
@@ -5,7 +5,7 @@ from .digitalocean_client import DigitalOcean
5
5
 
6
6
 
7
7
  class DigitalOceanChallengeStore(ChallengeStore):
8
- def __init__(self,api_key:str=None):
8
+ def __init__(self, api_key: str = None):
9
9
  self.digitalocean = DigitalOcean(api_key)
10
10
  self.challenges_map = {} # Stores key: record_id (needed for deletion)
11
11
 
@@ -5,7 +5,7 @@ from urllib.request import urlopen, Request # Python 3
5
5
 
6
6
 
7
7
  class DigitalOcean(object):
8
- def __init__(self, api_key:str=None):
8
+ def __init__(self, api_key: str = None):
9
9
  self.token = api_key
10
10
  self.api = "https://api.digitalocean.com/v2/domains"
11
11
  if not self.token:
@@ -2,6 +2,7 @@ import requests
2
2
  from typing import List, Union, Dict, Optional, Any
3
3
  from certapi.http.types import CertificateResponse, IssuedCert
4
4
 
5
+
5
6
  class CertManagerClient:
6
7
  def __init__(self, base_url: str):
7
8
  self.base_url = base_url
@@ -11,8 +12,15 @@ class CertManagerClient:
11
12
  response.raise_for_status()
12
13
  return response.json()
13
14
 
14
- def _post(self, path: str, data: Optional[Union[Dict[str, Any], str]] = None, headers: Optional[Dict[str, str]] = None) -> Any:
15
- response = requests.post(f"{self.base_url}{path}", json=data if isinstance(data, dict) else None, data=data if isinstance(data, str) else None, headers=headers)
15
+ def _post(
16
+ self, path: str, data: Optional[Union[Dict[str, Any], str]] = None, headers: Optional[Dict[str, str]] = None
17
+ ) -> Any:
18
+ response = requests.post(
19
+ f"{self.base_url}{path}",
20
+ json=data if isinstance(data, dict) else None,
21
+ data=data if isinstance(data, str) else None,
22
+ headers=headers,
23
+ )
16
24
  response.raise_for_status()
17
25
  return response.json()
18
26
 
@@ -32,17 +40,24 @@ class CertManagerClient:
32
40
  "key_type": key_type,
33
41
  "expiry_days": expiry_days,
34
42
  }
35
- if country: params["country"] = country
36
- if state: params["state"] = state
37
- if locality: params["locality"] = locality
38
- if organization: params["organization"] = organization
39
- if user_id: params["user_id"] = user_id
43
+ if country:
44
+ params["country"] = country
45
+ if state:
46
+ params["state"] = state
47
+ if locality:
48
+ params["locality"] = locality
49
+ if organization:
50
+ params["organization"] = organization
51
+ if user_id:
52
+ params["user_id"] = user_id
40
53
 
41
54
  data = self._get("/obtain", params=params)
42
55
  return CertificateResponse.from_json(data)
43
56
 
44
57
  def sign_csr(self, csr_pem: str) -> str:
45
- response = requests.post(f"{self.base_url}/sign_csr", data=csr_pem, headers={'Content-Type': 'application/x-pem-file'})
58
+ response = requests.post(
59
+ f"{self.base_url}/sign_csr", data=csr_pem, headers={"Content-Type": "application/x-pem-file"}
60
+ )
46
61
  response.raise_for_status()
47
62
  return response.text
48
63
 
@@ -88,11 +103,14 @@ class CertManagerClient:
88
103
  return None
89
104
  raise
90
105
 
91
- def save_cert(self, private_key_id: Union[int, str], cert_pem: str, domains: List[str], name: Optional[str] = None) -> Dict[str, Any]:
106
+ def save_cert(
107
+ self, private_key_id: Union[int, str], cert_pem: str, domains: List[str], name: Optional[str] = None
108
+ ) -> Dict[str, Any]:
92
109
  data = {
93
110
  "private_key_id": private_key_id,
94
111
  "cert": cert_pem,
95
112
  "domains": domains,
96
113
  }
97
- if name: data["name"] = name
98
- return self._post("/certs", data=data)
114
+ if name:
115
+ data["name"] = name
116
+ return self._post("/certs", data=data)
@@ -0,0 +1,2 @@
1
+ from .crypto import *
2
+ from .crypto_classes import *
@@ -10,11 +10,11 @@ from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
10
10
  from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
11
11
  from cryptography.x509 import Certificate, CertificateSigningRequestBuilder, CertificateSigningRequest
12
12
 
13
- from cryptography.x509.oid import NameOID,ExtensionOID
13
+ from cryptography.x509.oid import NameOID, ExtensionOID
14
14
  from cryptography.hazmat.primitives import hashes
15
15
 
16
16
 
17
- from certapi.util import b64_string
17
+ from certapi.util import b64_string
18
18
 
19
19
  __no_enc = serialization.NoEncryption()
20
20
 
@@ -177,6 +177,7 @@ def digest_sha256(data: bytes) -> bytes:
177
177
  h.update(data)
178
178
  return h.finalize()
179
179
 
180
+
180
181
  def get_csr_hostnames(csr: x509.CertificateSigningRequest):
181
182
 
182
183
  domains = []
@@ -203,4 +204,4 @@ def get_csr_hostnames(csr: x509.CertificateSigningRequest):
203
204
  seen.add(d)
204
205
  unique_domains.append(d)
205
206
 
206
- return unique_domains
207
+ return unique_domains
@@ -18,11 +18,11 @@ class Key(ABC):
18
18
  pass
19
19
 
20
20
  @abstractmethod
21
- def sign(self, message:bytes):
21
+ def sign(self, message: bytes):
22
22
  pass
23
23
 
24
24
  @abstractmethod
25
- def sign_csr(self, csr:x509.CertificateSigningRequestBuilder|x509.CertificateBuilder):
25
+ def sign_csr(self, csr: x509.CertificateSigningRequestBuilder | x509.CertificateBuilder):
26
26
  pass
27
27
 
28
28
  @staticmethod
@@ -37,7 +37,7 @@ class Key(ABC):
37
37
  raise ValueError("Unsupported key type. Use 'rsa' or 'ecdsa'")
38
38
 
39
39
  @staticmethod
40
- def from_der(der_bytes:bytes,password:str=None):
40
+ def from_der(der_bytes: bytes, password: str = None):
41
41
  key = serialization.load_der_private_key(der_bytes, password)
42
42
  if isinstance(key, rsa.RSAPrivateKey):
43
43
  return RSAKey(key)
@@ -49,7 +49,7 @@ class Key(ABC):
49
49
  raise ValueError("Unsupported key type")
50
50
 
51
51
  @staticmethod
52
- def from_pem(pem_str:str,password:str=None):
52
+ def from_pem(pem_str: str, password: str = None):
53
53
  key = serialization.load_pem_private_key(pem_str, password)
54
54
  if isinstance(key, rsa.RSAPrivateKey):
55
55
  return RSAKey(key)
@@ -127,7 +127,7 @@ class Key(ABC):
127
127
  csr_builder = x509.CertificateSigningRequestBuilder()
128
128
  if domain:
129
129
  subject = self._build_name(subject_fields, include_user_id=True, domain=domain)
130
- csr_builder=csr_builder.subject_name(subject)
130
+ csr_builder = csr_builder.subject_name(subject)
131
131
 
132
132
  # Always include the domain in SAN, and then add alt_names
133
133
  all_alt_names = [domain] + list(alt_names)
@@ -4,8 +4,15 @@ from cryptography.x509 import Certificate
4
4
  from ..crypto.crypto import cert_to_pem, certs_to_pem
5
5
  from ..crypto.crypto_classes import Key
6
6
 
7
+
7
8
  class IssuedCert:
8
- def __init__(self, *, key: Union[str, Key] = None, cert: Union[str, Certificate, List[Certificate]] = None, domains: List[str] = None):
9
+ def __init__(
10
+ self,
11
+ *,
12
+ key: Union[str, Key] = None,
13
+ cert: Union[str, Certificate, List[Certificate]] = None,
14
+ domains: List[str] = None,
15
+ ):
9
16
  if isinstance(key, Key):
10
17
  key = key.to_pem().decode("utf-8")
11
18
  elif isinstance(key, bytes):
@@ -25,9 +32,9 @@ class IssuedCert:
25
32
  @staticmethod
26
33
  def from_json(data: Dict[str, Any]) -> "IssuedCert":
27
34
  return IssuedCert(
28
- key=data.get('privateKey'),
29
- cert=data.get('certificate'),
30
- domains=data.get('domains', []),
35
+ key=data.get("privateKey"),
36
+ cert=data.get("certificate"),
37
+ domains=data.get("domains", []),
31
38
  )
32
39
 
33
40
  def __repr__(self):
@@ -48,8 +55,8 @@ class CertificateResponse:
48
55
  @staticmethod
49
56
  def from_json(data: Dict[str, Any]) -> "CertificateResponse":
50
57
  return CertificateResponse(
51
- existing=[IssuedCert.from_json(cert_data) for cert_data in data.get('existing', [])],
52
- issued=[IssuedCert.from_json(cert_data) for cert_data in data.get('issued', [])]
58
+ existing=[IssuedCert.from_json(cert_data) for cert_data in data.get("existing", [])],
59
+ issued=[IssuedCert.from_json(cert_data) for cert_data in data.get("issued", [])],
53
60
  )
54
61
 
55
62
  def __repr__(self):
@@ -1,11 +1,11 @@
1
- from certapi.crypto import ECDSAKey, Ed25519Key, RSAKey,Key
1
+ from certapi.crypto import ECDSAKey, Ed25519Key, RSAKey, Key
2
2
  from .abstract_certissuer import CertIssuer
3
3
  from cryptography import x509
4
4
  from cryptography.x509 import Certificate
5
5
  from typing import List, Literal, Union, Callable, Tuple, Dict, Optional
6
6
  import time
7
7
  from requests import Response
8
- from certapi.acme import Acme,Challenge, Order
8
+ from certapi.acme import Acme, Challenge, Order
9
9
  from certapi.challenge_store import ChallengeStore
10
10
 
11
11
 
@@ -15,7 +15,7 @@ class AcmeCertIssuer(CertIssuer):
15
15
  account_key: Key,
16
16
  challenge_store: ChallengeStore,
17
17
  acme_url=None,
18
- self_verify_challenge=False, # This never needs to be set to True
18
+ self_verify_challenge=False, # This never needs to be set to True
19
19
  ):
20
20
  self.acme = Acme(account_key, url=acme_url)
21
21
  self.challenge_store = challenge_store
@@ -29,7 +29,9 @@ class AcmeCertIssuer(CertIssuer):
29
29
  elif res.status_code != 200:
30
30
  raise Exception("Acme registration didn't return 200 or 201 ", res.json())
31
31
 
32
- def sign_csr(self, csr: x509.CertificateSigningRequest,challenge_store:ChallengeStore=None,expiry_days: int = 90) -> str:
32
+ def sign_csr(
33
+ self, csr: x509.CertificateSigningRequest, challenge_store: ChallengeStore = None, expiry_days: int = 90
34
+ ) -> str:
33
35
  challenge_store = challenge_store if challenge_store is not None else self.challenge_store
34
36
  hosts = self.get_csr_hostnames(csr)
35
37
  order: Order = self.acme.create_authorized_order(hosts)
@@ -41,7 +43,7 @@ class AcmeCertIssuer(CertIssuer):
41
43
  for c in challenges:
42
44
  if self.self_verify_challenge:
43
45
  c.self_verify()
44
- end = time.time() + max(len(challenges) * 10,300)
46
+ end = time.time() + max(len(challenges) * 10, 300)
45
47
  remaining_now: List[Challenge] = [x for x in challenges]
46
48
  next_remaining = []
47
49
  counter = 1
@@ -75,40 +77,49 @@ class AcmeCertIssuer(CertIssuer):
75
77
  return None
76
78
  return obtain_cert()
77
79
  return None
80
+
78
81
  return obtain_cert()
79
82
 
80
- def generate_key_and_cert_for_domains(self, hosts: Union[str, List[str]],
81
- key_type: Literal["rsa","ecdsa","ed25519"] = "rsa",
83
+ def generate_key_and_cert_for_domains(
84
+ self,
85
+ hosts: Union[str, List[str]],
86
+ key_type: Literal["rsa", "ecdsa", "ed25519"] = "rsa",
82
87
  expiry_days: int = 90,
83
88
  country: Optional[str] = None,
84
89
  state: Optional[str] = None,
85
90
  locality: Optional[str] = None,
86
91
  organization: Optional[str] = None,
87
92
  user_id: Optional[str] = None,
88
- challenge_store: Optional[ChallengeStore] = None):
89
- if len(hosts) ==0:
93
+ challenge_store: Optional[ChallengeStore] = None,
94
+ ):
95
+ if len(hosts) == 0:
90
96
  raise ValueError("CertIssuer.generate_key_and_cert_for_domains: empty hosts array provided")
91
- return self.generate_key_and_cert(hosts[0],hosts[0:],key_type,expiry_days,country,state,locality,organization,user_id,challenge_store)
97
+ return self.generate_key_and_cert(
98
+ hosts[0], hosts[0:], key_type, expiry_days, country, state, locality, organization, user_id, challenge_store
99
+ )
92
100
 
93
-
94
- def generate_key_and_cert_for_domain(self, host:str,
95
- key_type: Literal["rsa","ecdsa","ed25519"] = "rsa",
101
+ def generate_key_and_cert_for_domain(
102
+ self,
103
+ host: str,
104
+ key_type: Literal["rsa", "ecdsa", "ed25519"] = "rsa",
96
105
  expiry_days: int = 90,
97
106
  country: Optional[str] = None,
98
107
  state: Optional[str] = None,
99
108
  locality: Optional[str] = None,
100
109
  organization: Optional[str] = None,
101
110
  user_id: Optional[str] = None,
102
- challenge_store: Optional[ChallengeStore] = None):
111
+ challenge_store: Optional[ChallengeStore] = None,
112
+ ):
103
113
 
104
- return self.generate_key_and_cert(host,[],key_type,expiry_days,country,state,locality,organization,user_id,challenge_store)
105
-
114
+ return self.generate_key_and_cert(
115
+ host, [], key_type, expiry_days, country, state, locality, organization, user_id, challenge_store
116
+ )
106
117
 
107
118
  def generate_key_and_cert(
108
119
  self,
109
120
  domain: str,
110
121
  alt_names: List[str] = (),
111
- key_type: Literal["rsa","ecdsa","ed25519"] = "ecdsa",
122
+ key_type: Literal["rsa", "ecdsa", "ed25519"] = "ecdsa",
112
123
  expiry_days: int = 90,
113
124
  country: Optional[str] = None,
114
125
  state: Optional[str] = None,
@@ -5,6 +5,7 @@ from cryptography.x509.oid import NameOID
5
5
  from certapi.crypto import Key, RSAKey, ECDSAKey, Ed25519Key
6
6
  from .abstract_certissuer import CertIssuer
7
7
 
8
+
8
9
  class SelfCertIssuer(CertIssuer):
9
10
  def __init__(
10
11
  self,
@@ -43,7 +44,7 @@ class SelfCertIssuer(CertIssuer):
43
44
  name_attrs.append(x509.NameAttribute(oid, value))
44
45
 
45
46
  if include_user_id:
46
- user_id = fields.get("user_id",domain)
47
+ user_id = fields.get("user_id", domain)
47
48
  if user_id:
48
49
  name_attrs.append(x509.NameAttribute(NameOID.USER_ID, user_id))
49
50
 
@@ -84,4 +85,4 @@ class SelfCertIssuer(CertIssuer):
84
85
  for ext in csr.extensions:
85
86
  builder = builder.add_extension(ext.value, ext.critical)
86
87
 
87
- return self.root_key.sign_csr(builder)
88
+ return self.root_key.sign_csr(builder)
@@ -1,3 +1,3 @@
1
1
  from .abstract_certissuer import CertIssuer
2
2
  from .AcmeCertIssuer import AcmeCertIssuer
3
- from .SelfCertIssuer import SelfCertIssuer
3
+ from .SelfCertIssuer import SelfCertIssuer