Convert-SSH2

 view release on metacpan or  search on metacpan

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

package Convert::SSH2;

use 5.010;
use strict;
use warnings;

use Moo;
use MIME::Base64 qw(decode_base64);
use File::Slurp qw(read_file write_file);
use Carp qw(confess);
use Try::Tiny;
use Class::Load qw(load_class);
use Math::BigInt try => 'GMP';

=head1 NAME

Convert::SSH2 - Convert SSH2 RSA keys to other formats

=head1 VERSION

Version 0.01

=cut

our $VERSION = '0.01';

=head1 SYNOPSIS

    use 5.010;
    use Convert::SSH2;

    my $converter = Convert::SSH2->new('~/.ssh/id_rsa.pub');
    # Automatically calls parse()

    # Use default PKCS#1 format
    say $converter->format_output();

    $converter->write('/my/pub/key.pem');


=head1 PURPOSE

This library converts SSH2 style RSA public keys to other representations like PKCS#1.
This is useful if you want to use these public keys with other Perl cryptography 
libraries like L<Crypt::RSA> or L<Crypt::OpenSSL::RSA>.

=head1 ATTRIBUTES

=over

=item key

Required. Read-only.  The key material.  Attempts to be DWIMish. If this is a file path,
it will be used to load the file contents into memory.  If it's a buffer, it will use
the buffer contents.

=back

=cut

has 'key' => (
    is => 'ro',
    required => 1,
);

=over

=item format

Read-only. The output format. Current supports:

=over

=item * pkcs1

This format looks like

  -----BEGIN RSA PUBLIC KEY-----
  ...
  -----END RSA PUBLIC KEY-----

=item * pkcs8

This format looks like

  -----BEGIN PUBLIC KEY-----
  ...
  -----END PUBLIC KEY-----

=back

You can add your own format by implementing a L<Convert::SSH2::Format::Base> module.

=back

=cut

has 'format' => (
    is => 'ro',
    isa => sub {
        my $n = shift;
        confess "$n is not a supported format." unless 
            grep { $n eq $_ } qw(
                pkcs1
                pkcs8
            );
    },
    default => sub { 'pkcs1' },
);

has '_buffer' => (
    is => 'rw',
);

has '_output' => (
    is => 'rw',
    predicate => '_has_output',
);

has '_e' => (
    is => 'rw',
);

has '_n' => (
    is => 'rw',
);

=head1 METHODS 

Generally, errors are fatal.  Use L<Try::Tiny> if you want more graceful error handling.

=over

=item new()

Constructor. Takes any of the attributes as arguments.  You may optionally call new
with either a buffer or a path, and the class will assume that it is the C<key>
material.

The object automatically attempts to parse C<key> data after instantiation.

=back

=cut 

# Support single caller argument
around BUILDARGS => sub {
    my $orig = shift;
    my $class = shift;

    if ( @_ == 1 ) {
        unshift @_, "key";
    }

    $class->$orig(@_);
};

sub BUILD {
    my $self = shift;

    my $buf;
    unless ( $self->key =~ /\n/ ) {
        if ( -e $self->key ) {
            $buf = read_file($self->key, { binmode => ':raw' });
        }
        else {
            $buf = $self->key;
        }
    }
    else {
        $buf = $self->key;
    }

    $buf =~ s/\n//g;
    $self->_buffer( (split / /, $buf)[1] );

    $self->parse();
}

=over 

=item parse()

This method takes the Base64 encoded portion of the SSH key, decodes it, and then converts the
data inside of it into three components: the id string ('ssh-rsa'), the public exponent ('e'),
and the modulus ('n'). By default it looks for the Base64 data inside the instantiated object, 
but you can optionally pass in a Base64 string.

It uses L<Math::BigInt> to hold large integers such as 'n' or 'e'. If you don't have 
C<libgmp> installed, it will fall back to pure perl automatically, but there will be a speed 



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