PKCS8.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  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 Crypto.Util.py3compat import *
  34. from Crypto.Util.asn1 import (
  35. DerNull,
  36. DerSequence,
  37. DerObjectId,
  38. DerOctetString,
  39. )
  40. from Crypto.IO._PBES import PBES1, PBES2, PbesError, _DEFAULT_MAX_ITERATION_COUNT
  41. __all__ = ['wrap', 'unwrap']
  42. def wrap(private_key, key_oid, passphrase=None, protection=None,
  43. prot_params=None, key_params=DerNull(), randfunc=None):
  44. """Wrap a private key into a PKCS#8 blob (clear or encrypted).
  45. Args:
  46. private_key (bytes):
  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. or ``'1.2.840.10045.2.1'`` (for ECC keys).
  53. Keyword Args:
  54. passphrase (bytes or string):
  55. The secret passphrase from which the wrapping key is derived.
  56. Set it only if encryption is required.
  57. protection (string):
  58. The identifier of the algorithm to use for securely wrapping the key.
  59. Refer to :ref:`the encryption parameters<enc_params>` .
  60. The default value is ``'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC'``.
  61. prot_params (dictionary):
  62. Parameters for the key derivation function (KDF).
  63. Refer to :ref:`the encryption parameters<enc_params>` .
  64. key_params (DER object or None):
  65. The ``parameters`` field to use in the ``AlgorithmIdentifier``
  66. SEQUENCE. If ``None``, no ``parameters`` field will be added.
  67. By default, the ASN.1 type ``NULL`` is used.
  68. randfunc (callable):
  69. Random number generation function; it should accept a single integer
  70. N and return a string of random data, N bytes long.
  71. If not specified, a new RNG will be instantiated
  72. from :mod:`Crypto.Random`.
  73. Returns:
  74. bytes: The PKCS#8-wrapped private key (possibly encrypted).
  75. """
  76. #
  77. # PrivateKeyInfo ::= SEQUENCE {
  78. # version Version,
  79. # privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
  80. # privateKey PrivateKey,
  81. # attributes [0] IMPLICIT Attributes OPTIONAL
  82. # }
  83. #
  84. if key_params is None:
  85. algorithm = DerSequence([DerObjectId(key_oid)])
  86. else:
  87. algorithm = DerSequence([DerObjectId(key_oid), key_params])
  88. pk_info = DerSequence([
  89. 0,
  90. algorithm,
  91. DerOctetString(private_key)
  92. ])
  93. pk_info_der = pk_info.encode()
  94. if passphrase is None:
  95. return pk_info_der
  96. if not passphrase:
  97. raise ValueError("Empty passphrase")
  98. # Encryption with PBES2
  99. passphrase = tobytes(passphrase)
  100. if protection is None:
  101. protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC'
  102. return PBES2.encrypt(pk_info_der, passphrase,
  103. protection, prot_params, randfunc)
  104. def unwrap(p8_private_key, passphrase=None, max_iteration_count=None):
  105. """Unwrap a private key from a PKCS#8 blob (clear or encrypted).
  106. Args:
  107. p8_private_key (bytes):
  108. The private key wrapped into a PKCS#8 container, DER encoded.
  109. Keyword Args:
  110. passphrase (byte string or string):
  111. The passphrase to use to decrypt the blob (if it is encrypted).
  112. max_iteration_count (integer):
  113. The maximum value allowed for the PBKDF2/scrypt iteration count.
  114. An attacker could craft a malicious key blob that requires an
  115. extremely high number of iterations, tying up the CPU for hours.
  116. The default value is None (enforcing the default limit of 50,000,000).
  117. Set it to ``0`` to disable the check.
  118. Return:
  119. A tuple containing
  120. #. the algorithm identifier of the wrapped key (OID, dotted string)
  121. #. the private key (bytes, DER encoded)
  122. #. the associated parameters (bytes, DER encoded) or ``None``
  123. Raises:
  124. ValueError : if decoding fails
  125. """
  126. if passphrase is not None:
  127. passphrase = tobytes(passphrase)
  128. found = False
  129. try:
  130. p8_private_key = PBES1.decrypt(p8_private_key, passphrase,
  131. max_iteration_count=max_iteration_count)
  132. found = True
  133. except PbesError as e:
  134. error_str = "PBES1[%s]" % str(e)
  135. except ValueError:
  136. error_str = "PBES1[Invalid]"
  137. if not found:
  138. try:
  139. p8_private_key = PBES2.decrypt(p8_private_key, passphrase,
  140. max_iteration_count=max_iteration_count)
  141. found = True
  142. except PbesError as e:
  143. error_str += ",PBES2[%s]" % str(e)
  144. except ValueError:
  145. error_str += ",PBES2[Invalid]"
  146. if not found:
  147. raise ValueError("Error decoding PKCS#8 (%s)" % error_str)
  148. pk_info = DerSequence().decode(p8_private_key, nr_elements=(2, 3, 4, 5))
  149. if len(pk_info) == 2 and not passphrase:
  150. raise ValueError("Not a valid clear PKCS#8 structure "
  151. "(maybe it is encrypted?)")
  152. # RFC5208, PKCS#8, version is v1(0)
  153. #
  154. # PrivateKeyInfo ::= SEQUENCE {
  155. # version Version,
  156. # privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
  157. # privateKey PrivateKey,
  158. # attributes [0] IMPLICIT Attributes OPTIONAL
  159. # }
  160. #
  161. # RFC5915, Asymmetric Key Package, version is v2(1)
  162. #
  163. # OneAsymmetricKey ::= SEQUENCE {
  164. # version Version,
  165. # privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
  166. # privateKey PrivateKey,
  167. # attributes [0] Attributes OPTIONAL,
  168. # ...,
  169. # [[2: publicKey [1] PublicKey OPTIONAL ]],
  170. # ...
  171. # }
  172. if pk_info[0] == 0:
  173. if len(pk_info) not in (3, 4):
  174. raise ValueError("Not a valid PrivateKeyInfo SEQUENCE")
  175. elif pk_info[0] == 1:
  176. if len(pk_info) not in (3, 4, 5):
  177. raise ValueError("Not a valid PrivateKeyInfo SEQUENCE")
  178. else:
  179. raise ValueError("Not a valid PrivateKeyInfo SEQUENCE")
  180. algo = DerSequence().decode(pk_info[1], nr_elements=(1, 2))
  181. algo_oid = DerObjectId().decode(algo[0]).value
  182. if len(algo) == 1:
  183. algo_params = None
  184. else:
  185. try:
  186. DerNull().decode(algo[1])
  187. algo_params = None
  188. except:
  189. algo_params = algo[1]
  190. # PrivateKey ::= OCTET STRING
  191. private_key = DerOctetString().decode(pk_info[2]).payload
  192. # We ignore attributes and (for v2 only) publickey
  193. return (algo_oid, private_key, algo_params)