NAME

Mail::GPG - Handling of GnuPG encrypted / signed mails

SYNOPSIS

use Mail::GPG;

my $mg = Mail::GPG->new;

my $key_id = $mg->query_keyring (
  search => 'joern@zyn.de',
);

my $entity = MIME::Entity->build (
  From     => 'joern@zyn.de',
  Subject  => "Mail::GPG Testmail",
  Data     => [ "Hiho, a nice encrypted mail" ],
  Encoding => "quoted-printable",
  Charset  => "iso-8859-1",
);

my $encrypted_entity = $mg->mime_sign_encrypt (
  entity     => $entity,
  key_id     => $key_id,
  password   => 'topsecret',
  recipients => [ 'niceguy@zyn.de' ],
);

my $mail_text = $encrypted_entity->as_string;

# and a lot more...

$mg->mime_sign ( ... );
$mg->mime_encrypt ( ... );
$mg->mime_sign_encrypt ( ... );

$mg->armor_sign ( ... );
$mg->armor_encrypt ( ... );
$mg->armor_sign_encrypt ( ... );

$mg->decrypt ( ... );
$mg->verify ( ... );

$mg->is_encrypted ( ... );
$mg->is_signed ( ... );

$mg->get_decrypt_key ( ... );

DESCRIPTION

This Perl modules handles all the details of encrypting and signing Mails using GnuPG according to RFC 3156 and RFC 2440, that is OpenPGP MIME and traditional armor signed/encrypted mails.

PREREQUISITES

Perl              >= 5.00503
GnuPG::Interface  >= 0.33
MIME-tools        == 5.411 (with shipped patch applied)
MIME::QuotedPrint >= 2.20  (part of MIME-Base64 distribution)

INSTALLATION

First get MIME-tools 5.411 and extract it, e.g. on the same level where you extracted the Mail::GPG tarball.

% tar xvfz Mail-GPG-x.xx.tar.gz
% tar xvfz MIME-tools-5.411.tar.gz

Apply the MIME-tools patch shipped with this module and build and install the MIME-tools package (Mail::GPG works without this patch, but it's strongly suggested, that you apply it. Refer to the next chapter for details):

% cd MIME-tools-5.411
% patch -p1 < ../Mail-GPG.x.xx/patches/MIME-tools-5.411.enc.txt
% perl Makefile.PL
% make test
% make install

Make sure that the gpg program is installed and can be found using your standard PATH.

Then install Mail::GPG

% cd ../Mail-GPG-x.xx
% perl Makefile.PL
% make test
% make install

Mail::GPG has a bunch of tests which will create a temporary gpg keyring to be able to do real encryption and stuff. You need to have gpg in your path for the tests to succeed, otherwise all useful tests will be skipped.

Note that the test 04.big needs some time, on an Athlon 1800XP about 12 seconds, so be patient ;)

MIME-tools PATCH

Some words about MIME-tools: MIME::Entity internally stores all data in decoded form, that is without any content transfer encoding like quoted-printable or base64 applied. In particular if you parse with MIME::Parser, e.g. a MIME signed mail, the entity will always be stored that way.

But RFC 3156 requires the encoded version of the MIME entity, because the signature is calculated based on the encoded form. Some content transfer encodings are ambigious and you can't reverse the process and get back the correct encoded version without breaking the signature.

The shipped MIME-tools patch adds the ability of having encoded data in a MIME::Entity object and a method to advise MIME::Parser so use this ability and store the parsed data in encoded form.

Mail::GPG generally works without this patch, but it's strongly suggested that you apply it. Otherwise you have no guarantee that MIME signed messages are verified correctly by Mail::GPG.

Unfortunately the maintainer of MIME-tools currently seeks for a new maintainer and stopped development, so there is no chance to get the patch into an official CPAN version of MIME-tools. That's why you have to apply the patch manually.

WHY ANOTHER GnuPG MAIL MODULE?

I know the Mail::GnuPG module. I worked a long time with it and submitted a few patches adding features and fixing bugs. The problems with MIME signed messages mentioned above led me to my own implementation. In the meantime I know, that regarding the implemented RFC's Mail::GnuPG works as correct as Mail::GPG does. Only that Mail::GnuPG's documentation is not aware of these MIME signature problems resp. encoded vs decoded data storage.

I like clean OO interfaces and well documented source code. With Mail::GnuPG you need to access internal data structures from outside (e.g. things like gpg's last output). Also Mail::GnuPG modifies the MIME::Entity objects you pass to it, which is bad in some situations. Mail::GPG has some more features, e.g. multiplexed I/O with the gpg program, which makes it work even with huge amounts of data.

Last but not least it was simply more fun for me to fix my own bugs in my own code and to learn all the details by making my own faults. And fun is important for an Open Source programmer, in particular for me ;)

So it's up to you: you have the choice, not too bad at all, not? ;)

KNOWN BUGS

Currently none. Please report any bugs to the author: Joern Reder <joern AT zyn.de>.

EXAMPLES

The Mail::GPG distribution contains the program mgpg-test:

Usage: mgpg-test file ...

It takes one or more filenames of mails as arguments, analyzes them, prints information about signatures and decrypts encrypteded mails (after asking for the correspondent passphrases). The script is rather small and a good example of Mail::GPG usage.

The regression tests in the t/ directory of the distribution show exemplary usage of all Mail::GPG features.

CONSTRUCTOR AND ATTRIBUTES

new

$mg = Mail::GPG->new (
  attribute => value,
  ...
);

The new class method returns a new instance of the Mail::GPG class, initialized with the attributes passed as hash parameters.

Attributes

This is the list of attributes you can pass to the new method and access using the methods set_attribute and get_attribute:

default_key_id

The default_key_id takes a GnuPG key id. It is used for all methods which expect a key id, if you don't pass a specific key id to them.

default_passphrase

You can store the passphrase of the default_key_id using this attribute. All methods expecting the passphrase will take it from here by default.

WARNING

Aware that storing the secret key password in many variables in your program increases the risk of being attacked by memory inspection. So you probably don't want to use the default_passphrase attribute.

debug

Setting the debug attribute to a true value will cause Mail::GPG to dump files into the debug_dir (see beyond). This way you can track entities, if signature validation or decryption fails for some reason.

debug_dir

This defaults to File::Spec->tmpdir. The directory is used to store debug files (see debug above).

gnupg_hash_init

This attribute corresponds to the GnuPG::Interface hash_init attribute. Please refer to the GnuPG::Interface manpage for details. E.g. you can set gpg's --homedir option this way and much more.

digest

This is the digest used by GnuPG to calculate hash values for signatures. By default Mail::GPG sets it to "RIPEMD160", which is needed to handle DSA keys (which are very common). You can check the supported digests of your gpg installation by executing 'gpg --version'.

default_key_encrypt

Set this attribute to a true value if you whish to have the default_key_id always added as a recipient for encrypted mails.

no_strict_7bit_encoding

By default this attribute is false, that means that all data which should be signed or encrypted is firstly checked for a RFC 3156 conform 7bit encoding. Until you set no_strict_7bit_encoding to true, an exception will be raised for non 7bit transparent encodings.

gpg_call

This defaults to 'gpg' and is the path of the gpg program executed through GnuPG::Interface. Change this attribute if the 'gpg' program is not in your PATH.

METHODS FOR MIME OpenPGP (RFC 3156)

mime_sign

$signed_entity = $mg->mime_sign (
    entity     => $entity,
  [ key_id     => $key_id,
    passphrase => $passphrase ]
);

This method returns the MIME signed version of an entity.

entity

The MIME::Entity object to be signed. By default it must not contain any parts with non 7bit content transfer encodings, because RFC 3156 forbids that. If you want to be able to pass 8bit also (and thus create non RFC conform data), you have to set the no_strict_7bit_encoding attribute.

key_id

The id of the key used to sign the entity. This defaults to default_key_id if omitted here.

passphrase

The corresponding passphrase of the key. This defaults to default_passphrase if omitted here.

mime_encrypt

$encrypted_entity = $mg->mime_encrypt (
    entity     => $entity,
    recipients => \@recipients,
);

Returns the MIME encrypted version of an entity.

entity

The MIME::Entity object to be encrypted. By default it must not contain any parts with non 7bit content transfer encodings, because RFC 3156 forbids that. If you want to be able to pass 8bit also (and thus create non RFC conform data), you have to set the no_strict_7bit_encoding attribute.

recipients

This is a reference to an array of recipients, which may be email adresses or key id's. If default_key_encrypt is set, the default_key_id will be added as a recipient automatically.

mime_sign_encrypt

$encrypted_signed_entity = $mg->mime_sign_encrypt (
    entity     => $entity,
    recipients => \@recipients,
  [ key_id     => $key_id,
    passphrase => $passphrase ]
);

Returns the encrypted and signed version of an entity.

entity

The MIME::Entity object to be signed and encrypted. By default it must not contain any parts with non 7bit content transfer encodings, because RFC 3156 forbids that. If you want to be able to pass 8bit also (and thus create non RFC conform data), you have to set the no_strict_7bit_encoding attribute.

recipients

This is a reference to an array of recipients, which may be email adresses or key id's. If default_key_encrypt is set, the default_key_id will be added as an recipient automatically.

key_id

The id of the key used to sign the entity. This defaults to default_key_id if omitted here.

passphrase

The corresponding passphrase of the key. This defaults to default_passphrase if omitted here.

METHODS FOR ARMOR OpenPGP

armor_sign

$signed_entity = $mg->armor_sign (
    entity     => $entity,
  [ key_id     => $key_id,
    passphrase => $passphrase ]
);

This method returns the armor signed version of a MIME::Entity.

entity

The MIME::Entity object to be signed. It must not have any parts and a 7bit clean content transfer encoding.

key_id

The id of the key used to sign the entity. This defaults to default_key_id if omitted here.

passphrase

The corresponding passphrase of the key. This defaults to default_passphrase if omitted here.

armor_encrypt

$signed_entity = $mg->armor_encrypt (
    entity     => $entity,
    recipients => \@recipients,
);

Returns the armor encrypted version of an entity.

entity

The MIME::Entity object to be encrypted. It must not have any parts and a 7bit clean content transfer encoding.

recipients

This is a reference to an array of recipients, which may be email adresses or key id's. If default_key_encrypt is set, the default_key_id will be added as an recipient automatically.

armor_sign_encrypt

$signed_entity = $mg->mime_sign_encrypt (
    entity     => $entity,
    recipients => \@recipients,
  [ key_id     => $key_id,
    passphrase => $passphrase ]
);

Returns the encrypted and signed version of an entity.

entity

The MIME::Entity object to be encrypted. It must not have any parts and a 7bit clean content transfer encoding.

recipients

This is a reference to an array of recipients, which may be email adresses or key id's. If default_key_encrypt is set, the default_key_id will be added as an recipient automatically.

key_id

The id of the key used to sign the entity. This defaults to default_key_id if omitted here.

passphrase

The corresponding passphrase of the key. This defaults to default_passphrase if omitted here.

METHODS FOR DECRYPTION AND VERIFICATION

decrypt

($decrypted_entity, $result) = $mg->decrypt (
    entity     => $entity,
  [ passphrase => $passphrase ]
);

Retuns the decrypted version of an entity and a Mail::GPG::Result object with detailed information about the entities encryption (refer to the manpage of Mail::GPG::Result).

entity

The MIME::Entity to be decrypted.

Important: if you got this MIME::Entity from MIME::Parser, make sure that you set $parser->decode_bodies(0) before parsing the mail. Otherwise it's likely that decryption fails.

passphrase

The corresponding passphrase of the secret key which is needed to decrypt the message. Use get_decrypt_key to determine the corresponding key. This defaults to default_passphrase if omitted here.

verify

$result = $mg->verify (
    entity => $entity,
);

Returns a Mail::GPG::Result object with detailed information about the signature of an entity. Refer to the manpage of Mail::GPG::Result.

entity

The signed MIME::Entity to be verified.

Important: unfortunately we have a problem here with MIME::Parser and MIME::Entity. If the mail in question is text/plain and contains an ASCII armor signed PGP message, Mail::GPG must see the decoded data.

But if it's a MIME PGP signed message, Mail::GPG needs the encoded data.

With the shipped MIME-tools patch you can advice MIME::Parser to create an encoded entity (be default it creates decoded entities and encodes them on demand). You can activate this transparent encoding mode with the decode_bodies attribute of MIME::Parser, which defaults to 1:

$parser = MIME::Parser->new;
$parser->decode_bodies(0);

So you need to set decode_bodies(0) for MIME signed messages and keep the default of decode_bodies(1) for armor signed messages. But how can you know in advance which is right without having the entity parsed already? You can't!

One possible solution is to parse the entity twice if it's MIME signed, and keep the decoded version from the first parse run otherwise, or you do some quick analysis on the data in question, without really parsing it.

$parser = MIME::Parser->new;
$parser->decode_bodies(0);
$entity = $parser->parse_data($mail_data);
if ( $entity->effective_type eq 'multipart/signed' ) {
  $parser->decode_bodies(0);
  $entity = $parser->parse_data($mail_data);
}

I know this is a bad thing, but a conceptional problem with MIME::Parser and MIME::Entity. It's Ok for most situations that they store decoded data, but for our purpose it's bad. That's the way it is.

METHODS FOR ENTITY INSPECTION

is_signed

$signed = $mg->is_signed (
    entity => $entity,
);

Returns whether an entity is signed or not.

entity

The entity to be checked for a signature.

is_encrypted

$encrypted = $mg->is_encrypted (
    entity     => $entity,
);

Return whether an entity is encrypted or not.

entity

The entity to be checked for a encryption.

get_decrypt_key

($key_id, $key_mail) = $mg->get_decrypt_key (
    entity => $entity,
);

Returns secret key id and mail address which is needed to decrypt an encrypted entity.

entity

The entity to inspect.

METHODS FOR KEY RING INSPECTION

query_keyring

($key_id, $key_mail) = $mg->query_keyring (
    search => $search,
);

Searches the keyring for a key id or email address and returns key id and mail address of the first matching entry. If you need more detailed control about the query result, use GnuPG::Interface->get_public_keys and GnuPG::Interface->get_secret_keys instead. For details refer to the GnuPG::PrimaryKey manpage.

If you use Perl 5.8.0 or better the email address will be returned as an utf8 enabled scalar, because gpg always lists email adresses in utf8. Since Perl > 5.8.0 handles utf8 very nice and transparently, you mostly don't need to care about this ;)

If you use the module with older Perl versions you need to handle utf8 encoded data yourself.

Key id or email address to query for.

AUTHOR

Joern Reder <joern AT zyn.de>

COPYRIGHT

Copyright (C) 2004 by Joern Reder, All Rights Reserved.

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

SEE ALSO

Mail::GPG::Result, perl(1).