VOMS-Lite

 view release on metacpan or  search on metacpan

lib/VOMS/Lite/PEMHelper.pm  view on Meta::CPAN

  $type =~ s/[^A-Z0-9 ]//g;
  foreach (@_) {
    my $OpenSSLCompat=encode_base64($_,'');
    $OpenSSLCompat=~s/(.{1,64})/$&\n/g; 
    $certstr .= "-----BEGIN $type-----\n".$OpenSSLCompat."-----END $type-----\n";
  }
  return $certstr;
}

################################################################

sub writeCertKey {
# At least 3 arguements (file, public key, private key, [chain of signing certificates]);
  my $file=shift;
  my $pub=shift;
  my $pri=shift;

# Place file
  my $umasksave=umask(0077);
  
  if ( umask() != 0077 ) { 
    if ( $^O =~ /^MSWin/ ) { print STDERR "WARNING: Can't umask 0077 when writing $file\n"; }
    else                   { die "Can't umask 0077 when writing $file"; }
  }
  if ( -e $file ) { move($file,"$file.old"); } #move old file away

  open(CERTKEY,">$file") || die "Can't create file to save cert and key to.";
  print CERTKEY encodeCert($pub,"CERTIFICATE");
  print CERTKEY encodeCert($pri,"RSA PRIVATE KEY");
  foreach ( @_ ) { print CERTKEY encodeCert($_,"CERTIFICATE"); }
  close(CERTKEY);
  umask($umasksave);
  return;
}

################################################################

sub writeKey {
# At least 3 arguements (file, public key, private key, [chain of signing certificates]);
  my $file=shift;
  my $pri=shift;
  my $passwd=shift;
  my $ENCRYPTION="";

  if ( ! defined $passwd ) {
# Prompt for password
    require Term::ReadKey;
    print "I need the passphrase used to encrypt the key in \n$file\nPassphrase: ";
    my $dummy=Term::ReadKey::ReadMode('noecho');
    $passwd = Term::ReadKey::ReadLine(),
    $dummy=Term::ReadKey::ReadMode('normal');
    chomp $passwd;
    print "\n";
  }

# To encrypt or not to encrypt
  if ( $passwd ne "" ) {

# Spin up the Crypto stuff
    require Digest::MD5;
    require Crypt::DES_EDE3;

# Make Initialisation vector
    my $iv="";
    while (length($iv)<8 ) {$iv.=chr((rand(255)+1));}

# Construct DES Key from password (Munge)
    my $keysize=24;
    my $SALT=$iv;
    my $key=Digest::MD5::md5($passwd,$SALT);
    while (length($key) < $keysize) { $key .= Digest::MD5::md5($key, $passwd, $SALT);}
    $key=substr($key,0,$keysize);

# DES Padding Data as per RFC 1423 (not 1851 which adds message payload info)
    my $pad = ( 8 - (length($pri)%8) );
    my $padding=chr($pad) x $pad;
    $pri.=$padding;

# Encode Data
    my $DES = Crypt::DES_EDE3->new($key);
    my $cyphertextout="";
    while ( my $len=length($pri) ) {
      my $block=substr($pri,0,8);
      $pri=substr($pri,8);
      $block = $SALT ^ $block;
      my $cyphertext=$DES->encrypt($block);
      $SALT=$cyphertext;
      $cyphertextout.=$cyphertext;
    }

# Set PEM encryprion header
    $iv=unpack('H*',$iv);
    $iv =~ y/[a-f]/[A-F]/;
    $ENCRYPTION="Proc-Type: 4,ENCRYPTED\nDEK-Info: DES-EDE3-CBC,$iv\n\n";
    $pri=$cyphertextout;
  }

# Place file
  my $umasksave=umask(0077);

  if ( umask() != 0077 ) { 
    if ( $^O =~ /^MSWin/ ) { print STDERR "WARNING: Can't umask 0077 when writing $file\n"; }
    else                   { die "Can't umask 0077 when writing $file"; }
  }
  if ( -e $file ) { move($file,"$file.old"); } #move old file away

  open(KEY,">$file") || die "Can't create file to save cert and key to.";
  my $OpenSSLCompat=encode_base64($pri,'');
  $OpenSSLCompat=~s/(.{1,64})/$&\n/g;
  print KEY "-----BEGIN RSA PRIVATE KEY-----\n$ENCRYPTION".$OpenSSLCompat."-----END RSA PRIVATE KEY-----\n";
  close(KEY);
  umask($umasksave);
  return;
}

################################################################

sub writeCert {
# At least 3 arguements (file, public key, private key, [chain of signing certificates]);
  my $file=shift;
  my $pub=shift;
  my $type=shift;
  if ( ! defined($type) ) { $type="CERTIFICATE"; }
  $type =~ y/a-z/A-Z/;
  $type =~ s/[^A-Z0-9 ]//g;

# Place file
  if ( -e $file ) { move($file,"$file.old"); } #move old file away
  open(CERT,">$file") || die "Can't create file to save cert and key to.";
  my $OpenSSLCompat=encode_base64($pub,'');
  $OpenSSLCompat=~s/(.{1,64})/$&\n/g;
  print CERT "-----BEGIN $type-----\n".$OpenSSLCompat."-----END $type-----\n";
  close(CERT);
  return;
}


################################################################

