Skip to content

Commit 3596031

Browse files
authored
Merge pull request #21 from JSignPdf/fix/works-with-unicode-password
fix: works with unicode password
2 parents c80de15 + 740cea1 commit 3596031

2 files changed

Lines changed: 72 additions & 16 deletions

File tree

src/Sign/JSignService.php

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public function sign(JSignParam $params)
2424
$this->validation($params);
2525

2626
$commandSign = $this->commandSign($params);
27-
\exec($commandSign, $output);
27+
exec($commandSign, $output);
2828

2929
$out = json_encode($output);
3030
$messageSuccess = "Finished: Signature succesfully created.";
@@ -51,6 +51,21 @@ public function sign(JSignParam $params)
5151
}
5252
}
5353

54+
/**
55+
* JSignPdf don't works as well at CLI interfaceif the password have
56+
* unicode chars. As workaround, I changed the password certificate in
57+
* memory.
58+
*/
59+
private function repackCertificateIfPasswordIsUnicode(JSignParam $params, $cert, $pkey)
60+
{
61+
if (!mb_detect_encoding($params->getPassword(), 'ASCII', true)) {
62+
$password = md5(microtime());
63+
$newCert = $this->exportToPkcs12($cert, $pkey, $password);
64+
$params->setPassword($password);
65+
$params->setCertificate($newCert);
66+
}
67+
}
68+
5469
public function getVersion(JSignParam $params)
5570
{
5671
$java = $this->javaCommand($params);
@@ -75,8 +90,8 @@ private function validation(JSignParam $params)
7590
$this->throwIf(empty($params->getPdf()), 'PDF is Empty or Invalid.');
7691
$this->throwIf(empty($params->getCertificate()), 'Certificate is Empty or Invalid.');
7792
$this->throwIf(empty($params->getPassword()), 'Certificate Password is Empty.');
78-
$this->throwIf(!$this->isPasswordCertificateValid($params->getCertificate(), $params->getPassword()), 'Certificate Password Invalid.');
79-
$this->throwIf($this->isExpiredCertificate($params->getCertificate(), $params->getPassword()), 'Certificate expired.');
93+
$this->throwIf(!$this->isPasswordCertificateValid($params), 'Certificate Password Invalid.');
94+
$this->throwIf($this->isExpiredCertificate($params), 'Certificate expired.');
8095
if ($params->isUseJavaInstalled()) {
8196
$javaVersion = exec("java -version 2>&1");
8297
$hasJavaVersion = strpos($javaVersion, 'not found') === false;
@@ -135,9 +150,9 @@ private function throwIf($condition, $message)
135150
throw new Exception($message);
136151
}
137152

138-
private function isPasswordCertificateValid($certificate, $password)
153+
private function isPasswordCertificateValid(JSignParam $params)
139154
{
140-
return $this->pkcs12Read($certificate, $password);
155+
return $this->pkcs12Read($params);
141156
}
142157

143158
/**
@@ -153,9 +168,12 @@ private function isPasswordCertificateValid($certificate, $password)
153168
* https://github.com/php/php-src/issues/12128
154169
* https://www.php.net/manual/en/function.openssl-pkcs12-read.php#128992
155170
*/
156-
private function pkcs12Read($certificate, $password)
171+
private function pkcs12Read(JSignParam $params)
157172
{
173+
$certificate = $params->getCertificate();
174+
$password = $params->getPassword();
158175
if (openssl_pkcs12_read($certificate, $certInfo, $password)) {
176+
$this->repackCertificateIfPasswordIsUnicode($params, $certInfo['cert'], $certInfo['pkey']);
159177
return $certInfo;
160178
}
161179
$msg = openssl_error_string();
@@ -175,19 +193,33 @@ private function pkcs12Read($certificate, $password)
175193
REPACK_COMMAND
176194
);
177195
$certificateRepacked = file_get_contents($tempEncriptedRepacked);
196+
$params->setCertificate($certificateRepacked);
178197
unlink($tempPassword);
179198
unlink($tempEncriptedOriginal);
180199
unlink($tempEncriptedRepacked);
181200
unlink($tempDecrypted);
182201
openssl_pkcs12_read($certificateRepacked, $certInfo, $password);
202+
$this->repackCertificateIfPasswordIsUnicode($params, $certInfo['cert'], $certInfo['pkey']);
183203
return $certInfo;
184204
}
185205
return [];
186206
}
187207

