Skip to content

Commit e29c9db

Browse files
committed
Add a function to parse a CSR and return the information as an array, similar to openssl_x509_parse().
1 parent d86182f commit e29c9db

5 files changed

Lines changed: 345 additions & 0 deletions

File tree

ext/openssl/openssl.c

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1998,6 +1998,185 @@ PHP_FUNCTION(openssl_csr_get_public_key)
19981998
}
19991999
/* }}} */
20002000

2001+
/* {{{ Returns an array of the fields/values of the Certificate Request */
2002+
PHP_FUNCTION(openssl_csr_parse)
2003+
{
2004+
X509_REQ * csr = NULL;
2005+
zend_object *csr_obj;
2006+
zend_string *csr_str;
2007+
int i, sig_nid;
2008+
bool useshortnames = 1;
2009+
zval subitem;
2010+
X509_EXTENSION *extension;
2011+
X509_NAME *subject_name;
2012+
char *csr_name;
2013+
char *extname;
2014+
BIO *bio_out;
2015+
BUF_MEM *bio_buf;
2016+
char buf[256];
2017+
STACK_OF(X509_EXTENSION) *exts;
2018+
char *crit_name = NULL;
2019+
int crit_len = 0;
2020+
2021+
ZEND_PARSE_PARAMETERS_START(1, 2)
2022+
Z_PARAM_OBJ_OF_CLASS_OR_STR(csr_obj, php_openssl_request_ce, csr_str)
2023+
Z_PARAM_OPTIONAL
2024+
Z_PARAM_BOOL(useshortnames)
2025+
ZEND_PARSE_PARAMETERS_END();
2026+
2027+
csr = php_openssl_csr_from_param(csr_obj, csr_str, 1);
2028+
if (csr == NULL) {
2029+
// TODO Add Warning?
2030+
RETURN_FALSE;
2031+
}
2032+
array_init(return_value);
2033+
2034+
subject_name = X509_REQ_get_subject_name(csr);
2035+
csr_name = X509_NAME_oneline(subject_name, NULL, 0);
2036+
if (csr_name) {
2037+
add_assoc_string(return_value, "name", csr_name);
2038+
OPENSSL_free(csr_name);
2039+
}
2040+
2041+
php_openssl_add_assoc_name_entry(return_value, "subject", subject_name, useshortnames);
2042+
/* hash as used in CA directories to lookup csr by subject name */
2043+
{
2044+
char buf[32];
2045+
snprintf(buf, sizeof(buf), "%08lx", X509_NAME_hash_ex(subject_name, NULL, NULL, NULL));
2046+
add_assoc_string(return_value, "hash", buf);
2047+
}
2048+
2049+
add_assoc_long(return_value, "version", X509_REQ_get_version(csr));
2050+
2051+
sig_nid = X509_REQ_get_signature_nid(csr);
2052+
add_assoc_string(return_value, "signatureTypeSN", (char*)OBJ_nid2sn(sig_nid));
2053+
add_assoc_string(return_value, "signatureTypeLN", (char*)OBJ_nid2ln(sig_nid));
2054+
add_assoc_long(return_value, "signatureTypeNID", sig_nid);
2055+
2056+
array_init(&subitem);
2057+
int attrcnt = X509_REQ_get_attr_count(csr);
2058+
if (attrcnt > 0) {
2059+
for (i = 0; i < attrcnt; i++) {
2060+
X509_ATTRIBUTE *attr = X509_REQ_get_attr(csr,i);
2061+
char unknown[] = "Unknown";
2062+
if (attr) {
2063+
char objbuf[80];
2064+
/* Adapted from openssl's "req" app */
2065+
ASN1_TYPE *at;
2066+
ASN1_BIT_STRING *bs = NULL;
2067+
ASN1_OBJECT *aobj;
2068+
int j, type = 0, count = 1, ii = 0;
2069+
2070+
aobj = X509_ATTRIBUTE_get0_object(attr);
2071+
if (X509_REQ_extension_nid(OBJ_obj2nid(aobj)))
2072+
continue;
2073+
if ((j = i2t_ASN1_OBJECT(objbuf, sizeof(objbuf), aobj)) > 0) {
2074+
ii = 0;
2075+
count = X509_ATTRIBUTE_count(attr);
2076+
if (count == 0) {
2077+
RETURN_FALSE;
2078+
}
2079+
get_next:
2080+
at = X509_ATTRIBUTE_get0_type(attr, ii);
2081+
type = at->type;
2082+
bs = at->value.asn1_string;
2083+
} else {
2084+
strcpy(objbuf, unknown);
2085+
}
2086+
switch (type) {
2087+
case V_ASN1_PRINTABLESTRING:
2088+
case V_ASN1_T61STRING:
2089+
case V_ASN1_NUMERICSTRING:
2090+
case V_ASN1_UTF8STRING:
2091+
case V_ASN1_IA5STRING:
2092+
add_assoc_stringl(&subitem, objbuf, bs->data, bs->length);
2093+
break;
2094+
default:
2095+
add_assoc_stringl(&subitem, objbuf, unknown, sizeof(unknown));
2096+
break;
2097+
}
2098+
if (++ii < count)
2099+
goto get_next;
2100+
2101+
}
2102+
}
2103+
add_assoc_zval(return_value, "attributes", &subitem);
2104+
}
2105+
2106+
array_init(&subitem);
2107+
exts = X509_REQ_get_extensions(csr);
2108+
if (exts) {
2109+
int count = sk_X509_EXTENSION_num(exts);
2110+
for (i = 0; i < count; i++) {
2111+
int nid;
2112+
extension = sk_X509_EXTENSION_value(exts, i);
2113+
nid = OBJ_obj2nid(X509_EXTENSION_get_object(extension));
2114+
if (nid != NID_undef) {
2115+
extname = (char *)OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(extension)));
2116+
} else {
2117+
OBJ_obj2txt(buf, sizeof(buf)-1, X509_EXTENSION_get_object(extension), 1);
2118+
extname = buf;
2119+
}
2120+
if (X509_EXTENSION_get_critical(extension)) {
2121+
int new_len = strlen(extname) + 10;
2122+
if (new_len > crit_len) {
2123+
if (crit_name) {
2124+
efree(crit_name);
2125+
}
2126+
crit_len = new_len;
2127+
crit_name = emalloc(crit_len);
2128+
}
2129+
if (crit_name) {
2130+
strcpy(crit_name, extname);
2131+
strcat(crit_name, ":critical");
2132+
add_assoc_bool(&subitem, crit_name, 1);
2133+
}
2134+
}
2135+
bio_out = BIO_new(BIO_s_mem());
2136+
if (bio_out == NULL) {
2137+
php_openssl_store_errors();
2138+
goto err_subitem;
2139+
}
2140+
if (nid == NID_subject_alt_name) {
2141+
if (openssl_x509v3_subjectAltName(bio_out, extension) == 0) {
2142+
BIO_get_mem_ptr(bio_out, &bio_buf);
2143+
add_assoc_stringl(&subitem, extname, bio_buf->data, bio_buf->length);
2144+
} else {
2145+
BIO_free(bio_out);
2146+
goto err_subitem;
2147+
}
2148+
}
2149+
else if (X509V3_EXT_print(bio_out, extension, 0, 0)) {
2150+
BIO_get_mem_ptr(bio_out, &bio_buf);
2151+
add_assoc_stringl(&subitem, extname, bio_buf->data, bio_buf->length);
2152+
} else {
2153+
php_openssl_add_assoc_asn1_string(&subitem, extname, X509_EXTENSION_get_data(extension));
2154+
}
2155+
BIO_free(bio_out);
2156+
}
2157+
add_assoc_zval(return_value, "extensions", &subitem);
2158+
if (crit_name) {
2159+
efree(crit_name);
2160+
}
2161+
}
2162+
if (csr) {
2163+
X509_REQ_free(csr);
2164+
}
2165+
return;
2166+
2167+
err_subitem:
2168+
zval_ptr_dtor(&subitem);
2169+
if (crit_name) {
2170+
efree(crit_name);
2171+
}
2172+
zend_array_destroy(Z_ARR_P(return_value));
2173+
if (csr) {
2174+
X509_REQ_free(csr);
2175+
}
2176+
RETURN_FALSE;
2177+
}
2178+
/* }}} */
2179+
20012180
/* }}} */
20022181