sub readPrivateKey {  #Returns BER with Private key in it

lib/VOMS/Lite/PEMHelper.pm  view on Meta::CPAN


# Load and parse private key file
  my ($myKeyData,$PEMV,$PEMType,$PEMEnc,$SALT)=("","","","","");
  my $read=0;
  open(KEY,"<$file") || die "Can't access Private Key file $file";
  while (<KEY>) {
    my $line=$_;
    if ( $line =~ /^-----BEGIN RSA PRIVATE KEY-----$/ ) {$read=1; next;}
    if ( $line =~ /^-----BEGIN PRIVATE KEY-----$/ ) {$read=2; next;}
    if ( $line =~ /^-----END RSA PRIVATE KEY-----$/ ) {last;}
    if ( $line =~ /^-----END PRIVATE KEY-----$/ ) {last;}
    if ( $read==1 ) {
      if ( $line =~ /^Proc-Type: ([0-9]+),(ENCRYPTED)$/ ) {$PEMV=$1; $PEMType=$2}
      elsif ( $line =~ /^DEK-Info: (.*),(.*)$/ ) {$PEMEnc=$1; $SALT=$2}
      elsif ( $line =~ /^([A-Za-z0-9+\/=]+)$/ ) {$myKeyData.=$1;}
    }
    if ( $read==2 ) {
      if ( $line =~ /^([A-Za-z0-9+\/=]+)$/ ) {$myKeyData.=$1;}
    }
  }
  close(KEY);

# Return data if it's not encrypted
  if ( $myKeyData eq "" ) { die "I didn't understand the format of your key file:\n$file";}

# Obtain and check Encryption values
  my $cyphertext=decode_base64($myKeyData);

# If "PRIVATE KEY" but not "RSA PRIVATE KEY" Parse into it
  if ( $read == 2 ) { # Unencrypted pkcs #8
    die "I didn't understand the format of your key file:\n$file";
  }

  return $cyphertext if ( $PEMType ne "ENCRYPTED" ); # Because actually it's not encrypted.
  if ( $PEMEnc ne "DES-EDE3-CBC" ) { die "I don't know how to unencrypt your key\n";}
  if ( $SALT !~ /^[a-fA-F0-9]{16}$/ ) { die "Bad Initilisation Vector (salt)'; I can't unencrypt your key!\n";}
  if ( $PEMV ne "4" ) { print STDERR "Warning: I was expecting a version 4 PEM encrypted file you gave me a Version $PEMV\nFunny things may happen!\n"; }


# Check/get password
  if ( defined $passwd && $passwd eq "" ) { return undef; } #was expecting no password so abort
  elsif ( ! defined $passwd ) {
    require Term::ReadKey;
    require Digest::MD5;
    print "I need the passphrase used to encrypt the key in \n$file\nPassphrase: ";
    my $dummy=Term::ReadKey::ReadMode('noecho');
    $passwd = Term::ReadKey::ReadLine(),
    $dummy=Term::ReadKey::ReadMode('normal');
    chomp $passwd;
    print "\n";
  }

# Reconstruct DES Key from password (Munge)
  my $keysize=24;
  $SALT=pack('H*', $SALT);
  my $key=Digest::MD5::md5($passwd,$SALT);
  while (length($key) < $keysize) { $key .= Digest::MD5::md5($key, $passwd, $SALT);}
  $key=substr($key,0,$keysize);

# Decode Data
  require Crypt::DES_EDE3;
  my $DES = Crypt::DES_EDE3->new($key);
  my $dataout="";
  while ( my $len=length($cyphertext) ) {
    my $block=substr($cyphertext,0,8);
    $cyphertext=substr($cyphertext,8);
    my $data=$SALT ^ $DES->decrypt($block);
    $SALT=$block;
    $dataout.=$data;
  }

# Remove DES Padding
  my $unpad=substr ($dataout,-1);
  if ( "$unpad" =~ /[\001-\010]/  ) { $dataout=substr($dataout,0,-ord($unpad));}
  else { die "Your passphrase didn't do it for me!\n";}

  return $dataout;
}

1;
__END__

=head1 NAME

VOMS::Lite::PEMHelper - Perl extension for decoding and encoding PEM X.509 certificates and keys.

=head1 SYNOPSIS

  use VOMS::Lite::PEMHelper qw( writeAC encodeCert encodeAC readAC readCert decodeCert writeCertKey readPrivateKey writeCert writeKey);

  # write DER AC $data as a PEM AC to file $file
  writeAC($file,$data);

  # encode DER Certificate $data as a PEM Certificate
  $cert=encodeCert($data); 

  # encode DER AC $data as a PEM AC
  $ac=encodeAC($data); 

  # read PEM AC in file $file and return DER AC 
  $data=readAC($file);

  # read PEM Certificates in file $file and return DER Certificates 
  # in array
  @certs=readCert($file);

  # read PEM Certificates in file $file and return the first as a DER 
  # Certificate
  $cert=readCert($file);

  # takes a string and/or array of PEMs followsd by a type as arguments 
  # and decodes them into an array of DERs
  @DERs=decodeCert(@PEMs,"CERTIFICATE"); 

  # take DER certificate, private key and (optionally) a chain of
  # signers and write them in PEM format to file $file
  writeCert($file, $cert);

  # take DERprivate key and write it in PEM format to file $file, 
  # encrypting with $password. If $password is undefined it will prompt 
  # for a password, if password is "" no encryption will be used.
  writeKey($file, $privateKey, $password);



( run in 1.083 second using v1.01-cache-2.11-cpan-85f18b9d64f )