188-
private function isExpiredCertificate($certificate, $password)
208+
private function exportToPkcs12(\OpenSSLCertificate|string $certificate, \OpenSSLAsymmetricKey|\OpenSSLCertificate|string $privateKey, string $password)
209+
{
210+
$certContent = null;
211+
openssl_pkcs12_export(
212+
$certificate,
213+
$certContent,
214+
$privateKey,
215+
$password,
216+
);
217+
return $certContent;
218+
}
219+
220+
private function isExpiredCertificate(JSignParam $params)
189221
{
190-
$certInfo = $this->pkcs12Read($certificate, $password);
222+
$certInfo = $this->pkcs12Read($params);
191223
$certificate = openssl_x509_parse($certInfo['cert']);
192224
$dateCert = date_create()->setTimestamp($certificate['validTo_time_t']);
193225
return $dateCert <= date_create();

tests/JSignPDFTest.php

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
<?php
22

3+
namespace Jeidison\JSignPDF\Sign;
4+
5+
function exec(string $command, array &$output = null, int &$return_var = null) {
6+
global $mockExec;
7+
if ($mockExec) {
8+
$output = $mockExec;
9+
return $output;
10+
}
11+
return \exec($command, $output, $return_var);
12+
}
13+
314
namespace Jeidison\JSignPDF\Tests;
415

516
use Exception;
@@ -16,6 +27,8 @@ class JSignPDFTest extends TestCase
1627

1728
protected function setUp(): void
1829
{
30+
global $mockExec;
31+
$mockExec = null;
1932
$this->service = new JSignService();
2033
}
2134

@@ -41,14 +54,9 @@ private function getNewCert($password)
4154
$temporaryFile = tempnam(sys_get_temp_dir(), 'cfg');
4255
$csr = openssl_csr_new($csrNames, $privateKey);
4356
$x509 = openssl_csr_sign($csr, $rootCertificate, $rootPrivateKey, 365);
44-
return $this->exportToPkcs12($x509, $privateKey, $password);
45-
}
46-
47-
private function exportToPkcs12(\OpenSSLCertificate $certificate, \OpenSSLAsymmetricKey $privateKey, string $password)
48-
{
4957
$certContent = null;
5058
openssl_pkcs12_export(
51-
$certificate,
59+
$x509,
5260
$certContent,
5361
$privateKey,
5462
$password,
@@ -67,19 +75,35 @@ public function testSignSuccess()
6775
$this->assertNotNull($fileSigned);
6876
}
6977

70-
public function testSignUsingPasswordWithQuote()
78+
/**
79+
* @dataProvider providerSignUsingDifferentPasswords
80+
*/
81+
public function testSignUsingDifferentPasswords(string $password)
7182
{
83+
global $mockExec;
7284
if (!class_exists('JSignPDF\JSignPDFBin\JavaCommandService')) {
7385
$this->markTestSkipped('Install jsignpdf/jsignpdf-bin');
7486
}
7587
$params = JSignParamBuilder::instance()->withDefault();
76-
$password = "with ' quote";
7788
$params->setCertificate($this->getNewCert($password));
7889
$params->setPassword($password);
90+
$mockExec = 'Finished: Signature succesfully created.';
91+
$path = $params->getTempPdfSignedPath();
92+
file_put_contents($path, 'dummy');
7993
$fileSigned = $this->service->sign($params);
8094
$this->assertNotNull($fileSigned);
8195
}
8296

97+
public function providerSignUsingDifferentPasswords()
98+
{
99+
return [
100+
["with ' quote"],
101+
['with ( parentheis )'],
102+
['with $ dollar'],
103+
['with 😃 unicode'],
104+
];
105+
}
106+
83107
public function testCertificateExpired()
84108
{
85109
if (!class_exists('JSignPDF\JSignPDFBin\JavaCommandService')) {

0 commit comments

Comments
 (0)