Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#xmlseclibs
#xmlseclibs

xmlseclibs is a library written in PHP for working with XML Encryption and Signatures.

Expand Down
18 changes: 16 additions & 2 deletions src/XMLSecEnc.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class XMLSecEnc
const Content = 'http://www.w3.org/2001/04/xmlenc#Content';
const URI = 3;
const XMLENCNS = 'http://www.w3.org/2001/04/xmlenc#';
const XMLDSIGNS = 'http://www.w3.org/2000/09/xmldsig#';

/** @var null|DOMDocument */
private $encdoc = null;
Expand Down Expand Up @@ -390,9 +391,22 @@ public function locateKey($node=null)
$query = ".//xmlsecenc:EncryptionMethod";
$nodeset = $xpath->query($query, $node);
if ($encmeth = $nodeset->item(0)) {
$attrAlgorithm = $encmeth->getAttribute("Algorithm");
$attrAlgorithm = $encmeth->getAttribute("Algorithm");
$digestAlgorithm = null;

if ($attrAlgorithm === XMLSecurityKey::RSA_OAEP)
{
$xpath->registerNamespace('dsig', self::XMLDSIGNS);
$query = ".//ds:DigestMethod";
$nodeset = $xpath->query($query, $node);
if ($digmeth = $nodeset->item(0)) {
$digestAlgorithm = $digmeth->getAttribute("Algorithm");
$digestAlgorithm = explode('#', $digestAlgorithm)[1] ?? null;
}
}

try {
$objKey = new XMLSecurityKey($attrAlgorithm, array('type' => 'private'));
$objKey = new XMLSecurityKey($attrAlgorithm, ['type' => 'private', 'digest' => $digestAlgorithm]);
} catch (Exception $e) {
return null;
}
Expand Down
4 changes: 4 additions & 0 deletions src/XMLSecurityDSig.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class XMLSecurityDSig
{
const XMLDSIGNS = 'http://www.w3.org/2000/09/xmldsig#';
const SHA1 = 'http://www.w3.org/2000/09/xmldsig#sha1';
const SHA224 = 'https://www.w3.org/2001/04/xmldsig-more#sha224';
const SHA256 = 'http://www.w3.org/2001/04/xmlenc#sha256';
const SHA384 = 'http://www.w3.org/2001/04/xmldsig-more#sha384';
const SHA512 = 'http://www.w3.org/2001/04/xmlenc#sha512';
Expand Down Expand Up @@ -349,6 +350,9 @@ public function calculateDigest($digestAlgorithm, $data, $encode = true)
case self::SHA1:
$alg = 'sha1';
break;
case self::SHA224:
$alg = 'sha224';
break;
case self::SHA256:
$alg = 'sha256';
break;
Expand Down
209 changes: 198 additions & 11 deletions src/XMLSecurityKey.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

use DOMElement;
use Exception;
use phpseclib3\Crypt\RSA\PrivateKey;
use phpseclib3\Crypt\RSA\PublicKey;
use phpseclib3\Crypt\RSA;

/**
* xmlseclibs.php
Expand Down Expand Up @@ -61,6 +64,11 @@ class XMLSecurityKey
const RSA_SHA256 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256';
const RSA_SHA384 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384';
const RSA_SHA512 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512';
const SHA1_RSA_MGF1 = 'http://www.w3.org/2007/05/xmldsig-more#sha1-rsa-MGF1';
const SHA224_RSA_MGF1 = 'http://www.w3.org/2007/05/xmldsig-more#sha224-rsa-MGF1';
const SHA256_RSA_MGF1 = 'http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1';
const SHA384_RSA_MGF1 = 'http://www.w3.org/2007/05/xmldsig-more#sha384-rsa-MGF1';
const SHA512_RSA_MGF1 = 'http://www.w3.org/2007/05/xmldsig-more#sha512-rsa-MGF1';
const HMAC_SHA1 = 'http://www.w3.org/2000/09/xmldsig#hmac-sha1';
const AUTHTAG_LENGTH = 16;

Expand Down Expand Up @@ -176,7 +184,7 @@ public function __construct($type, $params=null)
$this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING;
$this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5';
if (is_array($params) && ! empty($params['type'])) {
if ($params['type'] == 'public' || $params['type'] == 'private') {
if ($params['type'] === 'public' || $params['type'] === 'private') {
$this->cryptParams['type'] = $params['type'];
break;
}
Expand All @@ -188,19 +196,30 @@ public function __construct($type, $params=null)
$this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p';
$this->cryptParams['hash'] = null;
if (is_array($params) && ! empty($params['type'])) {
if ($params['type'] == 'public' || $params['type'] == 'private') {
if ($params['type'] === 'public' || $params['type'] === 'private') {
$this->cryptParams['type'] = $params['type'];
break;
}
}
throw new Exception('Certificate "type" (private/public) must be passed via parameters');
// case (self::RSA_OAEP):
// $this->cryptParams['library'] = 'openssl';
// $this->cryptParams['padding'] = OPENSSL_PKCS1_OAEP_PADDING;
// $this->cryptParams['method'] = 'http://www.w3.org/2009/xmlenc11#rsa-oaep';
// $this->cryptParams['hash'] = 'http://www.w3.org/2009/xmlenc11#mgf1sha1';
// if (is_array($params) && ! empty($params['type'])) {
// if ($params['type'] === 'public' || $params['type'] === 'private') {
// $this->cryptParams['type'] = $params['type'];
// break;
// }
// }
// throw new Exception('Certificate "type" (private/public) must be passed via parameters');
case (self::RSA_OAEP):
$this->cryptParams['library'] = 'openssl';
$this->cryptParams['padding'] = OPENSSL_PKCS1_OAEP_PADDING;
$this->cryptParams['library'] = 'phpseclib';
$this->cryptParams['digest'] = $params['digest'] ?? 'sha1';
$this->cryptParams['method'] = 'http://www.w3.org/2009/xmlenc11#rsa-oaep';
$this->cryptParams['hash'] = 'http://www.w3.org/2009/xmlenc11#mgf1sha1';
if (is_array($params) && ! empty($params['type'])) {
if ($params['type'] == 'public' || $params['type'] == 'private') {
if ($params['type'] === 'public' || $params['type'] === 'private') {
$this->cryptParams['type'] = $params['type'];
break;
}
Expand All @@ -211,7 +230,7 @@ public function __construct($type, $params=null)
$this->cryptParams['method'] = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1';
$this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING;
if (is_array($params) && ! empty($params['type'])) {
if ($params['type'] == 'public' || $params['type'] == 'private') {
if ($params['type'] === 'public' || $params['type'] === 'private') {
$this->cryptParams['type'] = $params['type'];
break;
}
Expand All @@ -223,7 +242,7 @@ public function __construct($type, $params=null)
$this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING;
$this->cryptParams['digest'] = 'SHA256';
if (is_array($params) && ! empty($params['type'])) {
if ($params['type'] == 'public' || $params['type'] == 'private') {
if ($params['type'] === 'public' || $params['type'] === 'private') {
$this->cryptParams['type'] = $params['type'];
break;
}
Expand All @@ -235,7 +254,7 @@ public function __construct($type, $params=null)
$this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING;
$this->cryptParams['digest'] = 'SHA384';
if (is_array($params) && ! empty($params['type'])) {
if ($params['type'] == 'public' || $params['type'] == 'private') {
if ($params['type'] === 'public' || $params['type'] === 'private') {
$this->cryptParams['type'] = $params['type'];
break;
}
Expand All @@ -247,12 +266,67 @@ public function __construct($type, $params=null)
$this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING;
$this->cryptParams['digest'] = 'SHA512';
if (is_array($params) && ! empty($params['type'])) {
if ($params['type'] == 'public' || $params['type'] == 'private') {
if ($params['type'] === 'public' || $params['type'] === 'private') {
$this->cryptParams['type'] = $params['type'];
break;
}
}
throw new Exception('Certificate "type" (private/public) must be passed via parameters');
case (self::SHA1_RSA_MGF1):
$this->cryptParams['library'] = 'phpseclib';
$this->cryptParams['method'] = 'http://www.w3.org/2007/05/xmldsig-more#sha1-rsa-MGF1';
$this->cryptParams['digest'] = 'sha1';
if (is_array($params) && ! empty($params['type'])) {
if ($params['type'] === 'public' || $params['type'] === 'private') {
$this->cryptParams['type'] = $params['type'];
break;
}
}
throw new Exception('Certificate "type" (private/public) must be passed via parameters');
case (self::SHA224_RSA_MGF1):
$this->cryptParams['library'] = 'phpseclib';
$this->cryptParams['method'] = 'http://www.w3.org/2007/05/xmldsig-more#sha224-rsa-MGF1';
$this->cryptParams['digest'] = 'sha224';
if (is_array($params) && ! empty($params['type'])) {
if ($params['type'] === 'public' || $params['type'] === 'private') {
$this->cryptParams['type'] = $params['type'];
break;
}
}
throw new Exception('Certificate "type" (private/public) must be passed via parameters');
case (self::SHA256_RSA_MGF1):
$this->cryptParams['library'] = 'phpseclib';
$this->cryptParams['method'] = 'http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1';
$this->cryptParams['digest'] = 'sha256';
if (is_array($params) && ! empty($params['type'])) {
if ($params['type'] === 'public' || $params['type'] === 'private') {
$this->cryptParams['type'] = $params['type'];
break;
}
}
throw new Exception('Certificate "type" (private/public) must be passed via parameters');
case (self::SHA384_RSA_MGF1):
$this->cryptParams['library'] = 'phpseclib';
$this->cryptParams['method'] = 'http://www.w3.org/2007/05/xmldsig-more#sha384-rsa-MGF1';
$this->cryptParams['digest'] = 'sha384';
if (is_array($params) && ! empty($params['type'])) {
if ($params['type'] === 'public' || $params['type'] === 'private') {
$this->cryptParams['type'] = $params['type'];
break;
}
}
throw new Exception('Certificate "type" (private/public) must be passed via parameters');
case (self::SHA512_RSA_MGF1):
$this->cryptParams['library'] = 'phpseclib';
$this->cryptParams['method'] = 'http://www.w3.org/2007/05/xmldsig-more#sha512-rsa-MGF1';
$this->cryptParams['digest'] = 'sha512';
if (is_array($params) && ! empty($params['type'])) {
if ($params['type'] === 'public' || $params['type'] === 'private') {
$this->cryptParams['type'] = $params['type'];
break;
}
}
throw new Exception('Certificate "type" (private/public) must be passed via parameters');
case (self::HMAC_SHA1):
$this->cryptParams['library'] = $type;
$this->cryptParams['method'] = 'http://www.w3.org/2000/09/xmldsig#hmac-sha1';
Expand Down Expand Up @@ -369,7 +443,7 @@ public function loadKey($key, $isFile=false, $isCert = false)
} else {
$this->x509Certificate = null;
}
if ($this->cryptParams['library'] == 'openssl') {
if ($this->cryptParams['library'] === 'openssl') {
switch ($this->cryptParams['type']) {
case 'public':
if ($isCert) {
Expand All @@ -396,6 +470,20 @@ public function loadKey($key, $isFile=false, $isCert = false)
throw new Exception('Unknown type');
}
}
elseif ($this->cryptParams['library'] === 'phpseclib')
{
switch ($this->cryptParams['type'])
{
case 'public':
$this->key = RSA::load($this->key);
break;
case 'private':
$this->key = RSA::load($this->key, (!empty($this->passphrase) ? $this->passphrase : false));
break;
default:
throw new Exception('Unknown type');
}
}
}

/**
Expand Down Expand Up @@ -566,6 +654,37 @@ private function signOpenSSL($data)
return $signature;
}

/**
* Signs the given data (string) using phpSecLib
*
* @param string $data
* @return string
* @throws Exception
*/
private function signPhpSecLib($data)
{
if (empty($this->cryptParams['digest'])) {
throw new Exception('digest algo is not set');
}
if (empty($this->key)) {
throw new Exception('key not set');
}
if ($this->key instanceof PrivateKey)
{
$rsa = $this->key;
}
else
{
$rsa = RSA::load($this->key);
}
$rsa = $rsa->withHash($this->cryptParams['digest']);
$signature = $rsa->sign($data);
if (!$signature) {
throw new Exception('Failure Signing Data: - ' . $this->cryptParams['digest']);
}
return $signature;
}

/**
* Verifies the given data (string) belonging to the given signature using the openssl-extension
*
Expand All @@ -591,6 +710,42 @@ private function verifyOpenSSL($data, $signature)
return openssl_verify($data, $signature, $this->key, $algo);
}

/**
* Verifies the given data (string) belonging to the given signature using phpseclib
*
* Returns:
* 1 on succesful signature verification,
* 0 when signature verification failed,
*
* @param string $data
* @param string $signature
* @return int
*/
private function verifyPhpSecLib($data, $signature)
{
if (empty($this->cryptParams['digest'])) {
throw new Exception('digest algo is not set');
}
if (empty($this->key)) {
throw new Exception('key not set');
}
if ($this->key instanceof PublicKey)
{
$rsa = $this->key;
}
else
{
$rsa = RSA::load($this->key);
}
$rsa = $rsa->withHash($this->cryptParams['digest']);
$verified = $rsa->verify($data, $signature);
if ($verified) {
return 1;
} else {
return 0;
}
}

/**
* Encrypts the given data (string) using the regarding php-extension, depending on the library assigned to algorithm in the contructor.
*
Expand All @@ -609,6 +764,20 @@ public function encryptData($data)
return $this->encryptPrivate($data);
}
}
elseif ($this->cryptParams['library'] === 'phpseclib')
{
if ($this->key instanceof PublicKey)
{
$rsa = $this->key;
}
else
{
$rsa = RSA::load($this->key);
}

$rsa = $rsa->withHash($this->cryptParams['digest']);
return $rsa->encrypt($data);
}
}

/**
Expand All @@ -629,6 +798,20 @@ public function decryptData($data)
return $this->decryptPrivate($data);
}
}
elseif ($this->cryptParams['library'] === 'phpseclib')
{
if ($this->key instanceof PrivateKey)
{
$rsa = $this->key;
}
else
{
$rsa = RSA::load($this->key);
}

$rsa = $rsa->withHash($this->cryptParams['digest']);
return $rsa->decrypt($data);
}
}

/**
Expand All @@ -644,6 +827,8 @@ public function signData($data)
return $this->signOpenSSL($data);
case (self::HMAC_SHA1):
return hash_hmac("sha1", $data, $this->key, true);
case 'phpseclib':
return $this->signPhpSecLib($data);
}
}

Expand Down Expand Up @@ -671,6 +856,8 @@ public function verifySignature($data, $signature)
case (self::HMAC_SHA1):
$expectedSignature = hash_hmac("sha1", $data, $this->key, true);
return strcmp($signature, $expectedSignature) == 0;
case 'phpseclib':
return $this->verifyPhpSecLib($data, $signature);
}
}

Expand Down
Loading
Loading