pkcs7.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. # This file is dual licensed under the terms of the Apache License, Version
  2. # 2.0, and the BSD License. See the LICENSE file in the root of this repository
  3. # for complete details.
  4. from __future__ import absolute_import, division, print_function
  5. from enum import Enum
  6. from cryptography import x509
  7. from cryptography.hazmat.backends import _get_backend
  8. from cryptography.hazmat.primitives import hashes, serialization
  9. from cryptography.hazmat.primitives.asymmetric import ec, rsa
  10. from cryptography.utils import _check_byteslike
  11. def load_pem_pkcs7_certificates(data):
  12. backend = _get_backend(None)
  13. return backend.load_pem_pkcs7_certificates(data)
  14. def load_der_pkcs7_certificates(data):
  15. backend = _get_backend(None)
  16. return backend.load_der_pkcs7_certificates(data)
  17. class PKCS7SignatureBuilder(object):
  18. def __init__(self, data=None, signers=[], additional_certs=[]):
  19. self._data = data
  20. self._signers = signers
  21. self._additional_certs = additional_certs
  22. def set_data(self, data):
  23. _check_byteslike("data", data)
  24. if self._data is not None:
  25. raise ValueError("data may only be set once")
  26. return PKCS7SignatureBuilder(data, self._signers)
  27. def add_signer(self, certificate, private_key, hash_algorithm):
  28. if not isinstance(
  29. hash_algorithm,
  30. (
  31. hashes.SHA1,
  32. hashes.SHA224,
  33. hashes.SHA256,
  34. hashes.SHA384,
  35. hashes.SHA512,
  36. ),
  37. ):
  38. raise TypeError(
  39. "hash_algorithm must be one of hashes.SHA1, SHA224, "
  40. "SHA256, SHA384, or SHA512"
  41. )
  42. if not isinstance(certificate, x509.Certificate):
  43. raise TypeError("certificate must be a x509.Certificate")
  44. if not isinstance(
  45. private_key, (rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey)
  46. ):
  47. raise TypeError("Only RSA & EC keys are supported at this time.")
  48. return PKCS7SignatureBuilder(
  49. self._data,
  50. self._signers + [(certificate, private_key, hash_algorithm)],
  51. )
  52. def add_certificate(self, certificate):
  53. if not isinstance(certificate, x509.Certificate):
  54. raise TypeError("certificate must be a x509.Certificate")
  55. return PKCS7SignatureBuilder(
  56. self._data, self._signers, self._additional_certs + [certificate]
  57. )
  58. def sign(self, encoding, options, backend=None):
  59. if len(self._signers) == 0:
  60. raise ValueError("Must have at least one signer")
  61. if self._data is None:
  62. raise ValueError("You must add data to sign")
  63. options = list(options)
  64. if not all(isinstance(x, PKCS7Options) for x in options):
  65. raise ValueError("options must be from the PKCS7Options enum")
  66. if encoding not in (
  67. serialization.Encoding.PEM,
  68. serialization.Encoding.DER,
  69. serialization.Encoding.SMIME,
  70. ):
  71. raise ValueError(
  72. "Must be PEM, DER, or SMIME from the Encoding enum"
  73. )
  74. # Text is a meaningless option unless it is accompanied by
  75. # DetachedSignature
  76. if (
  77. PKCS7Options.Text in options
  78. and PKCS7Options.DetachedSignature not in options
  79. ):
  80. raise ValueError(
  81. "When passing the Text option you must also pass "
  82. "DetachedSignature"
  83. )
  84. if PKCS7Options.Text in options and encoding in (
  85. serialization.Encoding.DER,
  86. serialization.Encoding.PEM,
  87. ):
  88. raise ValueError(
  89. "The Text option is only available for SMIME serialization"
  90. )
  91. # No attributes implies no capabilities so we'll error if you try to
  92. # pass both.
  93. if (
  94. PKCS7Options.NoAttributes in options
  95. and PKCS7Options.NoCapabilities in options
  96. ):
  97. raise ValueError(
  98. "NoAttributes is a superset of NoCapabilities. Do not pass "
  99. "both values."
  100. )
  101. backend = _get_backend(backend)
  102. return backend.pkcs7_sign(self, encoding, options)
  103. class PKCS7Options(Enum):
  104. Text = "Add text/plain MIME type"
  105. Binary = "Don't translate input data into canonical MIME format"
  106. DetachedSignature = "Don't embed data in the PKCS7 structure"
  107. NoCapabilities = "Don't embed SMIME capabilities"
  108. NoAttributes = "Don't embed authenticatedAttributes"
  109. NoCerts = "Don't embed signer certificate"