Dr. Stephen Henson wrote:

> You should where possible use the command line switches
> rather than "expect" because the prompts of the various
> commands may change.

> You can generate requests via template configuration
> files and there are various ways to supply passphrases.

While I agree 100% with the thrust of what Stephen is saying,
the sad reality is that there are real problems with OpenSSL
in this area.

For example, tell me how to specify a specific serial number
on a C-language call like:

   execle(SSLBPATH,SSLBPATH,"x509","-req","-sha1",
      "-extfile",SPKICONF,
      "-CA","./ssign.cert.pem",
      "-CAkey","./ssign.key.pem",
      "-CAserial",snumbuff,
      "-days","365",
/*    "-passin","fd:fileno(KDR)", */
      "-passin","pass:aaaaa",
   0,env);

Well, OK, I can do

   int sn[2];
   pipe(sn);
   sprintf(snumbuff,"%lx",serial);
   write(sn[1],snumbuff,strlen(snumbuff));
   sprintf(snumbuff,"/dev/fd/%d",sn[0]);

(ignoring for the moment that it has to be an even number of
hex bytes, the code is a *little* more complicated, see below)

but this is currently failing because the /dev/fd directory on
this machine does not exist, and I have to get back to the
systems people to find out if this is a bug or a feature...

Likewise with the passphrase, I earlier found a bug with
specifying both "-passin fd:#" and "-passout fd:#" on the same
OpenSSL call (was rsa to change pass phrase I think overlapping
buffers or something :-) so when I take out the "aaaaa" above
and make it

   int pp[2];
   pipe(pp);
   sprintf(passbuff,"/dev/fd/%d",pp[0]);
   /* arrange for passphrase to be written to pp[1] */

But this also relies on operating system support for /dev/fd/#.

(actual code considering length must be even:)

   pipe(sn);
   sprintf(snumbuff,"0%lx",serial);
   pid = strlen(snumbuff);
   if (1 & pid) {
      write(sn[1],snumbuff+1,pid-1);
   } else {
      write(sn[1],snumbuff,pid);
   }
   sprintf(snumbuff,"/dev/fd/%d",sn[0]);
   close(sn[1]);

=====

The worst case I came up against required me to run a pipe of
three different commands.  There just seemed to be NO WAY to
specify a passphrase for the CA command, so I ended up with
an explicit call to OpenSSL rsa (second command in below pipe)
just to get the private key decoded.  Apologies if I missed
something, but I did futz around for a good amount of time
before doing it this way.

I don't recall why this code uses a temp file for the serial
number instead of using another pipe.  Maybe it didn't work
at the time, or maybe I didn't think about it.  This work was
with 0.9.6c or so, and it is possible some of these points
have been addressed in the evolution to 0.9.7

If there is a better way, score some points on me by telling
me about it...

# ##### SPKCSIGN #####

# Call OpenSSL ca to sign a SPKAC or PKCS10

# Because of various limitations in the OpenSSL code,
# this routine runs a pipe of three processes.
# 1. Vault program writes a passphrase to stdout (bound to pipe PW/PR)
# 2. An instance of OpenSSL rsa
#    * reads the passphrase from a -passin fd:<fileno(PR)>
#    * reads the encrypted private key from a -in file argument
#    * writes unencrypted private key to stdout (bound to pipe KW/KR).
# 3. An instance of OpenSSL ca
#    * reads unencrypted private key from
#      a -privateKey /dev/fd/<fileno(KR)>
#    * reads the SPKAC from a -spkac /dev/fd<fileno(SR)>
#    * writes the signed certificate to stdout (bound to pipe CW/CR).
# Standard error from all three is bound to pipe EW/ER.
#
# This routine writes the SPKAC data to SW and reads the signed
# certificate from CR, and any errors from ER.
#
#                                              SPKAC
#                                      +-------------------+
#                                   SR |                   ^ SW
#          PASS               KEY      v        CERT       |
#   vault -----> OpenSSL rsa -----> OpenSSL ca -----> this routine
#  EW |   PW  PR     | EW    KW  KR    | EW    CW  CR      ^ ER
#     v              v                 v                   |
#     +--------------+-----------------+-------------------+

