#!/usr/bin/perl ## Program: # openssl_generate_user_req.pl # ## Purpose: # This script generates user certificate requests if OpenSSL is avalable. ## Options: # Mandatory: -u " " -i # Advised: -r # Other: -f (overwrite existing files); -h (help) # # See http://www.gac-grid.de/project-overview/VirtualOrganisations/Authorities_de.html # for a list of RAs and OUs in AstroGrid-D # Default: config-files are created in /tmp/certs and removed after successful run # Default: user-dir ~/.globus and all user*pem are created in $HOME/.globus # ## History: # Version 1.03 261006 IN Even more verbose output, link to RA page included etc., some fixes # Version 1.02 25102006 H. Enke Enke /AIP - removed the interative prompt for settings already there # Version 1.01 beta 251006 Iliya Nickelt GAVO /AIP - improvements, work in progress # Version 1.0 October 2006 Harry Enke Astrogrid-D / AIP # TO DO [251006 IN] # - improve _LOCAL_RA_ handling (automatic?) use Getopt::Std; getopts('u:i:r:hf'); #verifz, on openssl is available in the path" my $ossl="openssl"; my $cmd =`openssl version`; my $ra_email_default="GridKA-CA\@iwr.fzk.de"; if($cmd ){ print "Using $cmd \n"; }else{ die "*** ERROR: No openssl found in path!\nPlease make sure openssl is installed properly. Try \"locate openssl\"\n"; } my $pwd=$ENV{'PWD'}; if ($opt_h) { print "openssl_generate_user_req.pl -- a certificate request generation script using openssl.\n"; print "Options: -f (overwrites existing keys!); -u \"\"; -r \"\"; -d ; -h (help)\n"; print "Example: openssl_generate_user_req.pl -u \"Karl Schwarzschild\" -i AIP\ -r henke\@aip.de\n"; print "Remember to put quotes around your name.\n"; print "For a list of OUs and RAs see http://www.gac-grid.de/project-overview/VirtualOrganisations/Authorities.html\n"; die "\n"; } my $loc_ra=($opt_r)?$opt_r:$ra_email_default; my $hdr=$ENV{'HOME'}; my $dr="$hdr/.globus"; my $tdr="/tmp/certs"; my $usr=$ENV{'LOGNAME'}; my $inst =$opt_i; if(! $opt_f){ my $pemcheck=&_check_pems($dr); if (! $pemcheck) { print "*** ERROR: A certificate request and a key file already exist.\n"; print " To overwrite your current files, please call the program again using the \"-f\" option\n"; die "\n"; } } if(!($opt_i && $opt_u)) { print "Insufficient input: \"-u\" (user) and \"-i\" (institute) are mandatory and must be specified. Please use:\n"; print " openssl_generate_user_req.pl -u \"\\" -i \n"; print "Remember to put quotes around your name.\n"; print "For details use \'openssl_generate_user_req.pl -h\' or contact your local Registration Authority\n"; die "\n"; } if (! $opt_r){ print "\aWarning: RA's email address was not specified.\n"; print "We advise you to look up your local RA's email address at http://www.gac-grid.de/project-overview/VirtualOrganisations/Authorities.html\n"; print "and call the script using the additional option \"-r \"\n"; print "To interrupt you can press CTRL-C\n"; sleep 3; } $pwd=(-d $opt_d)?$opt_d:$pwd; my $CN=$opt_u; my $sw=($opt_f)?" -force":""; #first: generate config for the user if(! -d $dr){ my $rtv = mkdir($dr); die "*** ERROR: Not allowed to create $dr, cannot proceed\n" if(!$rtv); } if(! -d $tdr){ my $rtv = mkdir($tdr); die "Not allowed to create $tdr, cannot proceed\n" if(!$rtv); } my $userconf=&gen_conf_usr($CN,$inst); #my $hostcnf=&gen_conf_host($inst); #second: generate request with openssl my $rtv= &create_certs($dr,$tdr,$userconf,$ossl,$loc_ra); #third: collect request to $dir with prefix usr $CN=~ s/\ /\_/g; my $reqf="$dr/$CN\_usercert_request.pem"; system("cp $dr/usercert_request.pem $reqf"); print "\n\n*************************************************************************************\n"; print "Please check if your NAME (CN) and ORGANISATION (OU) are correct in your name string:\n\n"; system("grep \/O=GermanGrid $reqf"); print "*************************************************************************************\n"; print "\nIf there are any errors, call the script again with the \"-f\" option and supply your correct username.\n"; print "For more information, try \"openssl_generate_user_req.pl -h\" or contact your local Registrate Authority\n"; print "\nIf everything is correct, please email the request file\n \"$reqf\"\nas an attachment to your local Registration Authority: "; if ($loc_ra eq $ra_email_default) {print "\n** Look up the email address at http://www.gac-grid.de/project-overview/VirtualOrganisations/Authorities.html **\n";} else {print "$loc_ra.\n";} print "In that mail you must also include your office phone number and the number of your passport or identity card.\n"; print "In a few days you will recieve an email from the Root Certificate Authority with your signed certificate.\n\n"; # print "Please email the request \n $reqf \nto your local Registration Authority. \nIt will be then forwarded (signed) to the root-CA\n"; 1; sub _check_pems{ my ($dr)=@_; chdir($dr); my $flist=`ls *.pem 2>/dev/null`; my @ar=split(/\n/,$flist); return 1 if $#ar < 1; my $c=0; for my $f (@ar){ $c++; $c++ if(-s $f); next; } # return 0 for all files nonzero(likely a valid certpair), otherwise return 1 #return ($c % 2); return 0; } sub gen_conf_usr{ my ($cn,$inst)=@_; my $conf=' # # SSLeay example configuration file. # This is mostly being used for generation of certificate requests. # RANDFILE = $ENV::HOME/.rnd #################################################################### [ ca ] default_ca = CA_default # The default ca section #################################################################### [ CA_default ] dir = ./demoCA # Where everything is kept certs = $dir/certs # Where the issued certs are kept crl_dir = $dir/crl # Where the issued crl are kept database = $dir/index.txt # database index file. new_certs_dir = $dir/newcerts # default place for new certs. certificate = $dir/cacert.pem # The CA certificate serial = $dir/serial # The current serial number crl = $dir/crl.pem # The current CRL private_key = $dir/private/cakey.pem# The private key RANDFILE = $dir/private/.rand # private random number file x509_extensions = x509v3_extensions # The extentions to add to the cert default_days = 365 # how long to certify for default_crl_days= 365 # DEE 30 # how long before next CRL default_md = md5 # which md to use. preserve = no # keep passed DN ordering # A few difference way of specifying how similar the request should look # For type CA, the listed attributes must be the same, and the optional # and supplied fields are just that :-) policy = policy_match # For the CA policy [ policy_match ] countryName = optional stateOrProvinceName = optional organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional # For the \'anything\' policy # At this point in time, you must list all acceptable \'object\' # types. [ policy_anything ] countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional #################################################################### [ req ] default_bits = 1024 default_keyfile = privkey.pem distinguished_name = req_distinguished_name req_extensions = v3_req # TO DO: [IN 251006] # - Set org name default GermanGrid without user input # - Suppress org name level 1 and 2 # - Auto-detect common name? [ req_distinguished_name ] # BEGIN CONFIG 0.organizationName = Level 0 Organization 0.organizationName_default = GermanGrid 0.organizationalUnitName = Institute Acronym 0.organizationalUnitName_default = __INST__ 1.organizationalUnitName = Level 1 Organizational Unit 1.organizationalUnitName_default = 2.organizationalUnitName = Level 2 Organizational Unit 2.organizationalUnitName_default = commonName = Name (First Last) commonName_default = __CN__ commonName_max = 64 # END CONFIG [ v3_req ] nsCertType = objsign,email,server,client basicConstraints = critical,CA:false '; $conf=~ s/__INST__/$inst/; $conf=~ s/__CN__/$cn/; my $cf="$tdr/_usr_cert_conf"; open(FO,">$cf"); print FO $conf; close(FO); return $cf; } ########################################################### # create_request_header ########################################################### #shamelessly changed to perl-syntax from globus-cert-request sub create_request_header { my ($loc_ra,$certf,$subject,$ossl)=@_; my $hdr=' This is a Certificate Request file: It should be mailed to _LOCAL_RA_ ========================================================================= Certificate Subject: __SUBJECT__ The above string is known as your user certificate subject, and it uniquely identifies this user. To install this user certificate, please save this e-mail message into the following file. __CERT_FILE__ You need not edit this file in any way. Simply send an e-mail with this file attached to _LOCAL_RA_ If you have any questions about the certificate contact the LOCAL_RA at _LOCAL_RA_. '; $hdr=~ s/_LOCAL_RA_/$loc_ra/g; $hdr=~ s/__CERT_FILE__/$cert/g; $hdr=~ s/__SUBJECT__/$subject/g; return $hdr; } ########################################################### # create_certs # Create the certificate, key, and certificate request # files ########################################################### #shamelessly changed to perl-syntax from globus-cert-request sub create_certs { my ($dr,$tdr,$userconf,$ossl,$loc_ra)=@_; my $KEY_FILE="$dr/userkey.pem"; my $CERT_FILE="$dr/usercert.pem"; my $REQU_FILE="$dr/usercert_request.pem"; my $REQU_OUT="$tdr/usercert_request.out"; my $RAND_TEMP="$tdr/RandFile"; my $SSL_EXEC ="$ossl"; # commented out IN 251006 #print ' # "A certificate request and private key is being created." # "You will be asked to enter a PEM pass phrase." # "This pass phrase is akin to your account password, " # "and is used to protect your key file." # "If you forget your pass phrase, you will need to" # "obtain a new certificate." #'; #------------------------ # Create the Certificate File my $rtv = system("umask 022; touch $CERT_FILE"); #------------------------ # Create some semi random data for key generation my $rtv = system("umask 066; touch $RAND_TEMP"); if ( -e "/dev/urandom" ){ my $cmd="head -c 1000 /dev/urandom >> $RAND_TEMP 2>&1"; system($cmd); } my $cmd ="date >> $RAND_TEMP 2>&1 netstat -in >> $RAND_TEMP 2>&1 ps -ef >> $RAND_TEMP 2>&1 ls -ln $ENV{HOME} >> $RAND_TEMP 2>&1 ls -ln /tmp >> $RAND_TEMP 2>&1 umask 266 "; $rtv=system($cmd); #------------------------ # Create the Key and Request Files # Trick to avoid interactive questions for fields already filled my $REQUA = "$tdr/req_answer"; system("echo \"\n\n\n\n\"$CN\"\" > $REQUA"); my $cmd=" $SSL_EXEC req -new -keyout $KEY_FILE -out $REQU_OUT -rand $RAND_TEMP -config $userconf < $REQUA umask 022 "; $rtv = system($cmd); if ($rtv){ print "Error number $rtv was returned by $SSL_EXEC\n"; die "exiting, something wrong with your ssl\n"; } #------------------------ # Insert instructions into the request file my $SUBJECT=`${SSL_EXEC} req -text -noout < '$REQU_OUT' 2>&1 | grep 'Subject:' | awk -F: '{print $2}' | cut -c2- `; chomp($SUBJECT); $SUBJECT =~ s/.*\:\ //; $SUBJECT =~ s/,\ /\//g; $SUBJECT = "/".$SUBJECT; print "DN:>$SUBJECT<\n"; # die "\n"; #Convert the subject to the correct form. my $REQU_HEAD = &create_request_header($loc_ra,$certf,$SUBJECT, $ossl); # Finalize the Request file. open(FI,"<$REQU_OUT"); my @ar=; close(FI); open(FO,">$REQU_FILE"); print FO $REQU_HEAD; print FO (join " ",@ar); close(FO); $rtv=system("rm -rf $tdr"); }