PKCS8.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. #
  2. # PublicKey/PKCS8.py : PKCS#8 functions
  3. #
  4. # ===================================================================
  5. #
  6. # Copyright (c) 2014, Legrandin <helderijs@gmail.com>
  7. # All rights reserved.
  8. #
  9. # Redistribution and use in source and binary forms, with or without
  10. # modification, are permitted provided that the following conditions
  11. # are met:
  12. #
  13. # 1. Redistributions of source code must retain the above copyright
  14. # notice, this list of conditions and the following disclaimer.
  15. # 2. Redistributions in binary form must reproduce the above copyright
  16. # notice, this list of conditions and the following disclaimer in
  17. # the documentation and/or other materials provided with the
  18. # distribution.
  19. #
  20. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  23. # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  24. # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  25. # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  26. # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  27. # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  28. # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29. # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  30. # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  31. # POSSIBILITY OF SUCH DAMAGE.
  32. # ===================================================================
  33. from tls.Crypto.Util.py3compat import *
  34. from tls.Crypto.Util.asn1 import (
  35. DerNull,
  36. DerSequence,
  37. DerObjectId,
  38. DerOctetString,
  39. )
  40. from tls.Crypto.IO._PBES import PBES1, PBES2, PbesError
  41. __all__ = ['wrap', 'unwrap']
  42. def wrap(private_key, key_oid, passphrase=None, protection=None,
  43. prot_params=None, key_params=None, randfunc=None):
  44. """Wrap a private key into a PKCS#8 blob (clear or encrypted).
  45. Args:
  46. private_key (byte string):
  47. The private key encoded in binary form. The actual encoding is
  48. algorithm specific. In most cases, it is DER.
  49. key_oid (string):
  50. The object identifier (OID) of the private key to wrap.
  51. It is a dotted string, like ``1.2.840.113549.1.1.1`` (for RSA keys).
  52. passphrase (bytes string or string):
  53. The secret passphrase from which the wrapping key is derived.
  54. Set it only if encryption is required.
  55. protection (string):
  56. The identifier of the algorithm to use for securely wrapping the key.
  57. The default value is ``PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC``.
  58. prot_params (dictionary):
  59. Parameters for the protection algorithm.
  60. +------------------+-----------------------------------------------+
  61. | Key | Description |
  62. +==================+===============================================+
  63. | iteration_count | The KDF algorithm is repeated several times to|
  64. | | slow down brute force attacks on passwords |
  65. | | (called *N* or CPU/memory cost in scrypt). |
  66. | | The default value for PBKDF2 is 1000. |
  67. | | The default value for scrypt is 16384. |
  68. +------------------+-----------------------------------------------+
  69. | salt_size | Salt is used to thwart dictionary and rainbow |
  70. | | attacks on passwords. The default value is 8 |
  71. | | bytes. |
  72. +------------------+-----------------------------------------------+
  73. | block_size | *(scrypt only)* Memory-cost (r). The default |
  74. | | value is 8. |
  75. +------------------+-----------------------------------------------+
  76. | parallelization | *(scrypt only)* CPU-cost (p). The default |
  77. | | value is 1. |
  78. +------------------+-----------------------------------------------+
  79. key_params (DER object):
  80. The algorithm parameters associated to the private key.
  81. It is required for algorithms like DSA, but not for others like RSA.
  82. randfunc (callable):
  83. Random number generation function; it should accept a single integer
  84. N and return a string of random data, N bytes long.
  85. If not specified, a new RNG will be instantiated
  86. from :mod:`Crypto.Random`.
  87. Return:
  88. The PKCS#8-wrapped private key (possibly encrypted), as a byte string.
  89. """
  90. if key_params is None:
  91. key_params = DerNull()
  92. #
  93. # PrivateKeyInfo ::= SEQUENCE {
  94. # version Version,
  95. # privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
  96. # privateKey PrivateKey,
  97. # attributes [0] IMPLICIT Attributes OPTIONAL
  98. # }
  99. #
  100. pk_info = DerSequence([
  101. 0,
  102. DerSequence([
  103. DerObjectId(key_oid),
  104. key_params
  105. ]),
  106. DerOctetString(private_key)
  107. ])
  108. pk_info_der = pk_info.encode()
  109. if passphrase is None:
  110. return pk_info_der
  111. if not passphrase:
  112. raise ValueError("Empty passphrase")
  113. # Encryption with PBES2
  114. passphrase = tobytes(passphrase)
  115. if protection is None:
  116. protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC'
  117. return PBES2.encrypt(pk_info_der, passphrase,
  118. protection, prot_params, randfunc)
  119. def unwrap(p8_private_key, passphrase=None):
  120. """Unwrap a private key from a PKCS#8 blob (clear or encrypted).
  121. Args:
  122. p8_private_key (byte string):
  123. The private key wrapped into a PKCS#8 blob, DER encoded.
  124. passphrase (byte string or string):
  125. The passphrase to use to decrypt the blob (if it is encrypted).
  126. Return:
  127. A tuple containing
  128. #. the algorithm identifier of the wrapped key (OID, dotted string)
  129. #. the private key (byte string, DER encoded)
  130. #. the associated parameters (byte string, DER encoded) or ``None``
  131. Raises:
  132. ValueError : if decoding fails
  133. """
  134. if passphrase:
  135. passphrase = tobytes(passphrase)
  136. found = False
  137. try:
  138. p8_private_key = PBES1.decrypt(p8_private_key, passphrase)
  139. found = True
  140. except PbesError as e:
  141. error_str = "PBES1[%s]" % str(e)
  142. except ValueError:
  143. error_str = "PBES1[Invalid]"
  144. if not found:
  145. try:
  146. p8_private_key = PBES2.decrypt(p8_private_key, passphrase)
  147. found = True
  148. except PbesError as e:
  149. error_str += ",PBES2[%s]" % str(e)
  150. except ValueError:
  151. error_str += ",PBES2[Invalid]"
  152. if not found:
  153. raise ValueError("Error decoding PKCS#8 (%s)" % error_str)
  154. pk_info = DerSequence().decode(p8_private_key, nr_elements=(2, 3, 4))
  155. if len(pk_info) == 2 and not passphrase:
  156. raise ValueError("Not a valid clear PKCS#8 structure "
  157. "(maybe it is encrypted?)")
  158. #
  159. # PrivateKeyInfo ::= SEQUENCE {
  160. # version Version,
  161. # privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
  162. # privateKey PrivateKey,
  163. # attributes [0] IMPLICIT Attributes OPTIONAL
  164. # }
  165. # Version ::= INTEGER
  166. if pk_info[0] != 0:
  167. raise ValueError("Not a valid PrivateKeyInfo SEQUENCE")
  168. # PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
  169. #
  170. # EncryptedPrivateKeyInfo ::= SEQUENCE {
  171. # encryptionAlgorithm EncryptionAlgorithmIdentifier,
  172. # encryptedData EncryptedData
  173. # }
  174. # EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
  175. # AlgorithmIdentifier ::= SEQUENCE {
  176. # algorithm OBJECT IDENTIFIER,
  177. # parameters ANY DEFINED BY algorithm OPTIONAL
  178. # }
  179. algo = DerSequence().decode(pk_info[1], nr_elements=(1, 2))
  180. algo_oid = DerObjectId().decode(algo[0]).value
  181. if len(algo) == 1:
  182. algo_params = None
  183. else:
  184. try:
  185. DerNull().decode(algo[1])
  186. algo_params = None
  187. except:
  188. algo_params = algo[1]
  189. # EncryptedData ::= OCTET STRING
  190. private_key = DerOctetString().decode(pk_info[2]).payload
  191. return (algo_oid, private_key, algo_params)