Skip to content

Commit 77e5ed9

Browse files
authored
Migrate all crypto from OpenSSL to phpseclib. (#276)
Make the OpenSSL extension optional so signing, encryption, and X.509 handling work through phpseclib alone.
1 parent 2f8c755 commit 77e5ed9

5 files changed

Lines changed: 237 additions & 271 deletions

File tree

.github/workflows/ci.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ jobs:
1616
- uses: shivammathur/setup-php@v2
1717
with:
1818
php-version: ${{ matrix.php-versions }}
19-
extensions: openssl
2019
tools: phpunit/phpunit:9.6
2120

2221
- uses: actions/checkout@v3
@@ -46,7 +45,6 @@ jobs:
4645
- uses: shivammathur/setup-php@v2
4746
with:
4847
php-version: ${{ matrix.php-versions }}
49-
extensions: openssl
5048
tools: phpunit/phpunit:9.6
5149

5250
- uses: actions/checkout@v3
@@ -76,7 +74,6 @@ jobs:
7674
- uses: shivammathur/setup-php@v2
7775
with:
7876
php-version: ${{ matrix.php-versions }}
79-
extensions: openssl
8077
tools: phpunit/phpunit:9.6
8178

8279
- uses: actions/checkout@v3

CHANGELOG.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Features:
88
- add support for RSA PSS (Julius Türich and joonlabs)
99
- use phpseclib for encrypting rsa-oaep and rsa-oaep-mgf1p
1010
(Julius Türich and joonlabs)
11+
- use phpseclib for all crypto (symmetric, RSA-1.5, RSA signatures,
12+
X.509); OpenSSL extension is now optional
1113

1214
12, Dec 2026, 3.1.5
1315
Security:

composer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
},
1717
"require": {
1818
"php": ">= 8.0",
19-
"ext-openssl": "*",
2019
"phpseclib/phpseclib": "~3.0"
20+
},
21+
"suggest": {
22+
"ext-openssl": "Improves phpseclib performance when available"
2123
}
2224
}

src/XMLSecurityDSig.php

Lines changed: 63 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use DOMNode;
77
use DOMXPath;
88
use Exception;
9+
use phpseclib3\File\X509;
910
use RobRichards\XMLSecLibs\Utils\XPath as XPath;
1011

1112
/**
@@ -1039,44 +1040,28 @@ public static function staticAdd509Cert($parentRef, $cert, $isPEMFormat=true, $i
10391040
// Attach all certificate nodes and any additional data
10401041
foreach ($certs as $X509Cert) {
10411042
if ($issuerSerial || $subjectName) {
1042-
if ($certData = openssl_x509_parse("-----BEGIN CERTIFICATE-----\n".chunk_split($X509Cert, 64, "\n")."-----END CERTIFICATE-----\n")) {
1043-
if ($subjectName && ! empty($certData['subject'])) {
1044-
if (is_array($certData['subject'])) {
1045-
$parts = array();
1046-
foreach ($certData['subject'] AS $key => $value) {
1047-
if (is_array($value)) {
1048-
foreach ($value as $valueElement) {
1049-
array_unshift($parts, "$key=$valueElement");
1050-
}
1051-
} else {
1052-
array_unshift($parts, "$key=$value");
1053-
}
1054-
}
1055-
$subjectNameValue = implode(',', $parts);
1056-
} else {
1057-
$subjectNameValue = $certData['subject'];
1043+
$pem = "-----BEGIN CERTIFICATE-----\n".chunk_split($X509Cert, 64, "\n")."-----END CERTIFICATE-----\n";
1044+
$x509 = new X509();
1045+
if ($certData = $x509->loadX509($pem)) {
1046+
if ($subjectName) {
1047+
$subjectNameValue = self::getX509NameString($x509, false);
1048+
if ($subjectNameValue !== null) {
1049+
$x509SubjectNode = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'X509SubjectName', $subjectNameValue);
1050+
$x509DataNode->appendChild($x509SubjectNode);
10581051
}
1059-
$x509SubjectNode = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'X509SubjectName', $subjectNameValue);
1060-
$x509DataNode->appendChild($x509SubjectNode);
10611052
}
1062-
if ($issuerSerial && ! empty($certData['issuer']) && ! empty($certData['serialNumber'])) {
1063-
if (is_array($certData['issuer'])) {
1064-
$parts = array();
1065-
foreach ($certData['issuer'] AS $key => $value) {
1066-
array_unshift($parts, "$key=$value");
1067-
}
1068-
$issuerName = implode(',', $parts);
1069-
} else {
1070-
$issuerName = $certData['issuer'];
1053+
if ($issuerSerial) {
1054+
$issuerName = self::getX509NameString($x509, true);
1055+
$serialNumber = self::getX509SerialNumber($certData);
1056+
if ($issuerName !== null && $serialNumber !== null) {
1057+
$x509IssuerNode = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'X509IssuerSerial');
1058+
$x509DataNode->appendChild($x509IssuerNode);
1059+
1060+
$x509Node = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'X509IssuerName', $issuerName);
1061+
$x509IssuerNode->appendChild($x509Node);
1062+
$x509Node = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'X509SerialNumber', $serialNumber);
1063+
$x509IssuerNode->appendChild($x509Node);
10711064
}
1072-
1073-
$x509IssuerNode = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'X509IssuerSerial');
1074-
$x509DataNode->appendChild($x509IssuerNode);
1075-
1076-
$x509Node = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'X509IssuerName', $issuerName);
1077-
$x509IssuerNode->appendChild($x509Node);
1078-
$x509Node = $baseDoc->createElementNS(self::XMLDSIGNS, $dsig_pfx.'X509SerialNumber', $certData['serialNumber']);
1079-
$x509IssuerNode->appendChild($x509Node);
10801065
}
10811066
}
10821067

@@ -1086,6 +1071,49 @@ public static function staticAdd509Cert($parentRef, $cert, $isPEMFormat=true, $i
10861071
}
10871072
}
10881073

1074+
/**
1075+
* Format an X.509 DN in openssl_x509_parse-compatible reverse RDN order.
1076+
*
1077+
* @param X509 $x509
1078+
* @param bool $issuer
1079+
* @return string|null
1080+
*/
1081+
private static function getX509NameString(X509 $x509, $issuer)
1082+
{
1083+
$dnArray = $issuer ? $x509->getIssuerDN(X509::DN_OPENSSL) : $x509->getDN(X509::DN_OPENSSL);
1084+
if (! is_array($dnArray) || empty($dnArray)) {
1085+
return null;
1086+
}
1087+
1088+
$parts = array();
1089+
foreach ($dnArray as $key => $value) {
1090+
if (is_array($value)) {
1091+
foreach ($value as $valueElement) {
1092+
array_unshift($parts, "$key=$valueElement");
1093+
}
1094+
} else {
1095+
array_unshift($parts, "$key=$value");
1096+
}
1097+
}
1098+
return implode(',', $parts);
1099+
}
1100+
1101+
/**
1102+
* @param array $certData
1103+
* @return string|null
1104+
*/
1105+
private static function getX509SerialNumber(array $certData)
1106+
{
1107+
if (! isset($certData['tbsCertificate']['serialNumber'])) {
1108+
return null;
1109+
}
1110+
$serial = $certData['tbsCertificate']['serialNumber'];
1111+
if (is_object($serial) && method_exists($serial, 'toString')) {
1112+
return $serial->toString();
1113+
}
1114+
return (string) $serial;
1115+
}
1116+
10891117
/**
10901118
* @param string $cert
10911119
* @param bool $isPEMFormat

0 commit comments

Comments
 (0)