-
Notifications
You must be signed in to change notification settings - Fork 1.6k
smime signer support #5465
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
smime signer support #5465
Changes from 7 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
6c0b653
smime signer support
reaperhulk 6678b7c
fix ed25519 check
reaperhulk 34a7388
change some wording
reaperhulk b309cf1
python 2.7...
reaperhulk 8b5e9cc
review feedback
reaperhulk cef8e6f
s/secure/signed
reaperhulk 06888f0
do some verification in the tests
reaperhulk e31957d
review feedback
reaperhulk b16de8a
doc return value
reaperhulk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,5 +14,6 @@ Primitives | |
mac/index | ||
cryptographic-hashes | ||
symmetric-encryption | ||
smime | ||
padding | ||
twofactor |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
.. hazmat:: | ||
|
||
S/MIME | ||
====== | ||
|
||
.. module:: cryptography.hazmat.primitives.smime | ||
|
||
.. testsetup:: | ||
|
||
ca_key = b""" | ||
-----BEGIN PRIVATE KEY----- | ||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgA8Zqz5vLeR0ePZUe | ||
jBfdyMmnnI4U5uAJApWTsMn/RuWhRANCAAQY/8+7+Tm49d3D7sBAiwZ1BqtPzdgs | ||
UiROH+AQRme1XxW5Yr07zwxvvhr3tKEPtLnLboazUPlsUb/Bgte+xfkF | ||
-----END PRIVATE KEY----- | ||
""".strip() | ||
|
||
ca_cert = b""" | ||
-----BEGIN CERTIFICATE----- | ||
MIIBUTCB96ADAgECAgIDCTAKBggqhkjOPQQDAjAnMQswCQYDVQQGEwJVUzEYMBYG | ||
A1UEAwwPY3J5cHRvZ3JhcGh5IENBMB4XDTE3MDEwMTEyMDEwMFoXDTM4MTIzMTA4 | ||
MzAwMFowJzELMAkGA1UEBhMCVVMxGDAWBgNVBAMMD2NyeXB0b2dyYXBoeSBDQTBZ | ||
MBMGByqGSM49AgEGCCqGSM49AwEHA0IABBj/z7v5Obj13cPuwECLBnUGq0/N2CxS | ||
JE4f4BBGZ7VfFblivTvPDG++Gve0oQ+0uctuhrNQ+WxRv8GC177F+QWjEzARMA8G | ||
A1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSQAwRgIhANES742XWm64tkGnz8Dn | ||
pG6u2lHkZFQr3oaVvPcemvlbAiEA0WGGzmYx5C9UvfXIK7NEziT4pQtyESE0uRVK | ||
Xw4nMqk= | ||
-----END CERTIFICATE----- | ||
""".strip() | ||
|
||
|
||
S/MIME provides a method to send and receive signed MIME messages. It is | ||
commonly used in email. S/MIME has multiple versions, but this | ||
module implements a subset of :rfc:`2632`, also known as S/MIME Version 3. | ||
|
||
|
||
.. class:: SMIMESignatureBuilder | ||
|
||
.. versionadded:: 3.2 | ||
|
||
.. doctest:: | ||
|
||
>>> from cryptography.hazmat.primitives import hashes, serialization, smime | ||
>>> from cryptography import x509 | ||
>>> cert = x509.load_pem_x509_certificate(ca_cert) | ||
>>> key = serialization.load_pem_private_key(ca_key, None) | ||
>>> options = [smime.SMIMEOptions.DetachedSignature] | ||
>>> smime.SMIMESignatureBuilder().set_data( | ||
... b"data to sign" | ||
... ).add_signer( | ||
... cert, key, hashes.SHA256() | ||
... ).sign( | ||
... serialization.Encoding.PEM, options | ||
... ) | ||
b'...' | ||
|
||
.. method:: set_data(data) | ||
|
||
:param data: The data to be hashed and signed. | ||
:type data: :term:`bytes-like` | ||
|
||
.. method:: add_signer(certificate, private_key, hash_algorithm) | ||
|
||
:param certificate: The :class:`~cryptography.x509.Certificate`. | ||
|
||
:param private_key: The | ||
:class:`~cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey` or | ||
:class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey` | ||
associated with the certificate provided. | ||
|
||
:param hash_algorithm: The | ||
:class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm` that | ||
will be used to generate the signature. This must be an instance of | ||
:class:`~cryptography.hazmat.primitives.hashes.SHA1`, | ||
:class:`~cryptography.hazmat.primitives.hashes.SHA224`, | ||
:class:`~cryptography.hazmat.primitives.hashes.SHA256`, | ||
:class:`~cryptography.hazmat.primitives.hashes.SHA384`, or | ||
:class:`~cryptography.hazmat.primitives.hashes.SHA512`. | ||
|
||
.. method:: sign(encoding, options, backend=None) | ||
|
||
:param encoding: :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` | ||
or :attr:`~cryptography.hazmat.primitives.serialization.Encoding.DER`. | ||
|
||
:param options: A list of :class:`~cryptography.hazmat.primitives.smime.SMIMEOptions`. | ||
|
||
:param backend: An optional backend. | ||
|
||
|
||
.. class:: SMIMEOptions | ||
|
||
.. versionadded:: 3.2 | ||
|
||
An enumeration of options for S/MIME signature creation. | ||
|
||
.. attribute:: Text | ||
|
||
The text option adds ``text/plain`` headers to the S/MIME message when | ||
serializing to | ||
:attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM`. | ||
This option has no effect with ``DER`` serialization. | ||
reaperhulk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
.. attribute:: Binary | ||
|
||
S/MIME signing normally converts line endings (LF to CRLF). When | ||
passing this option the data will not be converted. | ||
|
||
.. attribute:: DetachedSignature | ||
|
||
Don't embed the signed data within the ASN.1. When signing with | ||
:attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM` this | ||
also results in the data being added as clear text before the | ||
PEM encoded structure. | ||
alex marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
.. attribute:: NoCapabilities | ||
|
||
S/MIME structures contain a ``MIMECapabilities`` section inside the | ||
``authenticatedAttributes``. Passing this as an option removes | ||
``MIMECapabilities``. | ||
|
||
.. attribute:: NoAttributes | ||
|
||
S/MIME structures contain an ``authenticatedAttributes`` section. | ||
Passing this as an option removes that section. Note that if you | ||
pass ``NoAttributes`` you can't pass ``NoCapabilities`` since | ||
``NoAttributes`` removes ``MIMECapabilities`` and more. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
# This file is dual licensed under the terms of the Apache License, Version | ||
# 2.0, and the BSD License. See the LICENSE file in the root of this repository | ||
# for complete details. | ||
|
||
from __future__ import absolute_import, division, print_function | ||
|
||
from enum import Enum | ||
|
||
from cryptography import x509 | ||
from cryptography.hazmat.backends import _get_backend | ||
from cryptography.hazmat.primitives import hashes, serialization | ||
from cryptography.hazmat.primitives.asymmetric import ec, rsa | ||
from cryptography.utils import _check_byteslike | ||
|
||
|
||
class SMIMESignatureBuilder(object): | ||
def __init__(self, data=None, signers=[]): | ||
self._data = data | ||
self._signers = signers | ||
|
||
def set_data(self, data): | ||
_check_byteslike("data", data) | ||
if self._data is not None: | ||
raise ValueError("data may only be set once") | ||
reaperhulk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
return SMIMESignatureBuilder(data, self._signers) | ||
|
||
def add_signer(self, certificate, private_key, hash_algorithm): | ||
if not isinstance( | ||
hash_algorithm, | ||
( | ||
hashes.SHA1, | ||
hashes.SHA224, | ||
hashes.SHA256, | ||
hashes.SHA384, | ||
hashes.SHA512, | ||
), | ||
): | ||
raise TypeError( | ||
"hash_algorithm must be one of hashes.SHA1, SHA224, " | ||
"SHA256, SHA384, or SHA512" | ||
) | ||
if not isinstance(certificate, x509.Certificate): | ||
raise TypeError("certificate must be a x509.Certificate") | ||
|
||
if not isinstance( | ||
private_key, (rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey) | ||
): | ||
raise TypeError("Only RSA & EC keys are supported at this time.") | ||
|
||
return SMIMESignatureBuilder( | ||
self._data, | ||
self._signers + [(certificate, private_key, hash_algorithm)], | ||
) | ||
|
||
def sign(self, encoding, options, backend=None): | ||
reaperhulk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if len(self._signers) == 0: | ||
raise ValueError("Must have at least one signer") | ||
if self._data is None: | ||
raise ValueError("You must add data to sign") | ||
options = list(options) | ||
if not all(isinstance(x, SMIMEOptions) for x in options): | ||
raise ValueError("options must be from the SMIMEOptions enum") | ||
if ( | ||
encoding is not serialization.Encoding.PEM | ||
and encoding is not serialization.Encoding.DER | ||
): | ||
raise ValueError("Must be PEM or DER from the Encoding enum") | ||
|
||
# Text is a meaningless option unless it is accompanied by | ||
# DetachedSignature | ||
if ( | ||
SMIMEOptions.Text in options | ||
and SMIMEOptions.DetachedSignature not in options | ||
): | ||
raise ValueError( | ||
"When passing the Text option you must also pass " | ||
"DetachedSignature" | ||
) | ||
|
||
if ( | ||
SMIMEOptions.Text in options | ||
and encoding is serialization.Encoding.DER | ||
): | ||
raise ValueError( | ||
"The Text option does nothing when serializing to DER" | ||
) | ||
|
||
# No attributes implies no capabilities so we'll error if you try to | ||
# pass both. | ||
if ( | ||
SMIMEOptions.NoAttributes in options | ||
and SMIMEOptions.NoCapabilities in options | ||
): | ||
raise ValueError( | ||
"NoAttributes is a superset of NoCapabilities. Do not pass " | ||
"both values." | ||
) | ||
|
||
backend = _get_backend(backend) | ||
return backend.smime_sign(self, encoding, options) | ||
|
||
|
||
class SMIMEOptions(Enum): | ||
Text = "Add text/plain MIME type" | ||
Binary = "Don't translate input data into canonical MIME format" | ||
DetachedSignature = "Don't embed data in the PKCS7 structure" | ||
NoCapabilities = "Don't embed SMIME capabilities" | ||
NoAttributes = "Don't embed authenticatedAttributes" |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.