# $Id$ # Hack to generate a small test repository for testing Apache + OpenSSL + RPKI use strict; my %resources; my %parent; my @ordering; my %hashes; my $openssl = "../../openssl/openssl/apps/openssl"; my $subdir = "apacheca"; my $passwd = "fnord"; my $keybits = 2048; my $verbose = 0; my $debug = 1; my $revoke = 0; sub openssl { print(STDERR join(" ", qw(+ openssl), @_), "\n") if ($debug); !system($openssl, @_) or die("openssl @_ returned $?\n"); } # Ok, this is a bit complicated, but the idea is to let us specify the # resources we're giving to each leaf entity and let the program do # the work of figuring out what resources each issuers need to have, # the order in which we need to generate the certificates, which # certificates need to sign which other certificates, etcetera. # # This would be much easier to read in a sane language (eg, Scheme). { my @ctx; my $loop ; $loop= sub { my $x = shift; if (ref($x) eq "HASH") { while (my ($k, $v) = each(%$x)) { $parent{$k} = $ctx[@ctx - 1]; push(@ordering, $k); push(@ctx, $k); $loop->($v); pop(@ctx); } } else { for my $c (@ctx) { push(@{$resources{$c}}, @$x) } } }; $loop->({ RIR => { LIR1 => { ISP1 => [IPv4 => "192.0.2.1-192.0.2.33", AS => "64533"], ISP2 => [IPv4 => "192.0.2.44-192.0.2.100"], }, LIR2 => { ISP3 => [IPv6 => "2001:db8::44-2001:db8::100"], ISP4 => [IPv6 => "2001:db8::10:0:44", AS => "64544"], }, }, }); } # Put this stuff into a subdirectory mkdir($subdir) unless (-d $subdir); chdir($subdir) or die; # Generate configurations for each entity. while (my ($entity, $resources) = each(%resources)) { my %r; print($entity, ":\n") if ($verbose); for (my $i = 0; $i < @$resources; $i += 2) { printf(" %4s: %s\n", $resources->[$i], $resources->[$i+1]) if ($verbose); push(@{$r{$resources->[$i]}}, $resources->[$i+1]); } open(F, ">${entity}.cnf") or die; print(F <<EOF); [ ca ] default_ca = ca_default [ ca_default ] certificate = ${entity}.cer serial = ${entity}/serial private_key = ${entity}.key database = ${entity}/index new_certs_dir = ${entity} name_opt = ca_default cert_opt = ca_default default_days = 365 default_crl_days = 30 default_md = sha1 preserve = no copy_extensions = copy policy = ca_policy_anything unique_subject = no [ ca_policy_anything ] countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional givenName = optional surname = optional [ req ] default_bits = $keybits encrypt_key = no distinguished_name = req_dn x509_extensions = req_x509_ext prompt = no [ req_dn ] CN = TEST ENTITY $entity [ req_x509_ext ] basicConstraints = critical,CA:true subjectKeyIdentifier = hash authorityKeyIdentifier = keyid keyUsage = critical,keyCertSign,cRLSign subjectInfoAccess = 1.3.6.1.5.5.7.48.5;URI:rsync://wombats-r-us.hactrn.net/ EOF print(F <<EOF) if ($parent{$entity}); authorityInfoAccess = caIssuers;URI:rsync://wombats-r-us.hactrn.net/$parent{$entity}.cer EOF print(F <<EOF) if ($r{AS} || $r{RDI}); sbgp-autonomousSysNum = critical,\@asid_ext EOF print(F <<EOF) if ($r{IPv4} || $r{IPv6}); sbgp-ipAddrBlock = critical,\@addr_ext EOF print(F <<EOF); [ asid_ext ] EOF for my $n (qw(AS RDI)) { my $i = 0; for my $a (@{$r{$n}}) { print(F "\t", $n, ".", $i++, " = ", $a, "\n"); } } print(F <<EOF); [ addr_ext ] EOF for my $n (qw(IPv4 IPv6)) { my $i = 0; for my $a (@{$r{$n}}) { print(F "\t", $n, ".", $i++, " = ", $a, "\n"); } } close(F); } # Revoke old certificates, maybe. if ($revoke) { for my $cert (glob("*/*.pem")) { my $conf = (split("/", $cert))[0] . ".cnf"; openssl("ca", "-verbose", "-config", $conf, "-revoke", $cert); unlink($cert); } } # Run OpenSSL to create the keys and certificates. We generate keys # separately to avoid wasting /dev/random bits if we need to change # the configuration. for my $entity (@ordering) { openssl("genrsa", "-out", "${entity}.key", $keybits) unless (-f "${entity}.key"); openssl("req", "-new", "-config", "${entity}.cnf", "-key", "${entity}.key", "-out", "${entity}.req"); mkdir($entity) unless (-d $entity); if (!-f "${entity}/index") { open(F, ">${entity}/index") or die; close(F); } if (!-f "${entity}/serial") { open(F, ">${entity}/serial") or die; print(F "01\n") or die; close(F); } openssl("ca", "-batch", "-verbose", "-out", "${entity}.cer", "-in", "${entity}.req", "-extensions", "req_x509_ext", "-extfile", "${entity}.cnf", ($parent{$entity} ? ("-config", "${parent{$entity}}.cnf") : ("-config", "${entity}.cnf", "-selfsign"))); } # Generate CRLs for my $entity (@ordering) { openssl("ca", "-batch", "-verbose", "-out", "${entity}.crl", "-config", "${entity}.cnf", "-gencrl"); } # Generate EE certs for my $parent (@ordering) { my $entity = "${parent}-EE"; open(F, ">${entity}.cnf") or die; print(F <<EOF); [ req ] default_bits = $keybits encrypt_key = no distinguished_name = req_dn x509_extensions = req_x509_ext prompt = no [ req_dn ] CN = TEST ENDPOINT ENTITY ${entity} [ req_x509_ext ] basicConstraints = critical,CA:false subjectKeyIdentifier = hash authorityKeyIdentifier = keyid subjectInfoAccess = 1.3.6.1.5.5.7.48.5;URI:rsync://wombats-r-us.hactrn.net/ authorityInfoAccess = caIssuers;URI:rsync://wombats-r-us.hactrn.net/$parent.cer EOF close(F); openssl("genrsa", "-out", "${entity}.key", $keybits) unless (-f "${entity}.key"); openssl("req", "-new", "-config", "${entity}.cnf", "-key", "${entity}.key", "-out", "${entity}.req"); mkdir($entity) unless (-d $entity); if (!-f "${entity}/index") { open(F, ">${entity}/index") or die; close(F); } if (!-f "${entity}/serial") { open(F, ">${entity}/serial") or die; print(F "01\n") or die; close(F); } openssl("ca", "-batch", "-verbose", "-config", "${parent}.cnf", "-extensions", "req_x509_ext", "-extfile", "${entity}.cnf", "-out", "${entity}.cer", "-in", "${entity}.req"); } # Generate hashes for my $cert (map({("$_.cer", "$_-EE.cer")} @ordering)) { my $hash = `$openssl x509 -noout -hash -in $cert`; chomp($hash); $hash .= "."; $hash .= (0 + $hashes{$hash}++); unlink($hash) if (-l $hash); symlink($cert, $hash) or die("Couldn't link $hash to $cert: $!\n"); } for my $crl (map({"$_.crl"} @ordering)) { my $hash = `$openssl crl -noout -hash -in $crl`; chomp($hash); $hash .= ".r"; $hash .= (0 + $hashes{$hash}++); unlink($hash) if (-l $hash); symlink($crl, $hash) or die("Couldn't link $hash to $crl: $!\n"); } # Generate PKCS12 forms of EE certificates # -chain argument to pkcs12 requires certificate store, which we configure via an environment variable $ENV{SSL_CERT_DIR} = do { my $pwd = `pwd`; chomp($pwd); $pwd; }; for my $ee (map({"$_-EE"} @ordering)) { my @cmd = ("pkcs12", "-export", "-in", "$ee.cer", "-inkey", "$ee.key", "-password", "pass:$passwd"); openssl(@cmd, "-out", "$ee.p12"); openssl(@cmd, "-out", "$ee.chain.p12", "-chain"); } # Finally, generate an unrelated self-signed certificate for the server my $hostname = `hostname`; chomp($hostname); open(F, ">server.cnf") or die; print(F <<EOF); [ req ] default_bits = $keybits encrypt_key = no distinguished_name = req_dn prompt = no [ req_dn ] CN = $hostname EOF close(F); openssl(qw(genrsa -out server.key), $keybits) unless (-f "server.key"); openssl(qw(req -new -config server.cnf -key server.key -out server.req)); openssl(qw(x509 -req -CAcreateserial -in server.req -out server.cer -signkey server.key)); # Local Variables: # compile-command: "perl generate-testrepo.pl" # End: