<?php
// ----------------------------------------------
function openssl_check_functions ()
{
 $Result   = '';
 $OpenSSLs = array
 (
  'openssl_csr_new',
  'openssl_csr_sign',
  'openssl_csr_export_to_file',
  'openssl_pkey_new',
  'openssl_pkey_get_private',
  'openssl_pkey_export_to_file',
  'openssl_x509_export_to_file'
 );

 foreach ($OpenSSLs as $Function)
 {
  $Result .= $Function . '<font color="black">:</font> ' . (function_exists ($Function) ?
                                '<font color="black">OK</font>' :
                                '<font color="red">DOES NOT EXIST</font>') . '<br>';
 }    

 return $Result;
}

// ----------------------------------------------
function openssl_load_database ($afile = '')
{
 global $config;
 global $openssl;

 $afile = ($afile == '' ? $config['openssl']['database'] : $afile);
 $lines = file ($afile);

 if (!is_array ($lines))
  exit;

 foreach ($lines as $line_num => $line)
 {
  chomp ($line);
  $linetokens = explode ("\t", $line);

  // Ensure that all the fields are set
  if (count ($linetokens) == 6)
  {
   // Decode the openssl's database. See apps/apps.h
   $openssl['Database'][] = array ('Status'     => $linetokens[0],
                                   'ExpDate'    => $linetokens[1],
                                   'RevDate'    => $linetokens[2],
                                   'Serial'     => $linetokens[3],
                                   'File'       => $linetokens[4],
                                   'Name'       => $linetokens[5],
                                   'Country'    => openssl_get_country    ($linetokens[5]),
                                   'State'      => openssl_get_state      ($linetokens[5]),
                                   'City'       => openssl_get_city       ($linetokens[5]),
                                   'Company'    => openssl_get_company    ($linetokens[5]),
                                   'Department' => openssl_get_department ($linetokens[5]),
                                   'CN'         => openssl_get_CN         ($linetokens[5]),
                                   'Email'      => openssl_get_email      ($linetokens[5])
                                  );
  }
 }
}

// ----------------------------------------------
function openssl_write_database ($afile = '')
{
 global $config;
 global $openssl;

 $afile = ($afile == '' ? $config['openssl']['database'] : $afile);

 $atext = '';
 for ($i = 0; $i < count ($openssl['Database']); $i++)
 {
  $atext .= ($atext == '' ? '' : "\n");
  $atext .= $openssl['Database'][$i]['Status'] ."\t".
            $openssl['Database'][$i]['ExpDate'] ."\t".
            $openssl['Database'][$i]['RevDate'] ."\t".
            $openssl['Database'][$i]['Serial'] ."\t".
            $openssl['Database'][$i]['File'] ."\t".
            $openssl['Database'][$i]['Name'];
 }

 writefile ($afile, $atext, $afile .'.old');
}

// ----------------------------------------------
function openssl_write_database_attr ($atext = '', $afile = '')
{
 global $config;
 global $openssl;

 $afile = ($afile == '' ? $config['openssl']['database'] : $afile) .'.attr';
 $atext = ($atext == '' ? "unique_subject = yes\n"       : $atext);

 if (file_exists ($afile))
 {
  ob_start ();
  readfile ($afile);
  $atext = ob_get_contents ();
  ob_end_clean ();
 }

 writefile ($afile, $atext, $afile .'.old');
}

// ----------------------------------------------
// Returns the PEM file with spaces reduced and replaced to &nbsp;
function openssl_load_cert ($anid)
{
 global $config;

 do
 {
  $lines = file ($config['openssl']['pubfolder'] . $anid . '.pem');

  if (!is_array ($lines))
  {
   $Return = '';
   break;
  }

  foreach ($lines as $line_num => $line)
  {
   chomp ($line);
   $Return[] = str_replace (' ', '&nbsp;', htmlspecialchars (str_replace ('    ', ' ', $line)));
  }
 } while (FALSE);

 return $Return;
}

// ----------------------------------------------
function openssl_load_serial ($afile = '')
{
 global $config;

 $afile = ($afile == '' ? $config['openssl']['serial'] : $afile);

 $lines = file ($afile);
 
 if (!is_array ($lines))
  exit;

 $Return = sscanf ($lines[0], "%X");

 return $Return[0];
}

// ----------------------------------------------
function openssl_write_serial ($iNumber, $afile = '')
{
 global $config;

 $afile = ($afile == '' ? $config['openssl']['serial'] : $afile);

 writefile ($afile, openssl_hex_serial ($iNumber) . "\n", $afile .'.old');
}

