Skip to content
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

Signed S/MIME message has CRLF termination, whereas OpenSSL uses LF #10501

Open
1 task done
facutuesca opened this issue Feb 28, 2024 · 0 comments
Open
1 task done

Signed S/MIME message has CRLF termination, whereas OpenSSL uses LF #10501

facutuesca opened this issue Feb 28, 2024 · 0 comments

Comments

@facutuesca
Copy link
Contributor

facutuesca commented Feb 28, 2024

Description

Signed S/MIME message using cryptography:

b'MIME-Version: 1.0\r\nContent-Type: multipart/signed; protocol="application/x-pkcs7-signature"; micalg="sha-512"; boundary="===============3031221955182774630=="\r\n\r\nThis is an S/MIME signed message\r\n\r\n....'

Signed S/MIME message using OpenSSL 3.2.0:

b'MIME-Version: 1.0\nContent-Type: multipart/signed; protocol="application/x-pkcs7-signature"; micalg="sha-256"; boundary="----E9313AF891A4913F5C5CCBBD0E0E0B80"\n\nThis is an S/MIME signed message\n\n...'

The cryptography output has CRLF line endings, while the OpenSSL output has LF line endings.

How to reproduce

Here is code to generate the outputs:

For OpenSSL:

$ echo "message" > message.txt
$ openssl req -x509 -newkey rsa:4096 -keyout signer_key.pem -out signer.pem -sha256 -days 365 -nodes -subj "/C=US/ST=AAA/L=BBB/O=CCC/CN=www.example.com"

$ openssl smime -sign -in message.txt -text  -signer signer.pem -inkey signer_key.pem -out out.txt

For cryptography:

from cryptography.hazmat.primitives.serialization import load_pem_private_key, pkcs7, Encoding
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes
from cryptography.x509 import load_pem_x509_certificate

with open('signer_key.pem', 'rb') as key_data:
    key = load_pem_private_key(key_data.read(), password=None)
with open('signer.pem', 'rb') as cert_data:
    cert = load_pem_x509_certificate(cert_data.read())

with open('message.txt', 'rb') as data:
    output = pkcs7.PKCS7SignatureBuilder().set_data(
      b'message'
    ).add_signer(
        cert, key, hashes.SHA512(), rsa_padding=padding.PKCS1v15()
    ).sign(
        Encoding.SMIME, [pkcs7.PKCS7Options.DetachedSignature, pkcs7.PKCS7Options.Text]
    )
print(output)

Extra context

When encoding a signed PKCS7/SMIME message, OpenSSL usually “canonicalizes” the data to be signed by using CRLF as EOL. The hash is then computed over the canonicalized data. Having done that, it emits the signed data with all the corresponding headers:

$ openssl smime -sign -in message.txt -text  -signer signer.pem -inkey signer_key.pem
MIME-Version: 1.0
Content-Type: multipart/signed; protocol="application/x-pkcs7-signature"; micalg="sha-256"; boundary="----7908462305F5532112445928685D0F77"

This is an S/MIME signed message
.....
(signed data)
.....

Note, however, that the output itself has normal LF terminations. The LF->CRLF transformation was only applied to the input of the hash function, not to the rest of the message.

The inconsistency I noticed is that cryptography not only (correctly) canonicalizes the input data to the hash function, but it also emits the entire message using CRLF.

The end result is that OpenSSL outputs messages with LF termination, whereas cryptography outputs them with CRLF instead.

TODO

  • See if running the same OpenSSL command on Windows outputs LF or CRLF terminations.
    On Windows, OpenSSL outputs CRLF terminations, meaning the output certificate has different terminations depending on the OS.
@alex alex added this to the Forty Third Release milestone Mar 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants