Convert-PEM

 view release on metacpan or  search on metacpan

lib/Convert/PEM.pm  view on Meta::CPAN

sub encode {
    my $pem = shift;
    my %param = @_;

    my $buf = $param{DER} || $pem->to_der(%param);
    my (@headers);
    if ($param{Password}) {
        my ($info);
        ($buf, $info) = $pem->encrypt( Plaintext => $buf,
                                       %param )
            or return;
        push @headers, [ 'Proc-Type' => '4,ENCRYPTED' ];
        push @headers, [ 'DEK-Info'  => $info ];
    }

    $pem->implode( Object  => $param{Name} || $pem->name,
                   Headers => \@headers,
                   Content => $buf );
}

sub explode {
    my $pem = shift;
    my ($message) = @_;

    # Canonicalize line endings into "\n".
    $message =~ s/\r\n|\n|\r/\n/g;

    my ($head, $object, $headers, $content, $tail) = $message =~ 
        m:(-----BEGIN ([^\n\-]+)-----)\n(.*?\n\n)?(.+)(-----END .*?-----)$:s;
    my $buf = decode_base64($content);

    my @headers;
    if ($headers) {
        for my $h ( split /\n/, $headers ) {
            my ($k, $v) = split /:\s*/, $h, 2;
            push @headers, [ $k => $v ] if $k;
        }
    }

    { Content => $buf,
      Object  => $object,
      Headers => \@headers }
}

sub implode {
    my $pem = shift;
    my %param = @_;
    my $head = "-----BEGIN $param{Object}-----"; 
    my $tail = "-----END $param{Object}-----";
    my $content = encode_base64( $param{Content}, '' );
    $content =~ s!(.{1,64})!$1\n!g;
    my $headers = join '',
                  map { "$_->[0]: $_->[1]\n" }
                  @{ $param{Headers} };
    $headers .= "\n" if $headers;
    "$head\n$headers$content$tail\n";
}

use vars qw( %CTYPES );
%CTYPES = (
    'DES-CBC'           =>    {c => 'Crypt::DES',         ks=>8,  bs=>8,  },
    'DES-EDE3-CBC'      =>    {c => 'Crypt::DES_EDE3',    ks=>24, bs=>8,  },
    'AES-128-CBC'       =>    {c => 'Crypt::Rijndael',    ks=>16, bs=>16, },
    'AES-192-CBC'       =>    {c => 'Crypt::Rijndael',    ks=>24, bs=>16, },
    'AES-256-CBC'       =>    {c => 'Crypt::Rijndael',    ks=>32, bs=>16, },
    'CAMELLIA-128-CBC'  =>    {c => 'Crypt::Camellia',    ks=>16, bs=>16, },
    'CAMELLIA-192-CBC'  =>    {c => 'Crypt::Camellia',    ks=>24, bs=>16, },
    'CAMELLIA-256-CBC'  =>    {c => 'Crypt::Camellia',    ks=>32, bs=>16, },
    'IDEA-CBC'          =>    {c => 'Crypt::IDEA',        ks=>16, bs=>8,  },
    'SEED-CBC'          =>    {c => 'Crypt::SEED',        ks=>16, bs=>16, },
);

#### cipher module support and configuration
sub list_ciphers { return wantarray ? sort keys %CTYPES : join(':', sort keys %CTYPES); }

sub list_cipher_modules {
    # expect a cipher name, if found, return the module name used for encryption/decryption
    my $pem = ref($_[0]) || $_[0] eq __PACKAGE__ ? shift : '';
    if (defined $_[0]) {
        my $cn = has_cipher(shift) || return undef;
        return $CTYPES{$cn}->{c};
    }
    return wantarray
        ? map { $CTYPES{$_}->{c} } sort keys %CTYPES
        : join(':', map { $CTYPES{$_}->{c} } sort keys %CTYPES);
}

sub has_cipher {
    # expect a cipher name, return the cipher name if found
    my $pem = ref($_[0]) || $_[0] eq __PACKAGE__ ? shift : '';
    my $cn = uc(+shift);
    return $cn if exists $CTYPES{$cn} && exists $CTYPES{$cn}->{c};
    # try to figure out what cipher is meant in an overkill fashion
    $cn =~ s/(DES.*3|3DES|EDE)|(DES)|([a-zA-Z]+)(?:-?(\d+)(?:-?(\w+))?)/
        if ($1) {
            'DES-EDE3-CBC'
        } elsif ($2) {
            'DES-CBC'
        }
        else {
            $3.($4 ? "-".$4 : "").($5 ? "-$5" : "")
        }
    /e;
    my @c = sort grep { $_ =~ m/$cn/ } keys %CTYPES;
    # return undef unless @c;
    $c[0];
}

sub has_cipher_module
{
    my $pem = ref($_[0]) || $_[0] eq __PACKAGE__ ? shift : '';
    if (my $cn = has_cipher($_[0])) {
        eval "use $CTYPES{$cn}->{c};";
        if ($@) { undef $@; return undef; }
        return $CTYPES{$cn}->{c};
    }
}

sub set_cipher_module
{
    my $pem = ref($_[0]) || $_[0] eq __PACKAGE__ ? shift : '';
    # cipher name, cipher module name, replace all

lib/Convert/PEM.pm  view on Meta::CPAN


=back

=head2 Convert::PEM->has_cipher(I<$cipher_name>)

Will see if the cipher is supported and is configured with an encryption
module.

=head2 Convert::PEM->has_cipher_module(I<$cipher_name>)

Will see if the cipher is supported and if the configured encryption
module is usable.  If it is not usable, will return C<undef>.  If it is
usable, will return the name of the cipher module.

=head2 Convert::PEM->set_cipher_module($cipher,$module[,$all])

This function/method  is used to specify a module name for a supported
cipher.  It accepts 2 or 3 arguments.

    Convert::PEM->set_cipher_module(<cipher_name>, <module_name>[,0])
    or
    $OBJ->set_cipher_module(<cipher_name>, <module_name>[,0])

=over 4

=item C<cipher_name>

A supported cipher name. Use Convert::PEM::list_ciphers() to retrieve a
list of supported ciphers.

=item C<module_name>

A cipher module.  The module must support the following methods:

    $cipher_object = Cipher->new($key)
    $cipher_object->encrypt($plaintext)
    $cipher_object->decrypt($ciphertext)
    $cipher_object->blocksize()

=item C<all>

An optional boolean argument.  If true will replace the modules for all
supported ciphers matching the cipher being set.  Default is true. If
setting a cipher, only set this to false if it is desired to use a
separate cipher for different key lengths of the same algorithm.

=back

=head2 Convert::PEM->list_cipher_modules([$cipher_name])

If a I<cipher_name> is provided, will return the module configured for
the matching cipher name or C<undef> if cipher is not supported.
If I<cipher_name> is not provided, will return a list of modules names
configured as an array in array context or as a colon separated list in
scalar context.

Here is a list of the cipher modules used by default.

=over 4

=item * L<Crypt::DES>

=item * L<Crypt::DES_EDE3>

=item * L<Crypt::Rijndael> - C<AES-128-CBC, AES-192-CBC and AES-256-CBC>

=item * L<Crypt::Camellia> - C<CAMELLIA-128-CBC, CAMELLIA-192-CBC and CAMELLIA-256-CBC>

=item * Crypt::L<IDEA>

=item * L<Crypt::SEED>

=back

=head1 ERROR HANDLING

If an error occurs in any of the above methods, the method will return
C<undef>. You should then call the method I<errstr> to determine the
source of the error:

    $pem->errstr

In the case that you do not yet have a I<Convert::PEM> object (that is,
if an error occurs while creating a I<Convert::PEM> object), the error
can be obtained as a class method:

    Convert::PEM->errstr

For example, if you try to decode an encrypted object, and you do not
give a passphrase to decrypt the object:

    my $obj = $pem->read( Filename => "encrypted.pem" )
        or die "Decryption failed: ", $pem->errstr;

=head1 LICENSE

Convert::PEM is free software; you may redistribute it and/or modify
it under the same terms as Perl itself.

=head1 AUTHOR & COPYRIGHTS

Except where otherwise noted, Convert::PEM is Copyright Benjamin
Trott, cpan@stupidfool.org. All rights reserved.

=cut



( run in 0.904 second using v1.01-cache-2.11-cpan-39bf76dae61 )