kicost-digikey-api-v4 0.1.3__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 (86) hide show
  1. kicost_digikey_api_v4-0.1.3/MANIFEST.in +1 -0
  2. kicost_digikey_api_v4-0.1.3/PKG-INFO +65 -0
  3. kicost_digikey_api_v4-0.1.3/README.md +47 -0
  4. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/__init__.py +20 -0
  5. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/certauth/__init__.py +0 -0
  6. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/certauth/certauth.py +421 -0
  7. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/exceptions.py +10 -0
  8. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/oauth/__init__.py +0 -0
  9. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/oauth/oauth2.py +306 -0
  10. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/utils.py +185 -0
  11. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/__init__.py +0 -0
  12. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/api.py +145 -0
  13. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/__init__.py +79 -0
  14. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/api/__init__.py +6 -0
  15. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/api/product_search_api.py +1722 -0
  16. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/api_client.py +632 -0
  17. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/configuration.py +263 -0
  18. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/__init__.py +73 -0
  19. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/alternate_packaging.py +112 -0
  20. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/alternate_packaging_response.py +136 -0
  21. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/base_filter_v4.py +162 -0
  22. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/base_product.py +140 -0
  23. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/break_price.py +140 -0
  24. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/categories_response.py +166 -0
  25. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/category.py +224 -0
  26. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/category_node.py +308 -0
  27. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/category_response.py +136 -0
  28. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/category_type.py +168 -0
  29. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/classifications.py +224 -0
  30. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/description.py +140 -0
  31. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/digi_reel_pricing.py +222 -0
  32. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/dk_problem_details.py +266 -0
  33. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/filter_id.py +112 -0
  34. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/filter_options.py +287 -0
  35. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/filter_options_request.py +347 -0
  36. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/filter_value.py +202 -0
  37. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/filters.py +140 -0
  38. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/iso_search_locale.py +168 -0
  39. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/keyword_request.py +220 -0
  40. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/keyword_response.py +242 -0
  41. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/manufacturer.py +140 -0
  42. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/manufacturer_info.py +140 -0
  43. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/manufacturers_response.py +112 -0
  44. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/media_links.py +224 -0
  45. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/media_response.py +112 -0
  46. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/package_type.py +140 -0
  47. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/package_type_by_quantity_product.py +726 -0
  48. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/package_type_by_quantity_response.py +112 -0
  49. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/parameter.py +168 -0
  50. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/parameter_filter_options_response.py +222 -0
  51. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/parameter_filter_request.py +138 -0
  52. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/parameter_value.py +230 -0
  53. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/parametric_category.py +140 -0
  54. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/price_break.py +168 -0
  55. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/product.py +766 -0
  56. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/product_associations.py +196 -0
  57. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/product_associations_response.py +136 -0
  58. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/product_details.py +136 -0
  59. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/product_pricing.py +668 -0
  60. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/product_pricing_response.py +166 -0
  61. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/product_pricing_variation.py +306 -0
  62. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/product_status_v4.py +136 -0
  63. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/product_substitute.py +278 -0
  64. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/product_substitutes_response.py +166 -0
  65. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/product_summary.py +250 -0
  66. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/product_variation.py +416 -0
  67. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/recommendation.py +166 -0
  68. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/recommended_product.py +308 -0
  69. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/recommended_products_response.py +112 -0
  70. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/series.py +140 -0
  71. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/settings_used.py +164 -0
  72. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/sort_direction.py +93 -0
  73. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/sort_option.py +99 -0
  74. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/sort_options.py +152 -0
  75. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/supplier.py +140 -0
  76. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/top_category.py +192 -0
  77. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/models/top_category_node.py +168 -0
  78. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4/v4/productinformation/rest.py +317 -0
  79. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4.egg-info/PKG-INFO +65 -0
  80. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4.egg-info/SOURCES.txt +85 -0
  81. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4.egg-info/dependency_links.txt +1 -0
  82. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4.egg-info/requires.txt +9 -0
  83. kicost_digikey_api_v4-0.1.3/kicost_digikey_api_v4.egg-info/top_level.txt +1 -0
  84. kicost_digikey_api_v4-0.1.3/requirements.txt +9 -0
  85. kicost_digikey_api_v4-0.1.3/setup.cfg +25 -0
  86. kicost_digikey_api_v4-0.1.3/setup.py +40 -0