// ----------------------------------------------
// Supports up to 999,999 serials
function openssl_hex_serial ($iNumber)
{
 if ($iNumber < 100)
  $sString = sprintf ("%02X", $iNumber);
 elseif ($iNumber < 10000)
  $sString = sprintf ("%04X", $iNumber);
 else
  $sString = sprintf ("%06X", $iNumber);

 return $sString;
}

// ----------------------------------------------
// Builds User Private Key, CSR and Public Certificate
function openssl_build_key (&$anoutput, $adn, $validdays = NULL)
{
 global $config;
 global $openssl;

 $anoutput = '';
 $Return  = FALSE;

 // Allow to override default value
 $validdays = ($validdays == NULL ? $config['openssl']['default']['expiration'] : $validdays);

 do
 {
  if (!isset ($adn) ||
      !isset ($adn['countryName']) ||
      !isset ($adn['stateOrProvinceName']) ||
      !isset ($adn['localityName']) ||
      !isset ($adn['organizationName']) ||
      !isset ($adn['organizationalUnitName']) ||
      !isset ($adn['commonName']) ||
      !isset ($adn['emailAddress'])
     )
  { $anoutput .= "- ERROR on ". __LINE__ ." line: incomplete DN information\n"; break; }
  $anoutput .= "+ OK got the valid input\n";

  // Get the new User Private Key
  $UserPrivKey = openssl_pkey_new (array($config['openssl']['config'],0));
  if ($UserPrivKey == FALSE)
  { $anoutput .= "- ERROR on ". (__LINE__ - 2) ." line (openssl_pkey_new):\n  ". openssl_error_strings () ."  (that might usually mean that the openssl.cnf file is unavailable)"; break; }
  $anoutput .= "+ OK got the User Private Key\n";

  // Generate the User Certificate Request
  $UserReq = openssl_csr_new ($adn,
                              $UserPrivKey,
                              $config['openssl']['config']);
  if ($UserReq == FALSE)
  { $anoutput .= "- ERROR on ". (__LINE__ - 4) ." line (openssl_csr_new):\n  ". openssl_error_strings (); break; }
  $anoutput .= "+ OK generated the User Certificate Request\n";

  // Read the openssl serial
  $CAserial = openssl_load_serial ($config['openssl']['serial']);
  $anoutput .= "+ OK read current openssl serial (". openssl_hex_serial ($CAserial) .")\n";

  $UserPubCertFile   = $config['openssl']['pubfolder'] . openssl_hex_serial ($CAserial) .'.pem';
  $UserCertReqFile   = $config['openssl']['reqfolder'] . openssl_hex_serial ($CAserial) .'.csr';
  $UserPrivKeyFile   = $config['openssl']['prvfolder'] . openssl_hex_serial ($CAserial) .'.key';

  // Read the openssl database
  openssl_load_database  ($config['openssl']['database']);
  $anoutput .= "+ OK read the openssl database (". count ($openssl['Database']) ." items)\n";

  // Get CA's Private Key
  $CAPrivKey = openssl_pkey_get_private ($config['openssl']['CA']['priv']);

  if ($CAPrivKey == FALSE)
  { $anoutput .= "- ERROR on ". (__LINE__ - 2) ." line (openssl_pkey_get_private)\n  ". openssl_error_strings (); break; }
  $anoutput .= "+ OK read the CA Private Key\n";

  // Get a CA-signed cert that lasts for 1 year
  $UserPubCert = openssl_csr_sign ($UserReq,
                                   $config['openssl']['CA']['pub'],
                                   $CAPrivKey,
                                   $validdays,
                                   $config['openssl']['config'],
                                   $CAserial);
  if ($UserPubCert == FALSE)
  { $anoutput .= "- ERROR on ". (__LINE__ - 7) ." line (openssl_csr_sign)\n  ". openssl_error_strings (); break; }
  $anoutput .= "+ OK signed the User Certificate Request with CA Private Key\n";

  // Add the new row into openssl database
  $openssl['Database'][] = array ('Status'     => 'V',
                                  'ExpDate'    => date ('ymdHis',
                                                        time() +
                                                        date ('Z') +
                                                        ($validdays * 24 * 60 * 60)) .'Z',
                                  'RevDate'    => '',
                                  'Serial'     => openssl_hex_serial ($CAserial),
                                  'File'       => openssl_hex_serial ($CAserial) .'.crt',
                                  'Name'       => openssl_make_name ($adn)
                                 );

  // Create files

  $OldUMask = umask (0007);

  // Write User Private Key 
  if (!openssl_pkey_export_to_file ($UserPrivKey, $UserPrivKeyFile, NULL, $config['openssl']['config']))
  { $anoutput .= "- ERROR on ". (__LINE__ - 1) ." line (openssl_pkey_export_to_file)\n  ". openssl_error_strings () ."  (That might mean that the key folder is not write enabled for www user)"; break; }
  $anoutput .= "+ OK wrote User Private Key into file $UserPrivKeyFile\n";

  // Write User Public Certificate
  if (!openssl_x509_export_to_file ($UserPubCert, $UserPubCertFile, FALSE))
  { $anoutput .= "- ERROR on ". (__LINE__ - 1) ." line (openssl_x509_export_to_file)\n  ". openssl_error_strings (); break; }
  $anoutput .= "+ OK wrote User Public Certificate into file $UserPubCertFile\n";

  // Write User Certificate Request
  if (!openssl_csr_export_to_file ($UserReq, $UserCertReqFile))
  { $anoutput .= "- ERROR on ". (__LINE__ - 1) ." line (openssl_csr_export_to_file)\n  ". openssl_error_strings (); break; }
  $anoutput .= "+ OK wrote User Certificate Request into file $UserCertReqFile\n";

  // Write new openssl database
  openssl_write_database ($config['openssl']['database']);
  openssl_write_database_attr ('', $config['openssl']['database']);
  $anoutput .= "+ OK wrote new openssl database\n";

  // Write new openssl serial
  openssl_write_serial ($CAserial + 1, $config['openssl']['serial']);
  $anoutput .= "+ OK wrote new openssl serial\n";

  umask ($OldUMask);

  $Return = openssl_hex_serial ($CAserial);
 } while (FALSE);

 return $Return;
}

