You are viewing a plain text version of this content. The canonical link for it is here.
Posted to test-cvs@httpd.apache.org by do...@apache.org on 2001/12/14 09:54:30 UTC

cvs commit: httpd-test/perl-framework/Apache-Test/lib/Apache TestSSLCA.pm

dougm       01/12/14 00:54:30

  Added:       perl-framework/Apache-Test/lib/Apache TestSSLCA.pm
  Log:
  new module for generating a CA along with certs, keys, crls, etc.
  
  Revision  Changes    Path
  1.1                  httpd-test/perl-framework/Apache-Test/lib/Apache/TestSSLCA.pm
  
  Index: TestSSLCA.pm
  ===================================================================
  package Apache::TestSSLCA;
  
  use strict;
  use warnings FATAL => 'all';
  
  use Cwd ();
  use File::Path ();
  use File::Basename;
  use Apache::TestConfig ();
  use Apache::TestTrace;
  
  use vars qw(@EXPORT_OK &import);
  
  @EXPORT_OK = qw(dn dn_vars dn_oneline);
  *import = \&Exporter::import;
  
  my $openssl = $ENV{APACHE_TEST_OPENSSL_CMD} || 'openssl';
  
  my $CA = 'asf';
  my $Config; #global Apache::TestConfig object
  
  my $days     = '-days 365';
  my $cakey    = 'keys/cakey.pem';
  my $cacert   = 'certs/cacert.crt';
  my $capolicy = '-policy policy_anything';
  my $cacrl    = 'crl/ca-bundle.crl';
  
  #we use the same password for everything
  my $pass    = 'httpd';
  my $passin  = "-passin pass:$pass";
  my $passout = "-passout pass:$pass";
  
  my %ca_dn = (
      asf => {
          C  => 'US',
          ST => 'California',
          L  => 'San Francisco',
          O  => 'ASF',
          OU => 'httpd-test',
          CN => '',
          Email => 'test-dev@httpd.apache.org',
      },
  );
  
  my %cert_dn = (
      client_snakeoil => {
          C  => 'AU',
          ST => 'Queensland',
          L  => 'Mackay',
          O  => 'Snake Oil, Ltd.',
          OU => 'Staff',
      },
      server => {
          CN => 'localhost',
      },
      server_des3 => {
          CN => 'localhost',
          OU => 'httpd-test/perl-framework',
      },
  );
  
  sub dn {
      my $name = shift;
  
      my %dn = %{ $ca_dn{$CA} }; #default values
      $dn{CN} ||= $name; #try make sure each Common Name is different
  
      my $cert_dn = $cert_dn{$name};
  
      if ($cert_dn) {
          while (my($key, $value) = each %$cert_dn) {
              #override values
              $dn{$key} = $value;
          }
      }
  
      return wantarray ? %dn : \%dn;
  }
  
  sub dn_vars {
      my($name, $type) = @_;
  
      my $dn = dn($name);
      my $prefix = join '_', 'SSL', $type, 'DN';
  
      return { map { $prefix ."_$_", $dn->{$_} } keys %$dn };
  }
  
  sub dn_oneline {
      my($dn) = @_;
  
      unless (ref $dn) {
          $dn = dn($dn);
      }
  
      my $string = "";
  
      for my $k (qw(C ST L O OU CN Email)) {
          next unless $dn->{$k};
          $string .= "/$k=$dn->{$k}";
      }
  
      $string;
  }
  
  sub openssl {
      return $openssl unless @_;
  
      my $cmd = "$openssl @_";
  
      warning $cmd;
  
      unless (system($cmd) == 0) {
          my $status = $? >> 8;
          die "system @_ failed (exit status=$status)";
      }
  }
  
  my @dirs = qw(keys newcerts certs crl export csr conf);
  
  sub init {
      for my $dir (@dirs) {
          gendir($dir);
      }
  }
  
  sub config_file {
      my $name = shift;
  
      my $file = "conf/$name.cnf";
      return $file if -e $file;
  
      my $dn = dn($name);
  
      writefile($file, <<EOF);
  [ req ]
  distinguished_name     = req_distinguished_name
  attributes             = req_attributes
  prompt                 = no
  default_bits           = 1024
  output_password        = $pass
  
  [ req_distinguished_name ]
  C                      = $dn->{C}
  ST                     = $dn->{ST}
  L                      = $dn->{L}
  O                      = $dn->{O}
  OU                     = $dn->{OU}
  CN                     = $dn->{CN}
  emailAddress           = $dn->{Email}
  
  [ req_attributes ]
  challengePassword      = $pass
  
  [ ca ]
  default_ca	       = CA_default
  
  [ CA_default ]
  certs            = certs        # Where the issued certs are kept
  new_certs_dir    = newcerts     # default place for new certs.
  crl_dir          = crl          # Where the issued crl are kept
  database         = index.txt    # database index file.
  serial           = serial       # The current serial number
  
  certificate      = $cacert      # The CA certificate
  crl              = $cacrl       # The current CRL
  private_key      = $cakey       # The private key
  
  default_days     = 365          # how long to certify for
  default_crl_days = 30           # how long before next CRL
  default_md       = md5          # which md to use.
  preserve         = no           # keep passed DN ordering
  
  [ policy_anything ]
  countryName		= optional
  stateOrProvinceName	= optional
  localityName		= optional
  organizationName	= optional
  organizationalUnitName	= optional
  commonName		= supplied
  emailAddress		= optional
  EOF
  
      return $file;
  }
  
  sub config {
      my $name = shift;
  
      my $file = config_file($name);
  
      my $config = "-config $file";
  
      $config;
  }
  
  #http://www.modssl.org/docs/2.8/ssl_reference.html#ToC21
  my $basic_auth_password = 'xxj31ZMTZzkVA';
  my $digest_auth_hash    = '$1$OXLyS...$Owx8s2/m9/gfkcRVXzgoE/';
  
  sub new_ca {
      writefile('index.txt', '', 1);
      writefile('serial', "01\n", 1);
  
      writefile('ssl.htpasswd',
                join ':', dn_oneline('client_snakeoil'),
                $basic_auth_password);
  
      openssl req => "-new -x509 -keyout $cakey -out $cacert $days",
                     config('cacert');
  }
  
  sub new_key {
      my $name = shift;
  
      my $encrypt = @_ ? "@_ $passout" : "";
  
      openssl genrsa => "-out keys/$name.pem $encrypt 1024";
  }
  
  sub new_cert {
      my $name = shift;
  
      openssl req => "-new -key keys/$name.pem -out csr/$name.csr",
                     $passin, $passout, config($name);
  
      sign_cert($name);
  
      export_cert($name);
  }
  
  sub sign_cert {
      my $name = shift;
  
      openssl ca => "$capolicy -in csr/$name.csr -out certs/$name.crt",
                    $passin, config($name), '-batch';
  }
  
  #handy for importing into a browser such as netscape
  sub export_cert {
      my $name = shift;
  
      return if $name =~ /^server/; #no point in exporting server certs
  
      openssl pkcs12 => "-export -in certs/$name.crt -inkey keys/$name.pem",
                        "-out export/$name.p12", $passin, $passout;
  }
  
  sub revoke_cert {
      my $name = shift;
  
      my @args = (config('cacrl'), $passin);
  
      #revokes in the index.txt database
      openssl ca => "-revoke certs/$name.crt", @args;
  
      #generates crl from the index.txt database
      openssl ca => "-gencrl -out $cacrl", @args;
  }
  
  sub setup {
      $CA = shift;
  
      unless ($ca_dn{$CA}) {
          die "unknown CA $CA";
      }
  
      gendir($CA);
  
      chdir $CA;
  
      init();
      new_ca();
  
      my @names = qw(server client_ok client_revoked client_snakeoil);
  
      for my $name (@names) {
          new_key($name);
          new_cert($name);
      }
  
      @names = qw(server_des3);
  
      for my $name (@names) {
          new_key($name, '-des3');
          new_cert($name);
      }
  
      revoke_cert('client_revoked');
  }
  
  sub generate {
      $Config = shift;
  
      my $root = $Config->{vars}->{sslca};
  
      return if -d $root;
  
      my $pwd  = Cwd::cwd();
      my $base = dirname $root;
      my $dir  = basename $root;
  
      chdir $base;
  
      #make a note that we created the tree
      $Config->clean_add_path($root);
  
      gendir($dir);
  
      chdir $dir;
  
      warning "generating SSL CA";
  
      setup('asf');
  
      chdir $pwd;
  }
  
  sub clean {
      my $config = shift;
  
      my $dir = $config->{vars}->{sslca};
  
      unless ($config->{clean}->{dirs}->{$dir}) {
          return; #we did not generate this ca
      }
  
      unless ($config->{clean_level} > 1) {
          #skip t/TEST -conf
          warning "skipping regeneration of SSL CA; run t/TEST -clean to force";
          return;
      }
  
      File::Path::rmtree([$dir], 1, 1);
  }
  
  #not using Apache::TestConfig methods because the openssl commands
  #will generate heaps of files we cannot keep track of
  
  sub writefile {
      my($file, $content) = @_;
  
      my $fh = Symbol::gensym();
      open $fh, ">$file" or die "open $file: $!";
      print $fh $content;
      close $fh;
  }
  
  sub gendir {
      my($dir) = @_;
  
      return if -d $dir;
      mkdir $dir, 0755;
  }
  
  1;
  __END__