I have a script that generates CSRs and can integrate with windows CA to sign, but I want to have the option to sign with a local CA, so I was using the openssl_sign_csr function. And it is signing the certificates, however, it does not retain the x509 attributes (namely, extended key usage and subject alternative names).
I pass it the config file and I've tried a few samples that I've found on the internet, the key things that im including is the copy_extensions = copy
and x509_extensions = usr_cert
with the usr_cert block having:
basicConstraints=CA:FALSE
nsComment = "OpenSSL Generated Certificate"
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
subjectAltName=email:copy
however, any mix of config that doesn't result in an error does not carry across any of the extensions. the above applied the email field to the SAN instead. i cannot find a copy option. but also, I'd like to have it apply the CN field if the SAN is not specified.
I know the CSR has them because when signed by certsrv it carries them.
Would love some insight from anyone that has gotten this working in the past. (also, key usage will be for server identification, rarely client identification, if that makes any difference to it)
The copy_extensions
option can be used with the command-line version of OpenSSL. It doesn't seem possible
to use it with the PHP OpenSSL extension.
As a workaround, you can extract the informations you need from the CSR. It is possible to do so with the help of the phpseclib library (as mentioned here):
require('vendor/autoload.php');
use phpseclib3\File\X509;
function getCsrData(string $csrFile): array
{
$csr = file_get_contents($csrFile);
if(!$csr)
throw new Exception('CSR not found');
$x509 = new X509();
if(!$x509->loadCSR($csr))
throw new Exception('Invalid CSR');
// Get the Common Name
$data['CN'] = $x509->getDNProp('CN')[0];
// Get the Subject Alternative Name
$SAN = $x509->getExtension('id-ce-subjectAltName');
if($SAN)
{
$arr = [];
foreach($SAN as $val)
$arr[] = 'DNS:' . $val['dNSName'];
$data['subjectAltName'] = implode(', ', $arr);
}
// Get the Extended Key Usage
$XKU = $x509->getExtension('id-ce-extKeyUsage');
if($XKU)
{
$arr = [];
foreach($XKU as $val)
$arr[] = substr($val, 6);
$data['extKeyUsage'] = implode(', ', $arr);
}
return $data;
}
Example of a CSR generation:
openssl req -new -sha256 -nodes -newkey rsa:2048 -keyout test.key -out test.csr -config test.cnf
with the following config file (test.cnf):
[req]
distinguished_name = dn
req_extensions = req_ext
prompt = no
[dn]
CN = www.domain.com
[req_ext]
subjectAltName = DNS:www.domain.com, DNS:domain.com
extendedKeyUsage = serverAuth
The CSR is parsed like this:
$data = getCsrData('test.csr');
var_export($data);
Output:
array (
'CN' => 'www.domain.com',
'subjectAltName' => 'DNS:www.domain.com, DNS:domain.com',
'extKeyUsage' => 'serverAuth',
)