Add support for PEM- and Racoon-encoded private keys.

This commit is contained in:
Ryan Riske 2015-03-01 14:52:11 -06:00
parent e0750c46f0
commit 8a9a5f441f
2 changed files with 169 additions and 110 deletions

View File

@ -1,110 +0,0 @@
#!/usr/bin/perl -w
# Convert RSA public keys from and to various formats.
#
# Copyright © 2014 Ryan Riske <ryan@r1ske.net>
# This work is free. You can redistribute it and/or modify it under the
# terms of the Do What The Fuck You Want To Public License, Version 2,
# as published by Sam Hocevar. See the COPYING file for more details.
use strict;
use Parse::RecDescent;
use Crypt::OpenSSL::RSA;
use MIME::Base64;
use Getopt::Std;
use vars qw/ %opt /;
# Command line options processing
sub init() {
my $opts = 'hrdp';
getopts( "$opts", \%opt ) or usage();
usage() if $opt{h} or !($opt{r} or $opt{d} or $opt{p});
}
sub usage() {
print STDERR << "EOF";
This program converts RSA public keys from and to various formats.
You must specify at least one output format.
usage: $0 [-hrcp] < file
-h : print this message
-r : output public key in Base64 RFC 3110 format
-d : output public key in hexadecimal DER format
-p : output public key in PEM format
EOF
exit;
}
sub input_pem {
my $key = shift;
return Crypt::OpenSSL::RSA->new_public_key($key);
}
sub input_rfc {
my $key = shift;
my $decoded = decode_base64($key);
my $len = unpack("C", substr($decoded, 0, 1));
my $e = Crypt::OpenSSL::Bignum->new_from_bin(substr($decoded, 1, $len));
my $n = Crypt::OpenSSL::Bignum->new_from_bin(substr($decoded, 1 + $len));
return Crypt::OpenSSL::RSA->new_key_from_parameters($n, $e);
}
sub input_hex {
my $key = shift;
$key =~ s/\s+//g;
my @bytes = map { pack("C", hex($_)) } ($key =~ /(..)/g);
my $encoded = encode_base64(join("", @bytes));
$encoded =~ s/\s+//g;
$encoded =~ s/(.{64})/$1\n/g;
my $pem = "-----BEGIN PUBLIC KEY-----\n" . $encoded . "\n-----END PUBLIC KEY-----\n";
return input_pem($pem);
}
sub output_rfc {
my $rsa_pub = shift;
my ($n, $e) = $rsa_pub->get_key_parameters();
my $eb = $e->to_bin();
return "0s" . encode_base64(pack("C", length($eb)) . $eb . $n->to_bin(), '') . "\n";
}
sub output_pem {
my $rsa_pub = shift;
return $rsa_pub->get_public_key_x509_string();
}
sub output_hex {
my $rsa_pub = shift;
my $key = output_pem($rsa_pub);
$key =~ s/-----BEGIN PUBLIC KEY-----(.*?)-----END PUBLIC KEY-----/$1/s;
my $hex = uc(unpack("H*", decode_base64($key)));
$hex =~ s/(.{64})/$1\n/g;
$hex =~ s/(.{8})/$1 /g;
return $hex . "\n";
}
init();
my $grammar = q {
input: item
item: pempubkey | rfcpubkey | hexpubkey | other
pempubkey: m{-----BEGIN PUBLIC KEY-----.*?-----END PUBLIC KEY-----}s
{ $return = ::input_pem($item[1]); }
rfcpubkey: m{0s[A-Za-z0-9+/=]+}
{ $return = ::input_rfc(substr($item[1], 2)); }
hexpubkey: m{^\s*(?:[A-Z0-9]{2}\s*)+}s
{ $return = ::input_hex($item[1]); }
other: /.*/ { undef $return; }
};
my $parser = new Parse::RecDescent($grammar);
undef $/;
my $input = <>;
my $pubkey = $parser->input($input);
if (defined $pubkey) {
print output_rfc($pubkey) if $opt{r};
print output_pem($pubkey) if $opt{p};
print output_hex($pubkey) if $opt{d};
} else {
usage();
}

169
rsa-converter Executable file
View File

