Skip to content

Commit 1691440

Browse files
Support RSA_SHA256_MGF1 (#269)
* add support for RSA PSS (http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1) * use phpseclib for `http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p` as well * return 1 if successful and 0 otherwise * use phpseclib for encrypting http://www.w3.org/2009/xmlenc11#rsa-oaep --------- Co-authored-by: Julius Türich <info@joonlabs.com> Co-authored-by: joonlabs <52662462+joonlabs@users.noreply.github.com>
1 parent d28256c commit 1691440

1 file changed

Lines changed: 60 additions & 22 deletions

File tree

src/XMLSecurityKey.php

Lines changed: 60 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
use DOMElement;
55
use Exception;
6+
use phpseclib3\Crypt\PublicKeyLoader;
7+
use phpseclib3\Crypt\RSA;
68

79
/**
810
* xmlseclibs.php
@@ -62,6 +64,7 @@ class XMLSecurityKey
6264
const RSA_SHA384 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384';
6365
const RSA_SHA512 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512';
6466
const HMAC_SHA1 = 'http://www.w3.org/2000/09/xmldsig#hmac-sha1';
67+
const RSA_SHA256_MGF1 = 'http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1';
6568
const AUTHTAG_LENGTH = 16;
6669

6770
/** @var array */
@@ -183,10 +186,10 @@ public function __construct($type, $params=null)
183186
}
184187
throw new Exception('Certificate "type" (private/public) must be passed via parameters');
185188
case (self::RSA_OAEP_MGF1P):
186-
$this->cryptParams['library'] = 'openssl';
187-
$this->cryptParams['padding'] = OPENSSL_PKCS1_OAEP_PADDING;
189+
$this->cryptParams['library'] = 'phpseclib';
190+
$this->cryptParams['padding'] = RSA::ENCRYPTION_OAEP;
188191
$this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p';
189-
$this->cryptParams['hash'] = null;
192+
$this->cryptParams['digest'] = 'sha1';
190193
if (is_array($params) && ! empty($params['type'])) {
191194
if ($params['type'] == 'public' || $params['type'] == 'private') {
192195
$this->cryptParams['type'] = $params['type'];
@@ -195,10 +198,10 @@ public function __construct($type, $params=null)
195198
}
196199
throw new Exception('Certificate "type" (private/public) must be passed via parameters');
197200
case (self::RSA_OAEP):
198-
$this->cryptParams['library'] = 'openssl';
199-
$this->cryptParams['padding'] = OPENSSL_PKCS1_OAEP_PADDING;
201+
$this->cryptParams['library'] = 'phpseclib';
202+
$this->cryptParams['padding'] = RSA::ENCRYPTION_OAEP;
200203
$this->cryptParams['method'] = 'http://www.w3.org/2009/xmlenc11#rsa-oaep';
201-
$this->cryptParams['hash'] = 'http://www.w3.org/2009/xmlenc11#mgf1sha1';
204+
$this->cryptParams['digest'] = 'sha256';
202205
if (is_array($params) && ! empty($params['type'])) {
203206
if ($params['type'] == 'public' || $params['type'] == 'private') {
204207
$this->cryptParams['type'] = $params['type'];
@@ -257,6 +260,18 @@ public function __construct($type, $params=null)
257260
$this->cryptParams['library'] = $type;
258261
$this->cryptParams['method'] = 'http://www.w3.org/2000/09/xmldsig#hmac-sha1';
259262
break;
263+
case (self::RSA_SHA256_MGF1):
264+
$this->cryptParams['library'] = 'phpseclib';
265+
$this->cryptParams['method'] = 'http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1';
266+
$this->cryptParams['padding'] = RSA::SIGNATURE_PSS;
267+
$this->cryptParams['digest'] = 'SHA256';
268+
if (is_array($params) && ! empty($params['type'])) {
269+
if ($params['type'] == 'public' || $params['type'] == 'private') {
270+
$this->cryptParams['type'] = $params['type'];
271+
break;
272+
}
273+
}
274+
throw new Exception('Certificate "type" (private/public) must be passed via parameters');
260275
default:
261276
throw new Exception('Invalid Key Type');
262277
}
@@ -291,9 +306,9 @@ public function generateSessionKey()
291306
throw new Exception('Unknown key size for type "' . $this->type . '".');
292307
}
293308
$keysize = $this->cryptParams['keysize'];
294-
309+
295310
$key = openssl_random_pseudo_bytes($keysize);
296-
311+
297312
if ($this->type === self::TRIPLEDES_CBC) {
298313
/* Make sure that the generated key has the proper parity bits set.
299314
* Mcrypt doesn't care about the parity bits, but others may care.
@@ -308,7 +323,7 @@ public function generateSessionKey()
308323
$key[$i] = chr($byte);
309324
}
310325
}
311-
326+
312327
$this->key = $key;
313328
return $key;
314329
}
@@ -372,17 +387,17 @@ public function loadKey($key, $isFile=false, $isCert = false)
372387
if ($this->cryptParams['library'] == 'openssl') {
373388
switch ($this->cryptParams['type']) {
374389
case 'public':
375-
if ($isCert) {
376-
/* Load the thumbprint if this is an X509 certificate. */
377-
$this->X509Thumbprint = self::getRawThumbprint($this->key);
378-
}
379-
$this->key = openssl_get_publickey($this->key);
380-
if (! $this->key) {
381-
throw new Exception('Unable to extract public key');
382-
}
383-
break;
384-
385-
case 'private':
390+
if ($isCert) {
391+
/* Load the thumbprint if this is an X509 certificate. */
392+
$this->X509Thumbprint = self::getRawThumbprint($this->key);
393+
}
394+
$this->key = openssl_get_publickey($this->key);
395+
if (! $this->key) {
396+
throw new Exception('Unable to extract public key');
397+
}
398+
break;
399+
400+
case 'private':
386401
$this->key = openssl_get_privatekey($this->key, $this->passphrase);
387402
break;
388403

@@ -450,7 +465,7 @@ private function encryptSymmetric($data)
450465
$data = $this->padISO10126($data, $this->cryptParams['blocksize']);
451466
$encrypted = openssl_encrypt($data, $this->cryptParams['cipher'], $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->iv);
452467
}
453-
468+
454469
if (false === $encrypted) {
455470
throw new Exception('Failure encrypting Data (openssl symmetric) - ' . openssl_error_string());
456471
}
@@ -481,7 +496,7 @@ private function decryptSymmetric($data)
481496
} else {
482497
$decrypted = openssl_decrypt($data, $this->cryptParams['cipher'], $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->iv);
483498
}
484-
499+
485500
if (false === $decrypted) {
486501
throw new Exception('Failure decrypting Data (openssl symmetric) - ' . openssl_error_string());
487502
}
@@ -629,6 +644,14 @@ public function decryptData($data)
629644
case 'private':
630645
return $this->decryptPrivate($data);
631646
}
647+
} else if($this->cryptParams['library'] === 'phpseclib') {
648+
$private = PublicKeyLoader::load($this->key);
649+
$result = $private
650+
->withPadding($this->cryptParams['padding'])
651+
->withHash($this->cryptParams['digest'])
652+
->withMGFHash($this->cryptParams['digest'])
653+
->decrypt($data);
654+
return $result;
632655
}
633656
}
634657