sub spkcsign {
   my ($vault,$vkey,$openssl,$tmpdir,$serial,$req,$certlife,$certmail)
      = @_;
   my $pid, $error, $cert;              # Proc ID, error, result strs

# Make serial number as even-number-of-digits hex string and write file

   my $hex = sprintf("%lX",$serial);    # Convert serial to hex
   if ( length($hex) % 2 ) {
      $hex = '0'.$hex;                  # Requires even num digits!
   }
   my $snf = "$tmpdir/pca.serial.$$";   # Serial num file in config file
   open SERIAL,">$snf";                 # Open write to file
   print SERIAL $hex;                   # Write serial number to file
   close SERIAL;                        # Close file

# Make empty initial database file

   my $dbf = "$tmpdir/pca.dbf.$$";      # Database file
   open DBF,">$dbf";                    # Write empty database file
   close DBF;                           # Close file

# Copy passphrase from the vault into P pipe.

   pipe ER, EW;                         # Read and write for stderr pipe
   {
      $^F = 99;                         # FORCE CLOSE-ON-WRITE OFF!!!
      pipe PR, PW;                      # R & W for passphrase pipe
   }
   if ( !forkcode(sub{                  # Run code in forked process
      close PR;                         # Close parent's pipe end
      close ER;                         # Close parent's pipe end
      open STDOUT,'>&PW';               # Bind standard out to pipe
      open STDERR,'>&EW';               # Bind standard err to pipe
      exec $vault,$vkey;                # Send passphrase to PW
      die "Could not EXEC vault (spkcsign): $!"; # SHOULD NOT BE REACHED
   }) ) {
      htmlfail "Could not FORK vault (spkcsign): $!";
   }
   close PW;                            # Close kid's pipe end

# Run OpenSSL rsa to decrypt the private key

   {
      $^F = 99;                         # FORCE CLOSE-ON-WRITE OFF!!!
      pipe KR, KW;                      # R & W for priv key pipe
   }
   if ( !forkcode(sub{                  # Run code in forked process
      close KR;                         # Close parent's pipe end
      close ER;                         # Close parent's pipe end
      open STDOUT,'>&KW';               # Bind standard out to pipe
      open STDERR,'>&EW';               # Bind standard err to pipe
      exec $openssl.' rsa -passin fd:'.fileno(PR).
                    ' -in ../certs/testca.key.pem';
      die "Could not EXEC OpenSSL rsa (spkcsign): $!"; # NOT REACHED
   }) ) {
      htmlfail "Could not FORK OpenSSL rsa (SPKCSIGN): $1";
   }
   close PR;                            # Close pass phrase pipe
   close KW;                            # Close kid's pipe end

# Run OpenSSL ca to sign the certificate

   pipe CR, CW;                         # Read and write for cert pipe
   {
      $^F = 99;                         # FORCE CLOSE-ON-WRITE OFF!!!
      pipe SR, SW;                      # Read and write for SPKAC pipe
   }
   if ( !($pid=forkcode(sub{            # Run code in forked process
      $ENV{'CERTMAIL'} = $certmail;     # Add altname to extension
      $ENV{'CERTSERN'} = $snf;          # Set serial number file
      $ENV{'CADBF'} = $dbf;             # Set database file
      close CR;                         # Close parent's pipe end
      close ER;                         # Close parent's pipe end
      close SW;                         # Close parent's pipe end
      open STDOUT,'>&CW';               # Bind standard out to pipe
      open STDERR,'>&EW';               # Bind standard err to pipe
      exec $openssl.' ca -config ../config/pconfig'.
         " -days $certlife".
         ' -batch -notext -keyfile /dev/fd/'.fileno(KR).
         ' -spkac /dev/fd/'.fileno(SR);
      die "Could not EXEC OpenSSL ca (spkcsign): $!"; # NOT REACHED
   })) ) {
      htmlfail "Could not FORK OpenSSL ca (spkcsign): $!" ;
   }
   close KR;                            # Close pipe from OpenSSL rsa
   close CW;                            # Close kid's pipe end
   close EW;                            # Close kid's pipe end
   close SR;                            # Close kid's pipe end
   print SW $req;                       # Send SPKAC data to pipe
   close SW;                            # Make EOF on pipe
   read ER,$error,4096;                 # Read any errors from kid
   read CR,$cert,4096;                  # Read any output from kid
   waitpid($pid,0);                     # Wait for kid to terminate
   if ($?) {
      htmlfail "OpenSSL ca (spkcsign):\n".$error;
   }
   close CR;                            # Close pipe from kid
   close ER;                            # Close pipe from kid
   unlink "$dbf.old", "$snf.old";       # Unlink old filenames
   unlink $dbf, $snf, "$tmpdir/$hex.pem"; # Unlink new filnames

   return $cert;                        # Return certificate to caller
}

--
Charles B (Ben) Cranston
mailto: [EMAIL PROTECTED]
http://www.wam.umd.edu/~zben

______________________________________________________________________
OpenSSL Project                                 http://www.openssl.org
User Support Mailing List                    [EMAIL PROTECTED]
Automated List Manager                           [EMAIL PROTECTED]

Reply via email to