// ----------------------------------------------
function openssl_error_strings ()
{
 $sString = '';
 while ($msg = openssl_error_string ())
  $sString .= $msg ."\n";

 return $sString;
}

// ----------------------------------------------
function openssl_make_name ($adn)
{
 $sString = '';

 if (strlen ($adn['countryName'])            > 0) $sString .= '/C='           . $adn['countryName'];
 if (strlen ($adn['stateOrProvinceName'])    > 0) $sString .= '/ST='          . $adn['stateOrProvinceName'];
 if (strlen ($adn['localityName'])           > 0) $sString .= '/L='           . $adn['localityName'];
 if (strlen ($adn['organizationName'])       > 0) $sString .= '/O='           . $adn['organizationName'];
 if (strlen ($adn['organizationalUnitName']) > 0) $sString .= '/OU='          . $adn['organizationalUnitName'];
 if (strlen ($adn['commonName'])             > 0) $sString .= '/CN='          . $adn['commonName'];
 if (strlen ($adn['emailAddress'])           > 0) $sString .= '/emailAddress='. $adn['emailAddress'];

 return $sString;
}

// ----------------------------------------------
function openssl_get_country ($aline)
{ return str_get_sometag ($aline . '/', '\/C=([^/]*)\/'); }

// ----------------------------------------------
function openssl_get_state ($aline)
{ return str_get_sometag ($aline . '/', '\/ST=([^/]*)\/'); }

// ----------------------------------------------
function openssl_get_city ($aline)
{ return str_get_sometag ($aline . '/', '\/L=([^/]*)\/'); }

// ----------------------------------------------
function openssl_get_company ($aline)
{ return str_get_sometag ($aline . '/', '\/O=([^/]*)\/'); }

// ----------------------------------------------
function openssl_get_department ($aline)
{ return str_get_sometag ($aline . '/', '\/OU=([^/]*)\/'); }

// ----------------------------------------------
function openssl_get_CN ($aline)
{ return str_get_sometag ($aline . '/', '\/CN=([^/]*)\/'); }

// ----------------------------------------------
function openssl_get_email ($aline)
{ return str_get_sometag ($aline . '/', '\/emailAddress=([^/]*)\/'); }

// ----------------------------------------------
// Guess the full file name
function openssl_get_filename ($iSerial, $sExt)
{
 global $config;

 $sReturn = $config['openssl']['folder'] . openssl_hex_serial ($iSerial) . $sExt;
 if (!file_exists ($sReturn))
 {
  $sReturn = $config['openssl']['pubfolder'] . openssl_hex_serial ($iSerial) . $sExt;
  if (!file_exists ($sReturn))
  {
   $sReturn = $config['openssl']['reqfolder'] . openssl_hex_serial ($iSerial) . $sExt;
   if (!file_exists ($sReturn))
   {
    $sReturn = $config['openssl']['prvfolder'] . openssl_hex_serial ($iSerial) . $sExt;
    if (!file_exists ($sReturn))
    {
     $sReturn = '';
    }
   }
  }
 }

 return $sReturn;
}


?>
