NAME

Mail::Make::SMIME - S/MIME signing and encryption for Mail::Make (RFC 5751)

SYNOPSIS

use Mail::Make;

my $mail = Mail::Make->new;
$mail->from( 'jacques@example.com' );
$mail->to( 'recipient@example.com' );
$mail->subject( 'Signed message' );
$mail->plain( 'This message is signed.' );

# Sign only
my $signed = $mail->smime_sign(
    Cert => '/path/to/my.cert.pem',
    Key  => '/path/to/my.key.pem',
) || die $mail->error;
$signed->smtpsend( Host => 'smtp.example.com' );

# Encrypt only
my $encrypted = $mail->smime_encrypt(
    RecipientCert => '/path/to/recipient.cert.pem',
) || die $mail->error;

# Sign then encrypt
my $protected = $mail->smime_sign_encrypt(
    Cert          => '/path/to/my.cert.pem',
    Key           => '/path/to/my.key.pem',
    RecipientCert => '/path/to/recipient.cert.pem',
) || die $mail->error;

# Using the Mail::Make::SMIME object directly
use Mail::Make::SMIME;
my $smime = Mail::Make::SMIME->new(
    cert => '/path/to/my.cert.pem',
    key  => '/path/to/my.key.pem',
) || die Mail::Make::SMIME->error;

my $signed = $smime->sign( entity => $mail ) || die $smime->error;

VERSION

v0.1.2

DESCRIPTION

Mail::Make::SMIME provides S/MIME signing, encryption, and combined sign-then-encrypt operations for Mail::Make objects, following RFC 5751 (S/MIME Version 3.2).

It delegates cryptographic operations to Crypt::SMIME, which wraps the OpenSSL libcrypto library. All certificates and keys must be supplied in PEM format, either as strings or as file paths.

MEMORY USAGE AND LIMITATIONS

In-memory processing

All cryptographic operations performed by this module load the complete serialised message into memory before signing or encrypting it. This is a consequence of two factors:

1. Crypt::SMIME API

Crypt::SMIME accepts and returns plain Perl strings. It does not expose a streaming or filehandle-based interface.

2. Protocol constraints

Signing requires computing a cryptographic hash (e.g. SHA-256) over the entire content to be signed. Although the hash algorithm itself is sequential and could theoretically operate on a stream, the resulting multipart/signed structure must carry the original content followed by the detached signature. The signature cannot be emitted until the complete content has been hashed, which means either buffering the whole message in memory or reading it twice (once to hash, once to emit) — the latter requiring a temporary file.

Encryption uses a symmetric cipher (AES by default) operating on PKCS#7 EnvelopedData. The ASN.1 DER encoding of EnvelopedData declares the total length of the encrypted payload in the structure header, which must be known before the first byte is emitted. Streaming without a temporary file is therefore not possible with standard PKCS#7.

Practical impact

For typical email messages, such as plain text, HTML, and modest attachments, memory consumption is not a concern. Problems may arise with very large attachments (tens of megabytes or more).

Future work

A future v0.2.0 of Mail::Make::SMIME may optionally delegate to the openssl smime command-line tool via IPC::Run, using temporary files, to support large messages without holding them in memory. This mirrors the approach already used by Mail::Make::GPG.

If in-memory processing is a concern for your use case, consider using Mail::Make::GPG instead: OpenPGP uses partial body packets (RFC 4880 §4.2.2) which allow true streaming without knowing the total message size in advance.

CONSTRUCTOR

new( %opts )

my $smime = Mail::Make::SMIME->new(
    cert         => '/path/to/cert.pem',
    key          => '/path/to/key.pem',
    key_password => 'secret',    # or CODE ref
    ca_cert      => '/path/to/ca.pem',
);

All options are optional at construction time and can be overridden per method call.

METHODS

ca_cert( [$pem_or_path] )

Gets or sets the CA certificate used for signature verification.

cert( [$pem_or_path] )

Gets or sets the signer certificate.

encrypt( entity => $mail, RecipientCert => $cert [, %opts] )

Encrypts $mail for one or more recipients. Returns a new Mail::Make object whose entity is a application/pkcs7-mime; smime-type=enveloped-data message.

RecipientCert may be a PEM string, a file path, or an array reference of either, for multi-recipient encryption.

key( [$pem_or_path] )

Gets or sets the private key.

key_password( [$string_or_coderef] )

Gets or sets the private key passphrase.

sign( entity => $mail [, %opts] )

Signs $mail with a detached S/MIME signature and returns a new Mail::Make object whose entity is a multipart/signed message.

The signature is always detached (smime-type=signed-data with Content-Type: multipart/signed), which allows non-S/MIME-aware clients to read the message body.

Options (all override constructor defaults):

Cert => $pem_string_or_path

Signer certificate in PEM format.

Key => $pem_string_or_path

Private key in PEM format.

KeyPassword => $string_or_coderef

Passphrase for an encrypted private key, or a CODE ref that returns one.

CACert => $pem_string_or_path

CA certificate(s) to include in the signature for chain verification.

sign_encrypt( entity => $mail, RecipientCert => $cert [, %opts] )

Signs $mail then encrypts the signed result. Accepts all options of both "sign" and "encrypt".

DEPENDENCIES

Crypt::SMIME (XS module wrapping OpenSSL libcrypto).

SEE ALSO

Mail::Make, Mail::Make::GPG, Crypt::SMIME

RFC 5751 - Secure/Multipurpose Internet Mail Extensions (S/MIME) Version 3.2

RFC 4880 - OpenPGP Message Format (partial body length packets, §4.2.2)

RFC 5652 - Cryptographic Message Syntax (CMS / PKCS#7 EnvelopedData)

AUTHOR

Jacques Deguest <jack@deguest.jp>

COPYRIGHT & LICENSE

Copyright(c) 2026 DEGUEST Pte. Ltd.

All rights reserved.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.