@@ -643,6 +666,13 @@ public function signData($data)
643666
switch ($this->cryptParams['library']) {
644667
case 'openssl':
645668
return $this->signOpenSSL($data);
669+
case 'phpseclib':
670+
$private = PublicKeyLoader::load($this->key);
671+
return $private
672+
->withPadding($this->cryptParams['padding'])
673+
->withHash($this->cryptParams['digest'])
674+
->withMGFHash($this->cryptParams['digest'])
675+
->sign($data);
646676
case (self::HMAC_SHA1):
647677
return hash_hmac("sha1", $data, $this->key, true);
648678
}
@@ -669,6 +699,14 @@ public function verifySignature($data, $signature)
669699
switch ($this->cryptParams['library']) {
670700
case 'openssl':
671701
return $this->verifyOpenSSL($data, $signature);
702+
case 'phpseclib':
703+
$public = PublicKeyLoader::load($this->key);
704+
$result = $public
705+
->withPadding($this->cryptParams['padding'])
706+
->withHash($this->cryptParams['digest'])
707+
->withMGFHash($this->cryptParams['digest'])
708+
->verify($data, $signature);
709+
return $result === true ? 1 : 0;
672710
case (self::HMAC_SHA1):
673711
$expectedSignature = hash_hmac("sha1", $data, $this->key, true);
674712
return strcmp($signature, $expectedSignature) == 0;

0 commit comments

Comments
 (0)