@ -0,0 +1,169 @@
#!/usr/bin/perl -w
# Convert RSA keys between various formats.
#
# Copyright © 2014 Ryan Riske <ryan@r1ske.net>
# This work is free. You can redistribute it and/or modify it under the
# terms of the Do What The Fuck You Want To Public License, Version 2,
# as published by Sam Hocevar. See the COPYING file for more details.
use strict;
use Parse::RecDescent;
use Crypt::OpenSSL::RSA;
use MIME::Base64;
use Getopt::Std;
use vars qw/ %opt /;
# Command line options processing
sub init() {
my $opts = 'hrdpqs';
getopts( "$opts", \%opt ) or usage();
usage() if $opt{h} or !($opt{r} or $opt{d} or $opt{p} or $opt{q} or $opt{s});
}
sub usage() {
print STDERR << "EOF";
This program converts RSA keys between various formats.
You must specify at least one output format.
usage: $0 [-hrcpqs] < file
-h : print this message
-r : output public key in Base64 RFC 3110 format
-d : output public key in hexadecimal DER format
-p : output public key in PEM format
-q : output private key in PEM format (must supply a private key)
-s : output private key in Racoon/strongSwan < 5.0 format (must supply a private key)
EOF
exit;
}
sub input_pem {
my $key = shift;
return Crypt::OpenSSL::RSA->new_public_key($key);
}
sub input_rfc {
my $key = shift;
my $decoded = decode_base64($key);
my $len = unpack("C", substr($decoded, 0, 1));
my $e = Crypt::OpenSSL::Bignum->new_from_bin(substr($decoded, 1, $len));
my $n = Crypt::OpenSSL::Bignum->new_from_bin(substr($decoded, 1 + $len));
return Crypt::OpenSSL::RSA->new_key_from_parameters($n, $e);
}
sub input_hex {
my $key = shift;
$key =~ s/\s+//g;
my @bytes = map { pack("C", hex($_)) } ($key =~ /(..)/g);
my $encoded = encode_base64(join("", @bytes));
$encoded =~ s/\s+//g;
$encoded =~ s/(.{64})/$1\n/g;
my $pem = "-----BEGIN PUBLIC KEY-----\n" . $encoded . "\n-----END PUBLIC KEY-----\n";
return input_pem($pem);
}
sub input_pem_priv {
my $key = shift;
return Crypt::OpenSSL::RSA->new_private_key($key);
}
sub input_racoon_priv {
my $key = shift;
$key =~ /^\s+Modulus:\s+0x([0-9a-fA-F]+)$/m;
my $n = Crypt::OpenSSL::Bignum->new_from_hex($1);
$key =~ /^\s+PublicExponent:\s+0x([0-9a-fA-F]+)$/m;
my $e = Crypt::OpenSSL::Bignum->new_from_hex($1);
$key =~ /^\s+PrivateExponent:\s+0x([0-9a-fA-F]+)$/m;
my $d = Crypt::OpenSSL::Bignum->new_from_hex($1);
$key =~ /^\s+Prime1:\s+0x([0-9a-fA-F]+)$/m;
my $p = Crypt::OpenSSL::Bignum->new_from_hex($1);
$key =~ /^\s+Prime2:\s+0x([0-9a-fA-F]+)$/m;
my $q = Crypt::OpenSSL::Bignum->new_from_hex($1);
return Crypt::OpenSSL::RSA->new_key_from_parameters($n, $e, $d, $p, $q);
}
sub output_rfc {
my $rsa = shift;
my ($n, $e) = $rsa->get_key_parameters();
my $eb = $e->to_bin();
return "0s" . encode_base64(pack("C", length($eb)) . $eb . $n->to_bin(), '') . "\n";
}
sub output_pem {
my $rsa = shift;
return $rsa->get_public_key_x509_string();
}
sub output_hex {
my $rsa = shift;
my $key = output_pem($rsa);
$key =~ s/-----BEGIN PUBLIC KEY-----(.*?)-----END PUBLIC KEY-----/$1/s;
my $hex = uc(unpack("H*", decode_base64($key)));
$hex =~ s/(.{64})/$1\n/g;
$hex =~ s/(.{8})/$1 /g;
return $hex . "\n";
}
sub output_pem_priv {
my $rsa = shift;
return $rsa->get_private_key_string();
}
sub output_racoon_priv {
my $rsa = shift;
my ($n, $e, $d, $p, $q, $xp, $xq, $c) = $rsa->get_key_parameters();
my $output = ": RSA\t{\n";
$output .= "\t# RSA " . $rsa->size() * 8 . " bits\n";
$output .= "\t# for signatures only, UNSAFE FOR ENCRYPTION\n";
$output .= "\t#pubkey=" . output_rfc($rsa);
$output .= "\tModulus: 0x" . lc($n->to_hex()) . "\n";
$output .= "\tPublicExponent: 0x" . lc($e->to_hex()) . "\n";
$output .= "\t# everything after this point is secret\n";
$output .= "\tPrivateExponent: 0x" . lc($d->to_hex()) . "\n";
$output .= "\tPrime1: 0x" . lc($p->to_hex()) . "\n";
$output .= "\tPrime2: 0x" . lc($q->to_hex()) . "\n";
$output .= "\tExponent1: 0x" . lc($xp->to_hex()) . "\n";
$output .= "\tExponent2: 0x" . lc($xq->to_hex()) . "\n";
$output .= "\tCoefficient: 0x" . lc($c->to_hex()) . "\n";
$output .= "\t}\n# do not change the indenting of that \"}\"\n";
return $output;
}
init();
my $grammar = q {
input: item
item: pempubkey | rfcpubkey | hexpubkey | pemprivkey | racoonprivkey | other
pempubkey: m{-----BEGIN PUBLIC KEY-----.*?-----END PUBLIC KEY-----}s
{ $return = ::input_pem($item[1]); }
rfcpubkey: m{0s[A-Za-z0-9+/=]+}
{ $return = ::input_rfc(substr($item[1], 2)); }
hexpubkey: m{^\s*(?:[A-Z0-9]{2}\s*)+}s
{ $return = ::input_hex($item[1]); }
pemprivkey: m{-----BEGIN RSA PRIVATE KEY-----.*?-----END RSA PRIVATE KEY-----}s
{ $return = ::input_pem_priv($item[1]); }
racoonprivkey: m/:\s+RSA\s+\{.*/s
{ $return = ::input_racoon_priv($item[1]); }
other: /.*/ { undef $return; }
};
my $parser = new Parse::RecDescent($grammar);
undef $/;
my $input = <>;
my $rsa = $parser->input($input);
if (defined $rsa) {
print output_rfc($rsa) if $opt{r};
print output_pem($rsa) if $opt{p};
print output_hex($rsa) if $opt{d};
if ($opt{q} or $opt{s}) {
if ($rsa->is_private()) {
print output_pem_priv($rsa) if $opt{q};
print output_racoon_priv($rsa) if $opt{s};
} else {
print STDERR "Error: must supply a private key to use -q or -s!\n";
}
}
} else {
usage();
}