<?php$cert = openssl_x509_read(file_get_contents('cert.pem')); // PEM text, or 'file://' . $pathif ($cert === false) { throw new RuntimeException('not a certificate: ' . openssl_error_string());} $info = openssl_x509_parse($cert);echo 'subject: ', $info['name'], "\n"; // OpenSSL's one-line formecho 'common name: ', $info['subject']['CN'], "\n";echo 'issuer organization: ', $info['issuer']['O'], "\n";echo 'serial: ', $info['serialNumberHex'], "\n";echo 'valid: ', gmdate('c', $info['validFrom_time_t']), ' -> ', gmdate('c', $info['validTo_time_t']), "\n";echo 'signed with: ', $info['signatureTypeSN'], "\n"; // the issuer's algorithmecho 'SANs: ', $info['extensions']['subjectAltName'] ?? '(none)', "\n";echo 'key usage: ', $info['extensions']['keyUsage'] ?? '(none)', "\n"; $key = openssl_pkey_get_details(openssl_pkey_get_public($cert));if ($key['type'] === OPENSSL_KEYTYPE_EC) { echo 'key: EC ', $key['bits'], '-bit on ', $key['ec']['curve_name'], "\n";}echo 'SHA-256 fingerprint: ', openssl_x509_fingerprint($cert, 'sha256'), "\n"; // default would be SHA-1echo 'days until expiry: ', intdiv($info['validTo_time_t'] - time(), 86400), "\n"; <?php// openssl_x509_read wants PEM: wrap binary DER (e.g. from a .cer file) firstfunction derToPem(string $der): string{ return "-----BEGIN CERTIFICATE-----\n" . chunk_split(base64_encode($der), 64, "\n") . "-----END CERTIFICATE-----\n";} $cert = openssl_x509_read(derToPem(file_get_contents('cert.der')));echo 'DER input: ', openssl_x509_parse($cert)['subject']['CN'], "\n";echo 'signed by its own key: ', openssl_x509_verify($cert, openssl_pkey_get_public($cert)) === 1 ? 'yes' : 'no', "\n"; How it works
openssl_x509_readreturns anOpenSSLCertificatefor PEM text (or afile://path) andfalseotherwise; the otheropenssl_x509_*functions accept that object.openssl_x509_parsereturns the subject and issuer both as arrays (['subject']['CN']) and as OpenSSL's one-linename, the serial asserialNumberHex, and the validity asvalidFrom_time_t/validTo_time_tUnix timestamps.- Extensions come back as the strings
openssl x509 -textprints:subjectAltNameisDNS:a, DNS:b,keyUsageisDigital Signature. openssl_pkey_get_public($cert)extracts the key; its details give the type, size andcurve_name.openssl_x509_fingerprint($cert, 'sha256')hashes the whole DER certificate.openssl_x509_verify($cert, $key)returns1when that key signed the certificate — for a self-signed certificate, its own key.
Gotchas
openssl_x509_readandopenssl_x509_parsedo not accept binary DER: the run's raw.cerbytes fail withPEM routines::no start line. Wrap DER in PEM armor first, asderToPemdoes.openssl_x509_fingerprint($cert)without the algorithm returns SHA-1. Browsers and current tools show SHA-256, so the default output matches nothing they display.- Parsed extension values are display text, not data: split
subjectAltNameon,and strip theDNS:prefix to get host names, and do not rely on the exact wording of other extensions across OpenSSL versions. - Decoding is not validation. For trust decisions use
openssl_x509_checkpurposewith a CA file, or let the TLS layer (cURL, stream contexts withverify_peer) validate the chain.