<?php

namespace modules\phpseclib;
	
class main extends \moduleMain {
	
	const OPENSSL = '/usr/bin/openssl';

	public function __construct() {
		parent::__construct(__DIR__);
	}
	
	public function Activate() {
		
		hook_add('phpseclib.generatecsr', [$this, '__generatecsr']);
		hook_add('phpseclib.loadcert', [$this, '__loadcert']);
		hook_add('phpseclib.subject', [$this, '__subject']);			// extract the subject from a certificate
		hook_add('phpseclib.wildcard', [$this, '__wildcard']);			// does this certificate contain a wildcard?
	}

	function _ssl_generateConfigFile($aliases, $full) {
		$alt = '';
		$entrycount = 0;
		foreach((array)$aliases as $index => $item) {
			$index++;
			$alt .= 'DNS.' . $index . '=' . $item . "\n";
			$entrycount++;
		}
		
	if ($full === 1) {			// info needed to create the csr
			$body = <<<BLOCK
[req]
default_bits=2048
prompt=no
default_md=sha256
distinguished_name=dn
x509_extensions = v3_ca

[v3_ca]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
keyUsage = digitalSignature, keyEncipherment
basicConstraints = CA:false
subjectAltName      = @alt_names

[dn]
CN={$aliases[0]}

[alt_names]
{$alt}

BLOCK;

		} elseif ($full) {
			$body = <<<BLOCK
[req]
default_bits=2048
prompt=no
default_md=sha256
req_extensions=req_ext
distinguished_name=dn
x509_extensions = v3_ca
 
[dn]
CN={$aliases[0]}

[req_ext]
subjectAltName=@alt_names

[alt_names]
{$alt}

[ v3_ca ]

subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints = CA:true
subjectAltName      = @alt_names
keyUsage = digitalSignature, keyEncipherment

[ 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
crlnumber       = \$dir/crlnumber        # the current crl number
                                        # must be commented out to leave a V1 CRL
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 = usr_cert              # The extentions to add to the cert

name_opt        = ca_default            # Subject Name options
cert_opt        = ca_default            # Certificate field options

copy_extensions = copy

default_days    = 365                   # how long to certify for
default_crl_days= 30                    # how long before next CRL
default_md      = default               # use public key default MD
preserve        = no                    # keep passed DN ordering
policy          = policy_match

# For the CA policy
[ policy_match ]
countryName             = match
stateOrProvinceName     = match
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

BLOCK;
		
		} elseif ($entrycount == 1) {		// COMODO wildcard:  no SANs for just one domain

			$body = <<<BLOCK
[req]
default_bits=2048
prompt=no
default_md=sha256
distinguished_name=dn
 
[dn]
CN={$aliases[0]}

BLOCK;
		} else {

	// [dn] => values in this section cannot be empty
		
			$body = <<<BLOCK
[req]
default_bits=2048
prompt=no
default_md=sha256
req_extensions=req_ext
distinguished_name=dn
 
[dn]
CN={$aliases[0]}

[req_ext]
subjectAltName=@alt_names

[alt_names]
{$alt}

BLOCK;
		}

		return $body;
	}

//http://blog.endpoint.com/2014/10/openssl-csr-with-alternative-names-one.html
	public function __generatecsr(&$result, $hosts, $options = []) {

		// to do: extend this to include company details, state, etc
		if (!isset($result['privatekey'])) {
			
			$rsa = \phpseclib3\Crypt\RSA::createKey(2048);
			$pub = $rsa->getPublicKey();
			$result['privatekey']  = $rsa->toString('PKCS8');
			$result['fingerprint'] = $pub->getFingerprint('md5');
			$result['publickey']   = $pub->toString('PKCS8');
		}

        $full = array_key_exists('altnames', $options) ? $options['altnames'] : FALSE;

		$result['config'] = $this->_ssl_generateConfigFile($hosts, $full);

		if (!$full || $full == 1) {
            $dir = sys_get_temp_dir();
			$keyfile = tempnam($dir, 'key');
			file_put_contents($keyfile, $result['privatekey']);
			$configfile = tempnam($dir, 'cfg');
			file_put_contents($configfile, $result['config']);
			$csrfile = tempnam($dir, 'csr');
			
			$path = self::OPENSSL;	//  -newhdr
			$command = "$path req -new -key $keyfile -config $configfile -out $csrfile";
			`$command`;
			$csr = file_get_contents($csrfile);
			
			@unlink($keyfile);
			@unlink($csrfile);
			@unlink($configfile);
			$result['csr'] = $csr;
		}
		
	}
	
	public function __loadcert(&$result, $certificatedata) {
		$x509 = new \phpseclib3\File\X509;
		$result = $x509->loadX509($certificatedata);
	}

	public function __subject(&$result, $certificatedata) {
		if (is_null($result)) {
			$x509 = new \phpseclib3\File\X509;
			$x509->loadX509($certificatedata);
			$subjects = $x509->getSubjectDN()['rdnSequence'];
			foreach($subjects as $list) {
				$subject = $list[0];
				if ($subject['type'] == 'id-at-commonName') {
					if (isset($subject['value']['utf8String'])) {
						$result = $subject['value']['utf8String'];
						return;
						
					} elseif (isset($subject['value']['printableString'])) {
						$result = $subject['value']['printableString'];
						return;
					}
				}
			}
		}
	}

	public function __wildcard(&$result, $certificatedata) {
		if (is_null($result)) {
			$x509 = new \phpseclib3\File\X509;
			$x509->loadX509($certificatedata);
			
			$ext = array_values($x509->getExtension('id-ce-subjectAltName'));
			foreach($ext as $entry) {
				$name = $entry['dNSName'];
				if (strpos($name, '*') !== FALSE) {
					$result = $name;
					return;
				}
			}
		}
	}

}

/*

function ShowCSRasText($csr) {
        $csrname = _SavePrivateKey($csr);
		$path = PATH_OPENSSL;
        $data = `$path req -noout -text -in $csrname`;
        unlink($csrname);
        return wraptext($data);
}

*/
