From 491a0c3801eb56760c7a4153ac22291f9de411ab Mon Sep 17 00:00:00 2001 From: "Kyle J. McKay" Date: Thu, 28 Mar 2013 05:52:17 -0700 Subject: [PATCH] Make CACreateCertClient the only tool since it does it all --- CACreateCertClient => CACreateCert | 0 CACreateCertRoot | 790 ------------------------------------- 2 files changed, 790 deletions(-) rename CACreateCertClient => CACreateCert (100%) delete mode 100755 CACreateCertRoot diff --git a/CACreateCertClient b/CACreateCert similarity index 100% rename from CACreateCertClient rename to CACreateCert diff --git a/CACreateCertRoot b/CACreateCertRoot deleted file mode 100755 index 30bdb1d..0000000 --- a/CACreateCertRoot +++ /dev/null @@ -1,790 +0,0 @@ -#!/usr/bin/env perl - -# CACreateCertRoot - Create a Root Certificate Authority -# Copyright (C) 2011 Kyle J. McKay. All rights reserved. - -exit(&main()); - -use strict; -use warnings; -use bytes; - -use MIME::Base64; -use IPC::Open2; -use Digest::MD5 qw(md5 md5_hex md5_base64); -use Getopt::Long qw(:config gnu_getopt); - -our $VERSION; -my $VERSIONMSG; -my $HELP; -my $USAGE; - -my $hasSha2; - -BEGIN { - *VERSION = \'1.1.1'; - $VERSIONMSG = "CACreateCertRoot version $VERSION\n" . - "Copyright (C) 2011 Kyle J. McKay. All rights reserved.\n"; -} - -BEGIN { - $hasSha2 = 0; - - eval { - require Digest::SHA; - Digest::SHA->import( - qw( - sha1 sha1_hex sha1_base64 - sha224 sha224_hex sha224_base64 - sha256 sha256_hex sha256_base64 - sha384 sha384_hex sha384_base64 - sha512 sha512_hex sha512_base64 - ) - ); $hasSha2=1} || - eval { - require Digest::SHA::PurePerl; - require Digest::SHA1; - Digest::SHA1->import( - qw( - sha1 sha1_hex sha1_base64 - ) - ); - Digest::SHA::PurePerl->import( - qw( - sha224 sha224_hex sha224_base64 - sha256 sha256_hex sha256_base64 - sha384 sha384_hex sha384_base64 - sha512 sha512_hex sha512_base64 - ) - ); $hasSha2=1} || - eval { - require Digest::SHA::PurePerl; - Digest::SHA::PurePerl->import( - qw( - sha1 sha1_hex sha1_base64 - sha224 sha224_hex sha224_base64 - sha256 sha256_hex sha256_base64 - sha384 sha384_hex sha384_base64 - sha512 sha512_hex sha512_base64 - ) - ); $hasSha2=1} || - eval { - require Digest::SHA1; - Digest::SHA1->import( - qw( - sha1 sha1_hex sha1_base64 - ) - ); 1} || - die "One of Digest::SHA1 or Digest::SHA or Digest::SHA::PurePerl " - . "must be available\n"; - - eval {(`openssl version -v 2>/dev/null` || '') =~ /^OpenSSL /} || - die "OpenSSL (as the openssl command) is not available in the PATH\n"; -} - -BEGIN { - $USAGE = < out_cert.pem -USAGE - $HELP = < out_cert.pem - -DESCRIPTION - CACreateCertRoot creates a new root certificate suitable for signing - and authenticating other certificates as used to connect to SSL/TLS - servers. Typically the "common name" provided will be the full DNS name - (all lowercase) of the server to which clients connect to. However, it - can be any string desired as the created certificate is NOT intended to - be usable as a server certificate or client certificate, but ONLY as a - root certificate authority. - - The "common name" value is expected to be either Latin-1 or UTF-8. - - The key_file must be an RSA private key file in PEM format and - furthermore it must not have a password (both openssl genrsa and - ssh-keygen -t rsa can create these kinds of RSA private key files). If - a host is running an OpenSSH sshd daemon, then it probably already has a - suitable host private RSA key in either /etc/ssh/ssh_host_rsa_key or - /etc/ssh_host_rsa_key that can be used if desired. - - All systems support sha1 digest certificates, but sha1 should really not - be used anymore (NIST recommendation SP 800-131A). OpenSSL starting - with versions 0.9.8 (released 2005-07-05) supports the SHA-2 family of - hash functions (sha224, sha256, sha384 and sha512) which should be used - instead. - - NIST SP 800-131A requires use of an RSA key with 2048 or more bits and - a hash function with 224 or more bits after December 31 2010. - - RFC 6194 states sha256 is the most commonly used alternative to sha1 - (and will be used by default if a suitable SHA module is available). - - Note that NIST SP 800-78-3 requires RSA public key exponents to be - greater than or equal to 65537. OpenSSH version 5.4 and later generate - RSA keys with a public exponent of 65537 otherwise openssl genrsa can - be used together with ssh-keygen -y to create a suitable OpenSSH key. - - There are a few seldom-used options not shown in the usage example - above. The --now option can be given to set the validity not before - date to the current time rather than the beginning of the X.509v3 - standard's approval. While this option will not create distinct root - certificates for the purposes of certificate chain approval, it will - cause the actual certificate data output by CACreateCertRoot to differ - each time it's run. The --random option can be given to generate a - random serial number and include it in the certificate name as well as - make it the certificate's serial number. Using this option will produce - a new, distinct, root certificate each time CACreateCertRoot is run. If - neither of these two options is used, then for any given pair of RSA - private key and "common name", CACreateCertRoot will always output a - bytewise-identical certificate which means that only the RSA private key - and common name is needed to recreate the certificate at any time. - - With the --check option, a new certificate is not output, but all the - validity checks are still run. - -TIPS - Display the currently available version of OpenSSL with: - - openssl version - - Display the currently available version of OpenSSH with: - - ssh -V -HELP -} - -sub IsUTF8($) -{ - # Return 0 if non-UTF-8 sequences present - # Return -1 if no characters > 0x7F found - # Return 1 if valid UTF-8 sequences present - use bytes; - return -1 if $_[0] !~ /[\x80-\xFF]/so; - my $l = length($_[0]); - for (my $i=0; $i<$l; ++$i) { - my $c = ord(substr($_[0],$i,1)); - next if $c < 0x80; - return 0 if $c < 0xC0 || $c >= 0xF8; - if ($c <= 0xDF) { - # Need 1 more byte - ++$i; - return 0 if $i >= $l; - my $c2 = ord(substr($_[0],$i,1)); - return 0 if $c2 < 0x80 || $c2 > 0xBF; - my $u = (($c & 0x1F) << 6) | ($c2 & 0x3F); - return 0 if $u < 0x80; - next; - } - if ($c <= 0xEF) { - # Need 2 more bytes - $i += 2; - return 0 if $i >= $l; - my $c2 = ord(substr($_[0],$i-1,1)); - return 0 if $c2 < 0x80 || $c2 > 0xBF; - my $c3 = ord(substr($_[0],$i,1)); - return 0 if $c3 < 0x80 || $c3 > 0xBF; - my $u = (($c & 0x0F) << 12) | (($c2 & 0x3F) << 6) | ($c3 & 0x3F); - return 0 if $u < 0x800 || ($u >= 0xD800 && $u <= 0xDFFFF) || $u >= 0xFFFE; - next; - } - # Need 3 more bytes - $i += 3; - return 0 if $i >= $l; - my $c2 = ord(substr($_[0],$i-2,1)); - return 0 if $c2 < 0x80 || $c2 > 0xBF; - my $c3 = ord(substr($_[0],$i-1,1)); - return 0 if $c3 < 0x80 || $c3 > 0xBF; - my $c4 = ord(substr($_[0],$i,1)); - return 0 if $c4 < 0x80 || $c4 > 0xBF; - my $u = (($c & 0x07) << 18) | (($c2 & 0x3F) << 12) | (($c3 & 0x3F) << 6) - | ($c4 & 0x3F); - return 0 if $u < 0x10000 || $u >= 0x10FFFE || (($u & 0xFFFF) >= 0xFFFE); - } - return 1; -} - -sub Make1252() -{ - use bytes; - our %W1252; - - # Provide translations for 0x80-0x9F into UTF-8 - $W1252{0x80} = pack('H*','E282AC'); # 0x20AC Euro - $W1252{0x82} = pack('H*','E2809A'); # 0X201A Single Low-9 Quote - $W1252{0x83} = pack('H*','C692'); # 0x0192 Latin Small Letter f With Hook - $W1252{0x84} = pack('H*','E2809E'); # 0x201E Double Low-9 Quote - $W1252{0x85} = pack('H*','E280A6'); # 0x2026 Horizontal Ellipsis - $W1252{0x86} = pack('H*','E280A0'); # 0x2020 Dagger - $W1252{0x87} = pack('H*','E280A1'); # 0x2021 Double Dagger - $W1252{0x88} = pack('H*','CB86'); # 0x02C6 Modifier Letter Circumflex Accent - $W1252{0x89} = pack('H*','E28080'); # 0x2030 Per Mille Sign - $W1252{0x8A} = pack('H*','C5A0'); # 0x0160 Latin Capital Letter S With Caron - $W1252{0x8B} = pack('H*','E28089'); # 0x2039 Left Single Angle Quote - $W1252{0x8C} = pack('H*','C592'); # 0x0152 Latin Capital Ligature OE - $W1252{0x8E} = pack('H*','C5BD'); # 0x017D Latin Capital Letter Z With Caron - $W1252{0x91} = pack('H*','E28098'); # 0x2018 Left Single Quote - $W1252{0x92} = pack('H*','E28099'); # 0x2019 Right Single Quote - $W1252{0x93} = pack('H*','E2809C'); # 0x201C Left Double Quote - $W1252{0x94} = pack('H*','E2809D'); # 0x201D Right Double Quote - $W1252{0x95} = pack('H*','E280A2'); # 0x2022 Bullet - $W1252{0x96} = pack('H*','E28093'); # 0x2013 En Dash - $W1252{0x97} = pack('H*','E28094'); # 0x2014 Em Dash - $W1252{0x98} = pack('H*','CB9C'); # 0x02DC Small Tilde - $W1252{0x99} = pack('H*','E284A2'); # 0x2122 Trade Mark Sign - $W1252{0x9A} = pack('H*','C5A1'); # 0x0161 Latin Small Letter s With Caron - $W1252{0x9B} = pack('H*','E2808A'); # 0x203A Right Single Angle Quote - $W1252{0x9C} = pack('H*','C593'); # 0x0153 Latin Small Ligature oe - $W1252{0x9E} = pack('H*','C5BE'); # 0x017E Latin Small Letter z With Caron - $W1252{0x9F} = pack('H*','C5B8'); # 0x0178 Latin Cap Letter Y With Diaeresis -} - -sub MakeUTF8($) -{ - use bytes; - our %W1252; - - return $_[0] if (IsUTF8($_[0])); - my $ans = ''; - foreach my $c (unpack('C*',$_[0])) { - if ($c < 0x80) { - $ans .= chr($c); - } - else { - # Ass/u/me we have Latin-1 (ISO-8859-1) but per the HTML 5 draft treat - # it as windows-1252 - if ($c >= 0xA0 || !defined($W1252{$c})) { - $ans .= chr(0xC0 | ($c >> 6)); - $ans .= chr(0x80 | ($c & 0x3F)); - } - else { - $ans .= $W1252{$c}; - } - } - } - return $ans; -} - -sub formatbold($;$) -{ - my $str = shift; - my $fancy = shift || 0; - if ($fancy) { - $str = join('',map($_."\b".$_, split(//,$str))); - } - return $str; -} - -sub formatul($;$) -{ - my $str = shift; - my $fancy = shift || 0; - if ($fancy) { - $str = join('',map("_\b".$_, split(//,$str))); - } - return $str; -} - -sub formatman($;$) -{ - my $man = shift; - my $fancy = shift || 0; - my @inlines = split(/\n/, $man, -1); - my @outlines = (); - foreach my $line (@inlines) { - if ($line =~ /^[A-Z]+$/) { - $line = formatbold($line, $fancy); - } - else { - $line =~ s/'''(.+?)'''/formatbold($1,$fancy)/gse; - $line =~ s/''(.+?)''/formatul($1,$fancy)/gse; - } - push (@outlines, $line); - } - my $result = join("\n", @outlines); - $result =~ s/\\\n//gso; - return $result; -} - -sub DERLength($) -{ - # return a DER encoded length - my $len = shift; - return pack('C',$len) if $len <= 127; - return pack('C2',0x81, $len) if $len <= 255; - return pack('Cn',0x82, $len) if $len <= 65535; - return pack('CCn',0x83, ($len >> 16), $len & 0xFFFF) if $len <= 16777215; - # Silently returns invalid result if $len > 2^32-1 - return pack('CN',0x84, $len); -} - -sub SingleOID($) -{ - # return a single DER encoded OID component - no warnings; - my $num = shift; - $num += 0; - my $result = pack('C', $num & 0x7F); - $num >>= 7; - while ($num) { - $result = pack('C', 0x80 | ($num & 0x7F)) . $result; - $num >>= 7; - } - return $result; -} - -sub DEROID($) -{ - # return a DER encoded OID complete with leading 0x06 and DER length - # Input is a string of decimal numbers separated by '.' with at least - # two numbers required. - no warnings; - my @ids = split(/[.]/,$_[0]); - push(@ids, 0) while @ids < 2; # return something that's kind of valid - unshift(@ids, shift(@ids) * 40 + shift(@ids)); # combine first two - my $ans = ''; - foreach my $num (@ids) { - $ans .= SingleOID($num); - } - return pack('C',0x6).DERLength(length($ans)).$ans; -} - -sub DERTime($) -{ - my $t = shift; # a time() value - my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($t); - $year += 1900; - ++$mon; - my $tag; - my $tstr; - if (1950 <= $year && $year < 2050) { - # UTCTime - $tag = 0x17; - $tstr = sprintf("%02d%02d%02d%02d%02d%02dZ", $year % 100, $mon, $mday, - $hour, $min, $sec); - } - else { - # GeneralizedTime - $tag = 0x18; - $tstr = sprintf("%04d%02d%02d%02d%02d%02dZ", $year, $mon, $mday, - $hour, $min, $sec); - } - return pack('C',$tag).DERLength(length($tstr)).$tstr; -} - -sub RandomID(;$) -{ - # return 20 random bytes except that the first byte has its high bit clear - my $suppress = shift || 0; - print STDERR "Generating serial number, please wait...\n" unless $suppress; - open(RANDIN, "<", "/dev/random") - or die "Cannot open /dev/random for input\n"; - my $result = ''; - for (my $cnt = 0; $cnt < 20; ++$cnt) { - my $byte; - sysread(RANDIN, $byte, 1) - or die "Cannot read from /dev/random\n"; - if (!$cnt) { - my $val = unpack('C', $byte); - $val &= 0x7F; - $byte = pack('C', $val); - } - $result .= $byte; - } - close(RANDIN); - print STDERR "...done creating serial number.\n" unless $suppress; - return $result; -} - -sub ReadDERLength($) -{ - # Input is a DER encoded length with possibly extra trailing bytes - # Output is an array of length and bytes-used-for-encoded-length - my $der = shift; - return undef unless length($der); - my $byte = unpack('C',substr($der,0,1)); - return ($byte, 1) if $byte <= 127; - return undef if $byte == 128 || $byte > 128+8; # Fail if greater than 2^64 - my $cnt = $byte & 0x7F; - return undef unless length($der) >= $cnt+1; # Fail if not enough bytes - my $val = 0; - for (my $i = 0; $i < $cnt; ++$i) { - $val <<= 8; - $val |= unpack('C',substr($der,$i+1,1)); - } - return ($val, $cnt+1); -} - -sub GetKeyInfo($) -{ - # Input is an RSA PRIVATE KEY in DER format - # Output is an array of: - # how many bits in the modulus - # the public exponent - # the key id - # the OpenSSH md5 fingerprint - # the OpenSSH sha1 fingerprint - # or undef if the key is unparseable - # - # Expected format is: - # SEQUENCE { - # SEQUENCE { - # OBJECT IDENTIFIER :rsaEncryption = 1.2.840.113549.1.1.1 - # NULL - # } - # BIT STRING (primitive) { - # 0 unused bits - # SEQUENCE { # this part is the contents of an "RSA PUBLIC KEY" file - # INTEGER modulus - # INTEGER publicExponent - # } - # } - # } - - no warnings; - my $der = shift; - my $rawmod; - my $rawexp; - - return undef if unpack('C',substr($der,0,1)) != 0x30; - my ($len, $lenbytes) = ReadDERLength(substr($der,1)); - return undef unless length($der) == 1 + $lenbytes + $len; - substr($der, 0, 1 + $lenbytes) = ''; - - # the algorithm part always encodes as 30 0d 06092a864886f70d010101 0500 - return undef - unless substr($der, 0, 15) = pack('H*',"300d06092a864886f70d0101010500"); - substr($der, 0, 15) = ''; - - return undef if unpack('C',substr($der,0,1)) != 0x03; - ($len, $lenbytes) = ReadDERLength(substr($der,1)); - return undef unless length($der) == 1 + $lenbytes + $len && $len >= 1; - return undef unless unpack('C',substr($der, 1 + $lenbytes, 1)) == 0x00; - substr($der, 0, 1 + $lenbytes + 1) = ''; - - return undef if unpack('C',substr($der,0,1)) != 0x30; - ($len, $lenbytes) = ReadDERLength(substr($der,1)); - return undef unless length($der) == 1 + $lenbytes + $len; - my $id = sha1($der); # The id is the sha1 hash of the private key part - substr($der, 0, 1 + $lenbytes) = ''; - - return undef if unpack('C',substr($der,0,1)) != 0x02; - ($len, $lenbytes) = ReadDERLength(substr($der,1)); - substr($der, 0, 1 + $lenbytes) = ''; - my $derexp = substr($der, $len); - substr($der, $len) = ''; - return undef unless $len >= 1; - $rawmod = $der; - my $bits = length($der) * 8; - # But we have to discount any leading 0 bits in the first byte - my $byte = unpack('C',substr($der,0,1)); - if (!$byte) { - $bits -= 8; - } - else { - return undef if $byte & 0x80; # negative modulus is not allowed - while (!($byte & 0x80)) { - --$bits; - $byte <<= 1; - } - } - - $der = $derexp; - return undef if unpack('C',substr($der,0,1)) != 0x02; - ($len, $lenbytes) = ReadDERLength(substr($der,1)); - substr($der, 0, 1 + $lenbytes) = ''; - return undef unless length($der) == $len && $len >= 1; - return undef if unpack('C',substr($der,0,1)) & 0x80; # negative pub exp bad - $rawexp = $der; - my $exp; - if ($len > 8) { - # Fudge the result because it's bigger than a 64-bit number - my $lastbyte = unpack('C',substr($der,-1,1)); - $exp = $lastbyte & 0x01 ? 65537 : 65536; - } - else { - $exp = 0; - while (length($der)) { - $exp <<= 8; - $exp |= unpack('C',substr($der,0,1)); - substr($der,0,1) = ''; - } - } - - my $tohash = pack('N',7)."ssh-rsa".pack('N',length($rawexp)).$rawexp - .pack('N',length($rawmod)).$rawmod; - - return ($bits,$exp,$id,md5($tohash),sha1($tohash)); -} - -sub BreakLine($$) -{ - my ($line,$width) = @_; - my @ans = (); - return $line if $width < 1; - while (length($line) > $width) { - push(@ans, substr($line, 0, $width)); - substr($line, 0, $width) = ''; - } - push(@ans, $line) if length($line); - return @ans; -} - -sub tests -{ - print STDERR unpack('H*', DEROID('2.100.3')),"\n"; # should be 0603813403 - for (my $i=0; $i<16; ++$i) { - print STDERR unpack('H*', RandomID(1)),"\n"; # Hi bit should NOT be set - } -} - -sub GetDigest($) -{ - my $dgst = shift; - my $sha1 = DEROID('1.3.14.3.2.26'); - my $sha224 = DEROID('2.16.840.1.101.3.4.2.4'); - my $sha256 = DEROID('2.16.840.1.101.3.4.2.1'); - my $sha384 = DEROID('2.16.840.1.101.3.4.2.2'); - my $sha512 = DEROID('2.16.840.1.101.3.4.2.3'); - my $sha1WithRSAEncryption = DEROID('1.2.840.113549.1.1.5'); - my $sha224WithRSAEncryption = DEROID('1.2.840.113549.1.1.14'); - my $sha256WithRSAEncryption = DEROID('1.2.840.113549.1.1.11'); - my $sha384WithRSAEncryption = DEROID('1.2.840.113549.1.1.12'); - my $sha512WithRSAEncryption = DEROID('1.2.840.113549.1.1.13'); - return ($sha1, $sha1WithRSAEncryption, \&sha1) if $dgst eq 'sha1'; - my $h = undef; - my $oid = undef; - my $func = undef; - for (;;) { - $h=$sha224,$oid=$sha224WithRSAEncryption,$func=\&sha224,last - if $dgst eq 'sha224'; - $h=$sha256,$oid=$sha256WithRSAEncryption,$func=\&sha256,last - if $dgst eq 'sha256'; - $h=$sha384,$oid=$sha384WithRSAEncryption,$func=\&sha384,last - if $dgst eq 'sha384'; - $h=$sha512,$oid=$sha512WithRSAEncryption,$func=\&sha512,last - if $dgst eq 'sha512'; - last; - } - die "Invalid digest ($dgst) must be one of:\n" - . " sha1 sha224 sha256 sha384 sha512\n" unless $h && $oid; - die "Digest $dgst requires Digest::SHA or Digest::SHA::PurePerl " - . "to be available\n" if !$hasSha2; - return ($h,$oid,$func); -} - -sub toupper($) -{ - my $str = shift; - $str =~ tr/a-z/A-Z/; - return $str; -} - -sub tolower($) -{ - my $str = shift; - $str =~ tr/A-Z/a-z/; - return $str; -} - -sub RSASign($$) -{ - my ($data, $keyfile) = @_; - my $sig; - { - local(*CHLD_OUT, *CHLD_IN); - #open(my $olderr, ">&STDERR") or die "Cannot dup STDERR: $!\n"; - #open(STDERR, '>', "/dev/null") or die "Cannot redirect STDERR: $!"; - (my $pid = open2(\*CHLD_OUT, \*CHLD_IN, "openssl", "rsautl", "-sign", - "-inkey", $keyfile)) - or die "Cannot start openssl rsautl\n"; - print CHLD_IN $data; - close(CHLD_IN); - local $/; - die "Error reading RSA signature from openssl rsautl\n" - unless !!($sig = ); - waitpid($pid, 0); - close(CHLD_OUT); - #open(STDERR, ">&", $olderr) or die "Cannot dup \$olderr: $!"; - } - return $sig; -} - -sub main -{ - Make1252(); # Set up the UTF-8 auxiliary conversion table - - my $help = ''; - my $verbose = ''; - my $quiet = ''; - my $keyfile = ''; - my $useNow = ''; - my $useRandom = ''; - my $digest = $hasSha2 ? 'sha256' : 'sha1'; - my $digestChoice = ''; - my $debug = 0; - my $check = ''; - my $commonName = DEROID('2.5.4.3'); # :commonName - my $serialNumber = DEROID('2.5.4.5'); # :serialNumber - my $basicConstraints = DEROID('2.5.29.19'); - my $keyUsage = DEROID('2.5.29.15'); - my $authKeyId = DEROID('2.5.29.35'); - my $subjKeyId = DEROID('2.5.29.14'); - my $boolTRUE = pack('C*',0x01,0x01,0xFF); - my $boolFALSE = pack('C*',0x01,0x01,0x00); - my $v3Begin = pack('C',0x17).DERLength(13)."970811000000Z"; - my $noExpiry = pack('C',0x18).DERLength(15)."99991231235959Z"; - - #tests; - eval {GetOptions( - "help|h" => sub{$help=1;die"!FINISH"}, - "verbose|v" => \$verbose, - "version|V" => sub{print STDERR $VERSIONMSG;exit(0)}, - "debug" => \$debug, - "quiet" => \$quiet, - "check" => \$check, - "now" => \$useNow, - "random" => \$useRandom, - "digest=s" => \$digestChoice, - "key|k=s" => \$keyfile - )} || $help - or die $USAGE; - if ($help) { - local *MAN; - my $pager = $ENV{'PAGER'} || 'less'; - if (-t STDOUT && open(MAN, "|-", $pager)) { - print MAN formatman($HELP,1); - close(MAN); - } - else { - print formatman($HELP); - } - exit(0); - } - $verbose = 1 if $debug || $check; - $quiet = 0 if $verbose || $check; - print STDERR $VERSIONMSG if $verbose; - die $USAGE if !$keyfile || (!$check && @ARGV != 1); - die "Cannot read key file $keyfile\n" if ! -r $keyfile; - my ($did, $dalg, $dfunc) = GetDigest($digestChoice || $digest); - print STDERR "default digest: $digest\n" if $debug; - warn "*** Warning: defaulting to sha1 since sha256 support not available\n" - if !$quiet && $digest eq 'sha1' && !$digestChoice; - $digest = $digestChoice if $digestChoice; - warn "*** Warning: sha1 use is strongly discouraged, continuing anyway\n" - if !$quiet && $digest eq 'sha1'; - print STDERR "Using digest $digest\n" if $verbose; - my $inform = -T $keyfile ? 'PEM' : 'DER'; - print STDERR "-inform $inform\n" if $debug; - die "Input key does not appear to be in PEM format: $keyfile\n" - unless $inform eq 'PEM'; - my $pubkey; - { - local *READKEY; - open(my $olderr, ">&STDERR") or die "Cannot dup STDERR: $!\n"; - open(STDERR, '>', "/dev/null") or die "Cannot redirect STDERR: $!"; - open(READKEY, "-|", "openssl", "rsa", "-inform", $inform, "-outform", "DER", - "-pubout", "-passin", "pass:", "-in", $keyfile) - or die "Cannot read RSA private key in $keyfile\n"; - open(STDERR, ">&", $olderr) or die "Cannot dup \$olderr: $!"; - local $/; - die "Error reading RSA private key in $keyfile\n" - unless !!($pubkey = ); - close(READKEY); - } - my ($pubkeybits,$pubkeyexp,$pubkeyid,$fmd5,$fsha1) = GetKeyInfo($pubkey); - die "Unparseable public key format in $keyfile\n" unless $pubkeybits; - print STDERR "RSA Private Key $keyfile:\n", - " bits=$pubkeybits pubexp=$pubkeyexp\n" if $verbose; - print STDERR " keyid=", - join(":", toupper(unpack("H*",$pubkeyid))=~/../g), "\n" if $verbose; - print STDERR " fingerprint(md5)=", - join(":", tolower(unpack("H*",$fmd5))=~/../g), "\n" if $verbose; - print STDERR " fingerprint(sha1)=", - join(":", tolower(unpack("H*",$fsha1))=~/../g), "\n" if $verbose; - die "*** Error: Input key has less than 512 bits ($pubkeybits)\n" - . "*** You might as well just donate your system to hackers now.\n" - if $pubkeybits < 512; - die "*** Error: The public exponent is even ($pubkeyexp)!\n" - if !($pubkeyexp & 0x01); - warn "*** Warning: Input key has less than 2048 bits ($pubkeybits), " - . "continuing anyway\n" if !$quiet && $pubkeybits < 2048; - die "*** Error: The public key exponent of $pubkeyexp is unacceptably weak!\n" - if $pubkeyexp < 35; # OpenSSH ssh-keygen has used 35 until version 5.4 - warn "*** Warning: The public exponent ($pubkeyexp) is weak (< 65537), " - . "continuing anyway\n" if !$quiet && $pubkeyexp < 65537; - - return 0 if $check; - - my $version = pack('CCCCC', 0xA0, 0x03, 0x02, 0x01, 0x02); # v3 - my $randval = $useRandom ? RandomID($quiet) : undef; - my $sigAlg = $dalg . pack('CC',0x05,0x00); - $sigAlg = pack('C',0x30).DERLength(length($sigAlg)).$sigAlg; - my $name = MakeUTF8($ARGV[0]); - $name = pack('C',0x0C).DERLength(length($name)).$name; # utf8String - $name = $commonName . $name; - $name = pack('C',0x30).DERLength(length($name)).$name; - $name = pack('C',0x31).DERLength(length($name)).$name; - if ($useRandom) { - my $serialRDN = join(":", tolower(unpack("H*",$randval))=~/../g); - $serialRDN = pack('C',0x13).DERLength(length($serialRDN)).$serialRDN; - $serialRDN = $serialNumber . $serialRDN; - $serialRDN = pack('C',0x30).DERLength(length($serialRDN)).$serialRDN; - $serialRDN = pack('C',0x31).DERLength(length($serialRDN)).$serialRDN; - $name = $serialRDN.$name; - } - $name = pack('C',0x30).DERLength(length($name)).$name; - my $validity = ($useNow ? DERTime(time()) : $v3Begin).$noExpiry; - $validity = pack('C',0x30).DERLength(length($validity)).$validity; - my $extCATrue = pack('C',0x30).DERLength(length($boolTRUE)).$boolTRUE; - $extCATrue = pack('C',0x04).DERLength(length($extCATrue)).$extCATrue; - $extCATrue = $basicConstraints . $boolTRUE . $extCATrue; - $extCATrue = pack('C',0x30).DERLength(length($extCATrue)).$extCATrue; - my $extKeyUse = pack('C*',0x04,0x04,0x03,0x02,0x01,0x06); # OCT BIT 0x060 - $extKeyUse = $keyUsage . $boolTRUE. $extKeyUse; - $extKeyUse = pack('C',0x30).DERLength(length($extKeyUse)).$extKeyUse; - my $extSubjKey = pack('C',0x04).DERLength(length($pubkeyid)).$pubkeyid; - $extSubjKey = pack('C',0x04).DERLength(length($extSubjKey)).$extSubjKey; - $extSubjKey = $subjKeyId . $extSubjKey; - $extSubjKey = pack('C',0x30).DERLength(length($extSubjKey)).$extSubjKey; - my $extAuthKey = pack('C',0x80).DERLength(length($pubkeyid)).$pubkeyid; - $extAuthKey = pack('C',0x30).DERLength(length($extAuthKey)).$extAuthKey; - $extAuthKey = pack('C',0x04).DERLength(length($extAuthKey)).$extAuthKey; - $extAuthKey = $authKeyId . $extAuthKey; - $extAuthKey = pack('C',0x30).DERLength(length($extAuthKey)).$extAuthKey; - my $exts = $extCATrue . $extKeyUse . $extSubjKey . $extAuthKey; - $exts = pack('C',0x30).DERLength(length($exts)).$exts; - $exts = pack('C',0xA3).DERLength(length($exts)).$exts; - my $serial; - if ($useRandom) { - $serial = pack('C',0x2).DERLength(length($randval)).$randval; - } - else { - my $idtohash = $version.$sigAlg.$name.$validity.$name.$pubkey.$exts; - $idtohash = pack('C',0x30).DERLength(length($idtohash)).$idtohash; - my $idhash = sha1($idtohash); - my $byte0 = unpack('C',substr($idhash,0,1)); - $byte0 &= 0x7F; - substr($idhash,0,1) = pack('C',$byte0); - $serial = pack('C',0x2).DERLength(length($idhash)).$idhash; - } - my $tbs = $version.$serial.$sigAlg.$name.$validity.$name.$pubkey.$exts; - $tbs = pack('C',0x30).DERLength(length($tbs)).$tbs; - my $tbsseq = &$dfunc($tbs); - $tbsseq = pack('C',0x04).DERLength(length($tbsseq)).$tbsseq; - my $algid = $did . pack('CC',0x05,0x00); - $algid = pack('C',0x30).DERLength(length($algid)).$algid; - $tbsseq = $algid . $tbsseq; - $tbsseq = pack('C',0x30).DERLength(length($tbsseq)).$tbsseq; - my $sig = RSASign($tbsseq, $keyfile); - $sig = pack('C',0x03).DERLength(length($sig)+1).pack('C',0x00).$sig; - my $cert = $tbs . $sigAlg . $sig; - $cert = pack('C',0x30).DERLength(length($cert)).$cert; - my $base64 = join("\n", BreakLine(encode_base64($cert, ''), 64))."\n"; - print "-----BEGIN CERTIFICATE-----\n", - $base64, - "-----END CERTIFICATE-----\n"; - return 0; -} -- 2.11.4.GIT