20032182
/* {{{ EVP Public/Private key functions */

ext/openssl/openssl.stub.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,8 @@ function openssl_csr_get_subject(OpenSSLCertificateSigningRequest|string $csr, b
501501

502502
function openssl_csr_get_public_key(OpenSSLCertificateSigningRequest|string $csr, bool $short_names = true): OpenSSLAsymmetricKey|false {}
503503

504+
function openssl_csr_parse(OpenSSLCertificateSigningRequest|string $csr, bool $short_names = true): array|false {}
505+
504506
function openssl_pkey_new(?array $options = null): OpenSSLAsymmetricKey|false {}
505507

506508
/**

ext/openssl/openssl_arginfo.h

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
--TEST--
2+
openssl_csr_parse() tests
3+
--EXTENSIONS--
4+
openssl
5+
--SKIPIF--
6+
<?php
7+
if (OPENSSL_VERSION_NUMBER >= 0x30200000) die('skip For OpenSSL < 3.2');
8+
?>
9+
--FILE--
10+
<?php
11+
$csr = "file://" . __DIR__ . "/parse.csr";
12+
13+
$parsedCSR = openssl_csr_parse($csr);
14+
var_dump($parsedCSR === openssl_csr_parse(file_get_contents($csr)));
15+
var_dump($parsedCSR);
16+
var_dump(openssl_csr_parse($csr, false));
17+
?>
18+
--EXPECTF--
19+
bool(true)
20+
array(8) {
21+
["name"]=>
22+
string(71) "/C=UK/ST=England/L=London/CN=test.php.net/emailAddress=test.php@php.net"
23+
["subject"]=>
24+
array(5) {
25+
["C"]=>
26+
string(2) "UK"
27+
["ST"]=>
28+
string(7) "England"
29+
["L"]=>
30+
string(6) "London"
31+
["CN"]=>
32+
string(12) "test.php.net"
33+
["emailAddress"]=>
34+
string(16) "test.php@php.net"
35+
}
36+
["hash"]=>
37+
string(8) "b21872c1"
38+
["version"]=>
39+
int(0)
40+
["signatureTypeSN"]=>
41+
string(10) "RSA-SHA256"
42+
["signatureTypeLN"]=>
43+
string(23) "sha256WithRSAEncryption"
44+
["signatureTypeNID"]=>
45+
int(668)
46+
["extensions"]=>
47+
array(1) {
48+
["basicConstraints"]=>
49+
string(8) "CA:FALSE"
50+
}
51+
}
52+
array(8) {
53+
["name"]=>
54+
string(71) "/C=UK/ST=England/L=London/CN=test.php.net/emailAddress=test.php@php.net"
55+
["subject"]=>
56+
array(5) {
57+
["countryName"]=>
58+
string(2) "UK"
59+
["stateOrProvinceName"]=>
60+
string(7) "England"
61+
["localityName"]=>
62+
string(6) "London"
63+
["commonName"]=>
64+
string(12) "test.php.net"
65+
["emailAddress"]=>
66+
string(16) "test.php@php.net"
67+
}
68+
["hash"]=>
69+
string(8) "b21872c1"
70+
["version"]=>
71+
int(0)
72+
["signatureTypeSN"]=>
73+
string(10) "RSA-SHA256"
74+
["signatureTypeLN"]=>
75+
string(23) "sha256WithRSAEncryption"
76+
["signatureTypeNID"]=>
77+
int(668)
78+
["extensions"]=>
79+
array(1) {
80+
["basicConstraints"]=>
81+
string(8) "CA:FALSE"
82+
}
83+
}

ext/openssl/tests/parse.csr

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
Certificate Request:
2+
Data:
3+
Version: 1 (0x0)
4+
Subject: C = UK, ST = England, L = London, CN = test.php.net, emailAddress = test.php@php.net
5+
Subject Public Key Info:
6+
Public Key Algorithm: rsaEncryption
7+
Public-Key: (2048 bit)
8+
Modulus:
9+
00:ad:b5:26:55:6d:58:fa:b2:73:65:10:b7:0d:80:
10+
74:91:d2:20:be:4e:cc:01:d6:b2:ef:cc:9b:18:f3:
11+
c3:85:59:5b:01:be:02:8d:66:da:3e:ce:27:c9:40:
12+
93:12:df:4a:54:6d:07:ee:95:c7:c4:36:d6:4a:c0:
13+
b6:f7:be:33:76:fa:a5:3f:42:f6:a2:9d:7f:33:64:
14+
ad:79:37:06:08:13:9d:c0:61:f6:8c:e4:55:01:92:
15+
fc:fd:66:d3:39:ff:48:a4:a0:0c:ef:ba:0d:d7:8b:
16+
1c:b3:23:a5:40:12:6a:86:1f:b1:ac:b8:3c:92:c0:
17+
76:74:ea:2c:e7:8c:83:aa:8d:13:ab:b4:79:b6:57:
18+
55:f9:a9:4a:65:75:a5:26:7a:91:09:f1:6e:c6:fa:
19+
ad:7d:62:39:9c:64:c0:79:d5:59:86:f8:d0:7c:b4:
20+
10:82:e8:df:5c:7a:05:5a:81:9b:5e:7d:9b:bb:37:
21+
f0:28:62:44:7a:a5:8f:6d:03:99:17:f1:5e:38:93:
22+
e5:80:e0:61:84:36:f7:04:01:4d:54:2b:4c:de:4e:
23+
f5:45:b9:63:e7:8a:4d:77:7c:af:ab:5e:76:c6:c8:
24+
05:77:4a:37:b3:5e:5f:b9:2c:19:81:ea:d4:8d:e1:
25+
2e:c3:fc:13:2b:d9:3b:bf:2a:7e:32:b2:10:1d:09:
26+
8f:75
27+
Exponent: 65537 (0x10001)
28+
Attributes:
29+
streetAddress :
30+
facsimileTelephoneNumber :
31+
postalCode :N11
32+
telephoneNumber :012345678
33+
name :Organisation
34+
1.3.6.1.4.1.11278.1150.2.1:11112222
35+
1.3.6.1.4.1.11278.1150.2.2:12345678
36+
emailAddress :info@example.com
37+
Requested Extensions:
38+
X509v3 Basic Constraints:
39+
CA:FALSE
40+
Signature Algorithm: sha256WithRSAEncryption
41+
Signature Value:
42+
0a:0f:23:fb:16:63:44:0a:3c:c1:01:b9:1a:7a:30:77:2f:5a:
43+
04:84:c7:09:24:41:f4:49:41:99:58:75:ea:6e:e9:3c:34:89:
44+
9c:18:45:33:91:e2:c1:27:57:3f:79:aa:ca:d6:a8:7a:7a:42:
45+
45:f1:74:51:bd:14:f1:e2:e0:de:ba:39:7d:97:6f:94:ed:1e:
46+
00:c6:33:1a:c9:4f:06:c7:fb:b5:5d:b0:98:97:2e:45:9b:78:
47+
bb:a8:cc:ab:fc:06:ca:e1:2d:16:22:66:49:7e:55:62:2b:37:
48+
23:9c:2b:b6:a8:da:c0:fe:0f:76:24:08:10:38:24:ae:0e:16:
49+
17:e1:c5:8e:37:0f:6b:26:7b:b6:84:41:58:eb:4b:e5:2f:12:
50+
3b:88:00:b3:74:00:fe:a0:3b:60:0c:89:43:83:3c:1a:e3:b0:
51+
f2:37:36:93:78:d1:55:2f:55:4f:87:b2:9d:53:96:ab:ed:87:
52+
f0:18:01:2b:86:4f:6c:ad:33:96:1d:71:29:bb:27:06:86:03:
53+
02:20:3a:ff:17:3d:44:06:7a:6e:76:c8:c1:2f:cf:24:91:0b:
54+
86:72:4d:d7:5f:89:90:24:79:32:e6:6e:1f:92:31:56:c7:5d:
55+
73:a4:51:e1:33:f9:fb:e6:43:82:fe:b1:cd:d1:13:cb:be:33:
56+
c8:37:d4:b3
57+
-----BEGIN CERTIFICATE REQUEST-----
58+
MIIDcDCCAlgCAQAwaDELMAkGA1UEBhMCVUsxEDAOBgNVBAgMB0VuZ2xhbmQxDzAN
59+
BgNVBAcMBkxvbmRvbjEVMBMGA1UEAwwMdGVzdC5waHAubmV0MR8wHQYJKoZIhvcN
60+
AQkBFhB0ZXN0LnBocEBwaHAubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
61+
CgKCAQEArbUmVW1Y+rJzZRC3DYB0kdIgvk7MAday78ybGPPDhVlbAb4CjWbaPs4n
62+
yUCTEt9KVG0H7pXHxDbWSsC2974zdvqlP0L2op1/M2SteTcGCBOdwGH2jORVAZL8
63+
/WbTOf9IpKAM77oN14scsyOlQBJqhh+xrLg8ksB2dOos54yDqo0Tq7R5tldV+alK
64+
ZXWlJnqRCfFuxvqtfWI5nGTAedVZhvjQfLQQgujfXHoFWoGbXn2buzfwKGJEeqWP
65+
bQOZF/FeOJPlgOBhhDb3BAFNVCtM3k71Rblj54pNd3yvq152xsgFd0o3s15fuSwZ
66+
gerUjeEuw/wTK9k7vyp+MrIQHQmPdQIDAQABoIHCMAkGA1UECTECDAAwCQYDVQQX
67+
MQIMADAMBgNVBBExBQwDTjExMBIGA1UEFDELDAkwMTIzNDU2NzgwFQYDVQQpMQ4M
68+
DE9yZ2FuaXNhdGlvbjAZBgsrBgEEAdgOiH4CATEKDAgxMTExMjIyMjAZBgsrBgEE
69+
AdgOiH4CAjEKDAgxMjM0NTY3ODAaBgkqhkiG9w0BCQ4xDTALMAkGA1UdEwQCMAAw
70+
HwYJKoZIhvcNAQkBMRIWEGluZm9AZXhhbXBsZS5jb20wDQYJKoZIhvcNAQELBQAD
71+
ggEBAAoPI/sWY0QKPMEBuRp6MHcvWgSExwkkQfRJQZlYdepu6Tw0iZwYRTOR4sEn
72+
Vz95qsrWqHp6QkXxdFG9FPHi4N66OX2Xb5TtHgDGMxrJTwbH+7VdsJiXLkWbeLuo
73+
zKv8BsrhLRYiZkl+VWIrNyOcK7ao2sD+D3YkCBA4JK4OFhfhxY43D2sme7aEQVjr
74+
S+UvEjuIALN0AP6gO2AMiUODPBrjsPI3NpN40VUvVU+Hsp1Tlqvth/AYASuGT2yt
75+
M5YdcSm7JwaGAwIgOv8XPUQGem52yMEvzySRC4ZyTddfiZAkeTLmbh+SMVbHXXOk
76+
UeEz+fvmQ4L+sc3RE8u+M8g31LM=
77+
-----END CERTIFICATE REQUEST-----

0 commit comments

Comments
 (0)