@@ -0,0 +1 @@
1
+ include requirements.txt
@@ -0,0 +1,65 @@
1
+ Metadata-Version: 2.1
2
+ Name: kicost_digikey_api_v4
3
+ Version: 0.1.3
4
+ Summary: KiCost plugin for the Digikey PartSearch API
5
+ Home-page: https://github.com/set-soft/kicost-digikey-api-v4
6
+ Author: Salvador E. Tropea
7
+ Author-email: stropea@inti.gob.ar
8
+ License: GPL v3
9
+ Description: KiCost plug-in for Digi-Key API
10
+ =================================
11
+ This is an experimental plug-in to allow KiCost to do native Digi-Key API requests.
12
+
13
+ The plug-in is based on the [Python Client for Digikey API](https://github.com/peeter123/digikey-api)
14
+ by [Peter Oostewechel](https://github.com/peeter123).
15
+
16
+ I'm not using the original project as a module because it pulls some rare dependencies.
17
+
18
+ The license is GPL v3 as the original work.
19
+
20
+ # Quickstart
21
+
22
+ - Register at Digi-Key. You need a valid user in order to register an application and in order to authenticate.
23
+ - Go to the API portal: [Digi-Key API Solutions](https://developer.digikey.com/get_started)
24
+ - Login to Digi-Key from the API portal
25
+ - Create an [organization](https://developer.digikey.com/teams)
26
+ - Choose the `Production Apps` operation
27
+ - Create a production app
28
+ - Choose a `Production App name`
29
+ - Use `https://localhost:8139/digikey_callback` for the OAuth Callback. So you can authorize the use of the app.
30
+ - Give a description
31
+ - Enable the `Product Information` API
32
+ - Click `Add production app`
33
+ - Now select your newly created `Production App`
34
+ - In the `Credential` section enable the options to show the `Client ID` and `Client Secret`
35
+ - Copy these values to a file named `config.txt` containing:
36
+
37
+ ```
38
+ DIGIKEY_CLIENT_ID = Client_ID_Value_for_your_app
39
+ DIGIKEY_CLIENT_SECRET = Client_Secret_Value_for_your_app
40
+ ```
41
+
42
+ - Create a folder `~/.config/kicost_digikey_api_v4`
43
+ - Store the config.txt file
44
+ - Now install the plug-in:
45
+ - Clone the git repo somewhere using `git clone https://github.com/set-soft/kicost-digikey-api-v4.git`
46
+ - Enter to `kicost-digikey-api-v4`
47
+ - Install it using `pip3 install -e .`
48
+ - Now test the plug-in:
49
+ - run `python3 test_production.py`
50
+ - A browser window will pop-up asking to login to Digi-Key. Login.
51
+ - Choose the allow option to enable the token.
52
+ - Now you'll load a local page, allow it.
53
+ - Now the token is stored on disk and you won't need to confirm it for months.
54
+ - You should get the information for Digi-Key part 296-6501-6-ND
55
+
56
+
57
+ Platform: UNKNOWN
58
+ Classifier: Development Status :: 3 - Alpha
59
+ Classifier: Environment :: Web Environment
60
+ Classifier: Intended Audience :: Developers
61
+ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
62
+ Classifier: Programming Language :: Python :: 2.7
63
+ Classifier: Programming Language :: Python :: 3.6
64
+ Classifier: Topic :: Software Development
65
+ Description-Content-Type: text/markdown
@@ -0,0 +1,47 @@
1
+ KiCost plug-in for Digi-Key API
2
+ =================================
3
+ This is an experimental plug-in to allow KiCost to do native Digi-Key API requests.
4
+
5
+ The plug-in is based on the [Python Client for Digikey API](https://github.com/peeter123/digikey-api)
6
+ by [Peter Oostewechel](https://github.com/peeter123).
7
+
8
+ I'm not using the original project as a module because it pulls some rare dependencies.
9
+
10
+ The license is GPL v3 as the original work.
11
+
12
+ # Quickstart
13
+
14
+ - Register at Digi-Key. You need a valid user in order to register an application and in order to authenticate.
15
+ - Go to the API portal: [Digi-Key API Solutions](https://developer.digikey.com/get_started)
16
+ - Login to Digi-Key from the API portal
17
+ - Create an [organization](https://developer.digikey.com/teams)
18
+ - Choose the `Production Apps` operation
19
+ - Create a production app
20
+ - Choose a `Production App name`
21
+ - Use `https://localhost:8139/digikey_callback` for the OAuth Callback. So you can authorize the use of the app.
22
+ - Give a description
23
+ - Enable the `Product Information` API
24
+ - Click `Add production app`
25
+ - Now select your newly created `Production App`
26
+ - In the `Credential` section enable the options to show the `Client ID` and `Client Secret`
27
+ - Copy these values to a file named `config.txt` containing:
28
+
29
+ ```
30
+ DIGIKEY_CLIENT_ID = Client_ID_Value_for_your_app
31
+ DIGIKEY_CLIENT_SECRET = Client_Secret_Value_for_your_app
32
+ ```
33
+
34
+ - Create a folder `~/.config/kicost_digikey_api_v4`
35
+ - Store the config.txt file
36
+ - Now install the plug-in:
37
+ - Clone the git repo somewhere using `git clone https://github.com/set-soft/kicost-digikey-api-v4.git`
38
+ - Enter to `kicost-digikey-api-v4`
39
+ - Install it using `pip3 install -e .`
40
+ - Now test the plug-in:
41
+ - run `python3 test_production.py`
42
+ - A browser window will pop-up asking to login to Digi-Key. Login.
43
+ - Choose the allow option to enable the token.
44
+ - Now you'll load a local page, allow it.
45
+ - Now the token is stored on disk and you won't need to confirm it for months.
46
+ - You should get the information for Digi-Key part 296-6501-6-ND
47
+
@@ -0,0 +1,20 @@
1
+ import logging
2
+ from kicost_digikey_api_v4.v4.api import (keyword_search, product_details, digi_reel_pricing, suggested_parts)
3
+ from .utils import by_digikey_pn, by_manf_pn, by_keyword, DK_API
4
+ from .exceptions import DigikeyError
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+
9
+ def setup_logger(logger_ref):
10
+ logger_ref.setLevel(logging.DEBUG)
11
+ formatter = logging.Formatter(
12
+ '%(asctime)s - %(name)20.20s - %(levelname)8s: %(message)s')
13
+ handler = logging.StreamHandler()
14
+ handler.setFormatter(formatter)
15
+ logger_ref.addHandler(handler)
16
+
17
+
18
+ setup_logger(logger)
19
+
20
+ name = 'digikey'
@@ -0,0 +1,421 @@
1
+ import logging
2
+ import os
3
+
4
+ from io import BytesIO
5
+
6
+ from OpenSSL import crypto
7
+ from OpenSSL.SSL import FILETYPE_PEM
8
+
9
+ import random
10
+
11
+ import ipaddress
12
+ import tldextract
13
+
14
+ from argparse import ArgumentParser
15
+
16
+ from collections import OrderedDict
17
+
18
+ import threading
19
+
20
+ # =================================================================
21
+ # Valid for 3 years from now
22
+ # Max validity is 39 months:
23
+ # https://casecurity.org/2015/02/19/ssl-certificate-validity-periods-limited-to-39-months-starting-in-april/
24
+ CERT_NOT_AFTER = 3 * 365 * 24 * 60 * 60
25
+
26
+ CERTS_DIR = './ca/certs/'
27
+
28
+ CERT_NAME = 'certauth sample CA'
29
+
30
+ DEF_HASH_FUNC = 'sha256'
31
+
32
+ ROOT_CA = '!!root_ca'
33
+
34
+
35
+ # =================================================================
36
+ class CertificateAuthority(object):
37
+ """
38
+ Utility class for signing individual certificate
39
+ with a root cert.
40
+
41
+ Static generate_ca_root() method for creating the root cert
42
+
43
+ All certs saved on filesystem. Individual certs are stored
44
+ in specified certs_dir and reused if previously created.
45
+ """
46
+
47
+ def __init__(self, ca_name,
48
+ ca_file_cache,
49
+ cert_cache=None,
50
+ cert_not_before=0,
51
+ cert_not_after=CERT_NOT_AFTER,
52
+ overwrite=False):
53
+
54
+ if isinstance(ca_file_cache, str):
55
+ self.ca_file_cache = RootCACache(ca_file_cache)
56
+ else:
57
+ self.ca_file_cache = ca_file_cache
58
+
59
+ if isinstance(cert_cache, str):
60
+ self.cert_cache = FileCache(cert_cache)
61
+ elif isinstance(cert_cache, int):
62
+ self.cert_cache = LRUCache(max_size=cert_cache)
63
+ elif cert_cache is None:
64
+ self.cert_cache = LRUCache(max_size=100)
65
+ else:
66
+ self.cert_cache = cert_cache
67
+
68
+ self.ca_name = ca_name
69
+
70
+ self.cert_not_before = cert_not_before
71
+ self.cert_not_after = cert_not_after
72
+
73
+ res = self.load_root_ca_cert(overwrite=overwrite)
74
+ self.ca_cert, self.ca_key = res
75
+
76
+ def load_root_ca_cert(self, overwrite=False):
77
+ cert_str = None
78
+
79
+ if not overwrite:
80
+ cert_str = self.ca_file_cache.get(ROOT_CA)
81
+
82
+ # if cached, just read pem
83
+ if cert_str:
84
+ cert, key = self.read_pem(BytesIO(cert_str))
85
+
86
+ else:
87
+ cert, key = self.generate_ca_root(self.ca_name)
88
+
89
+ # Write cert + key
90
+ buff = BytesIO()
91
+ self.write_pem(buff, cert, key)
92
+ cert_str = buff.getvalue()
93
+
94
+ # store cert in cache
95
+ self.ca_file_cache[ROOT_CA] = cert_str
96
+
97
+ return cert, key
98
+
99
+ def is_host_ip(self, host):
100
+ try:
101
+ # if py2.7, need to decode to unicode str
102
+ if hasattr(host, 'decode'): #pragma: no cover
103
+ host = host.decode('ascii')
104
+
105
+ ipaddress.ip_address(host)
106
+ return True
107
+ except (ValueError, UnicodeDecodeError) as e:
108
+ return False
109
+
110
+ def get_wildcard_domain(self, host):
111
+ host_parts = host.split('.', 1)
112
+ if len(host_parts) < 2 or '.' not in host_parts[1]:
113
+ return host
114
+
115
+ ext = tldextract.extract(host)
116
+
117
+ # allow using parent domain if:
118
+ # 1) no suffix (unknown tld)
119
+ # 2) the parent domain contains 'domain.suffix', not just .suffix
120
+ if not ext.suffix or ext.domain + '.' + ext.suffix in host_parts[1]:
121
+ return host_parts[1]
122
+
123
+ return host
124
+
125
+ def load_cert(self, host, overwrite=False,
126
+ wildcard=False,
127
+ wildcard_use_parent=False,
128
+ include_cache_key=False):
129
+
130
+ is_ip = self.is_host_ip(host)
131
+
132
+ if is_ip:
133
+ wildcard = False
134
+
135
+ if wildcard and wildcard_use_parent:
136
+ host = self.get_wildcard_domain(host)
137
+
138
+ cert_str = None
139
+
140
+ if not overwrite:
141
+ cert_str = self.cert_cache.get(host)
142
+
143
+ # if cached, just read pem
144
+ if cert_str:
145
+ cert, key = self.read_pem(BytesIO(cert_str))
146
+
147
+ else:
148
+ # if not cached, generate new root or host cert
149
+ cert, key = self.generate_host_cert(host,
150
+ self.ca_cert,
151
+ self.ca_key,
152
+ wildcard,
153
+ is_ip=is_ip)
154
+
155
+ # Write cert + key
156
+ buff = BytesIO()
157
+ self.write_pem(buff, cert, key)
158
+ cert_str = buff.getvalue()
159
+
160
+ # store cert in cache
161
+ self.cert_cache[host] = cert_str
162
+
163
+ if not include_cache_key:
164
+ return cert, key
165
+
166
+ else:
167
+ cache_key = host
168
+ if hasattr(self.cert_cache, 'key_for_host'):
169
+ cache_key = self.cert_cache.key_for_host(host)
170
+
171
+ return cert, key, cache_key
172
+
173
+ def cert_for_host(self, host, overwrite=False,
174
+ wildcard=False):
175
+
176
+ res = self.load_cert(host, overwrite=overwrite,
177
+ wildcard=wildcard,
178
+ wildcard_use_parent=False,
179
+ include_cache_key=True)
180
+
181
+ return res[2]
182
+
183
+
184
+
185
+ def get_wildcard_cert(self, cert_host, overwrite=False):
186
+ res = self.load_cert(cert_host, overwrite=overwrite,
187
+ wildcard=True,
188
+ wildcard_use_parent=True,
189
+ include_cache_key=True)
190
+
191
+ return res[2]
192
+
193
+ def get_root_PKCS12(self):
194
+ p12 = crypto.PKCS12()
195
+ p12.set_certificate(self.ca_cert)
196
+ p12.set_privatekey(self.ca_key)
197
+ return p12.export()
198
+
199
+ def get_root_pem(self):
200
+ return self.ca_file_cache.get(ROOT_CA)
201
+
202
+ def get_root_pem_filename(self):
203
+ return self.ca_file_cache.ca_file
204
+
205
+ def _make_cert(self, certname):
206
+ cert = crypto.X509()
207
+ cert.set_serial_number(random.randint(0, 2 ** 64 - 1))
208
+ cert.get_subject().CN = certname
209
+
210
+ cert.set_version(2)
211
+ cert.gmtime_adj_notBefore(self.cert_not_before)
212
+ cert.gmtime_adj_notAfter(self.cert_not_after)
213
+ return cert
214
+
215
+ def generate_ca_root(self, ca_name, hash_func=DEF_HASH_FUNC):
216
+ # Generate key
217
+ key = crypto.PKey()
218
+ key.generate_key(crypto.TYPE_RSA, 2048)
219
+
220
+ # Generate cert
221
+ cert = self._make_cert(ca_name)
222
+
223
+ cert.set_issuer(cert.get_subject())
224
+ cert.set_pubkey(key)
225
+ cert.add_extensions([
226
+ crypto.X509Extension(b"basicConstraints",
227
+ True,
228
+ b"CA:TRUE, pathlen:0"),
229
+
230
+ crypto.X509Extension(b"keyUsage",
231
+ True,
232
+ b"keyCertSign, cRLSign"),
233
+
234
+ crypto.X509Extension(b"subjectKeyIdentifier",
235
+ False,
236
+ b"hash",
237
+ subject=cert),
238
+ ])
239
+ cert.sign(key, hash_func)
240
+
241
+ return cert, key
242
+
243
+ def generate_host_cert(self, host, root_cert, root_key,
244
+ wildcard=False,
245
+ hash_func=DEF_HASH_FUNC,
246
+ is_ip=False):
247
+
248
+ host = host.encode('utf-8')
249
+
250
+ # Generate key
251
+ key = crypto.PKey()
252
+ key.generate_key(crypto.TYPE_RSA, 2048)
253
+
254
+ # Generate CSR
255
+ req = crypto.X509Req()
256
+ req.get_subject().CN = host
257
+ req.set_pubkey(key)
258
+ req.sign(key, hash_func)
259
+
260
+ # Generate Cert
261
+ cert = self._make_cert(host)
262
+
263
+ cert.set_issuer(root_cert.get_subject())
264
+ cert.set_pubkey(req.get_pubkey())
265
+
266
+ primary = b'DNS:' + host
267
+
268
+ if wildcard:
269
+ alt_hosts = primary + b', DNS:*.' + host
270
+
271
+ elif is_ip:
272
+ alt_hosts = b'IP:' + host + b', ' + primary
273
+
274
+ else:
275
+ alt_hosts = primary
276
+
277
+ cert.add_extensions([
278
+ crypto.X509Extension(b'subjectAltName',
279
+ False,
280
+ alt_hosts)])
281
+
282
+ cert.sign(root_key, hash_func)
283
+ return cert, key
284
+
285
+ def write_pem(self, buff, cert, key):
286
+ buff.write(crypto.dump_privatekey(FILETYPE_PEM, key))
287
+ buff.write(crypto.dump_certificate(FILETYPE_PEM, cert))
288
+
289
+ def read_pem(self, buff):
290
+ cert = crypto.load_certificate(FILETYPE_PEM, buff.read())
291
+ buff.seek(0)
292
+ key = crypto.load_privatekey(FILETYPE_PEM, buff.read())
293
+ return cert, key
294
+
295
+
296
+ # =================================================================
297
+ class FileCache(object):
298
+ def __init__(self, certs_dir):
299
+ self._lock = threading.Lock()
300
+ self.certs_dir = certs_dir
301
+ self.modified = False
302
+
303
+ if self.certs_dir and not os.path.exists(self.certs_dir):
304
+ os.makedirs(self.certs_dir)
305
+
306
+ def key_for_host(self, host):
307
+ host = host.replace(':', '-')
308
+ return os.path.join(self.certs_dir, host) + '.pem'
309
+
310
+ def __setitem__(self, host, cert_string):
311
+ filename = self.key_for_host(host)
312
+ with self._lock:
313
+ with open(filename, 'wb') as fh:
314
+ fh.write(cert_string)
315
+ self.modified = True
316
+
317
+ def get(self, host):
318
+ filename = self.key_for_host(host)
319
+ try:
320
+ with open(filename, 'rb') as fh:
321
+ return fh.read()
322
+ except:
323
+ return b''
324
+
325
+
326
+ # =================================================================
327
+ class RootCACache(FileCache):
328
+ def __init__(self, ca_file):
329
+ self.ca_file = ca_file
330
+ ca_dir = os.path.dirname(ca_file) or '.'
331
+ super(RootCACache, self).__init__(ca_dir)
332
+
333
+ def key_for_host(self, host=None):
334
+ return self.ca_file
335
+
336
+
337
+ # =================================================================
338
+ class LRUCache(OrderedDict):
339
+ def __init__(self, max_size):
340
+ super(LRUCache, self).__init__()
341
+ self.max_size = max_size
342
+
343
+ def __setitem__(self, host, cert_string):
344
+ super(LRUCache, self).__setitem__(host, cert_string)
345
+ if len(self) > self.max_size:
346
+ self.popitem(last=False)
347
+
348
+
349
+ # =================================================================
350
+ def main(args=None):
351
+ parser = ArgumentParser(description='Certificate Authority Cert Maker Tools')
352
+
353
+ parser.add_argument('root_ca_cert',
354
+ help='Path to existing or new root CA file')
355
+
356
+ parser.add_argument('-c', '--certname', action='store', default=CERT_NAME,
357
+ help='Name for root certificate')
358
+
359
+ parser.add_argument('-n', '--hostname',
360
+ help='Hostname certificate to create')
361
+
362
+ parser.add_argument('-d', '--certs-dir', default=CERTS_DIR,
363
+ help='Directory for host certificates')
364
+
365
+ parser.add_argument('-f', '--force', action='store_true',
366
+ help='Overwrite certificates if they already exist')
367
+
368
+ parser.add_argument('-w', '--wildcard_cert', action='store_true',
369
+ help='add wildcard SAN to host: *.<host>, <host>')
370
+
371
+ r = parser.parse_args(args=args)
372
+
373
+ certs_dir = r.certs_dir
374
+ wildcard = r.wildcard_cert
375
+
376
+ root_cert = r.root_ca_cert
377
+ hostname = r.hostname
378
+
379
+ if not hostname:
380
+ overwrite = r.force
381
+ else:
382
+ overwrite = False
383
+
384
+ cert_cache = FileCache(r.certs_dir)
385
+ ca_file_cache = RootCACache(root_cert)
386
+
387
+ ca = CertificateAuthority(ca_name=r.certname,
388
+ ca_file_cache=ca_file_cache,
389
+ cert_cache=cert_cache,
390
+ overwrite=overwrite)
391
+
392
+ # Just creating the root cert
393
+ if not hostname:
394
+ if ca_file_cache.modified:
395
+ print('Created new root cert: "' + root_cert + '"')
396
+ return 0
397
+ else:
398
+ print('Root cert "' + root_cert +
399
+ '" already exists,' + ' use -f to overwrite')
400
+ return 1
401
+
402
+ # Sign a certificate for a given host
403
+ overwrite = r.force
404
+ ca.load_cert(hostname, overwrite=overwrite,
405
+ wildcard=wildcard,
406
+ wildcard_use_parent=False)
407
+
408
+ if cert_cache.modified:
409
+ print('Created new cert "' + hostname +
410
+ '" signed by root cert ' +
411
+ root_cert)
412
+ return 0
413
+
414
+ else:
415
+ print('Cert for "' + hostname + '" already exists,' +
416
+ ' use -f to overwrite')
417
+ return 1
418
+
419
+
420
+ if __name__ == "__main__": #pragma: no cover
421
+ main()
@@ -0,0 +1,10 @@
1
+ class DigikeyError(Exception):
2
+ pass
3
+
4
+
5
+ class DigikeyTypeError(Exception):
6
+ pass
7
+
8
+
9
+ class DigikeyOauthException(Exception):
10
+ pass