ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
XMLSecurityKey.php
Go to the documentation of this file.
1 <?php
2 namespace RobRichards\XMLSecLibs;
3 
4 use DOMElement;
5 use Exception;
6 
48 {
49  const TRIPLEDES_CBC = 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc';
50  const AES128_CBC = 'http://www.w3.org/2001/04/xmlenc#aes128-cbc';
51  const AES192_CBC = 'http://www.w3.org/2001/04/xmlenc#aes192-cbc';
52  const AES256_CBC = 'http://www.w3.org/2001/04/xmlenc#aes256-cbc';
53  const RSA_1_5 = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5';
54  const RSA_OAEP_MGF1P = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p';
55  const DSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#dsa-sha1';
56  const RSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1';
57  const RSA_SHA256 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256';
58  const RSA_SHA384 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384';
59  const RSA_SHA512 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512';
60  const HMAC_SHA1 = 'http://www.w3.org/2000/09/xmldsig#hmac-sha1';
61 
63  private $cryptParams = array();
64 
66  public $type = 0;
67 
69  public $key = null;
70 
72  public $passphrase = "";
73 
75  public $iv = null;
76 
78  public $name = null;
79 
81  public $keyChain = null;
82 
84  public $isEncrypted = false;
85 
87  public $encryptedCtx = null;
88 
90  public $guid = null;
91 
97  private $x509Certificate = null;
98 
103  private $X509Thumbprint = null;
104 
110  public function __construct($type, $params=null)
111  {
112  switch ($type) {
113  case (self::TRIPLEDES_CBC):
114  $this->cryptParams['library'] = 'openssl';
115  $this->cryptParams['cipher'] = 'des-ede3-cbc';
116  $this->cryptParams['type'] = 'symmetric';
117  $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc';
118  $this->cryptParams['keysize'] = 24;
119  $this->cryptParams['blocksize'] = 8;
120  break;
121  case (self::AES128_CBC):
122  $this->cryptParams['library'] = 'openssl';
123  $this->cryptParams['cipher'] = 'aes-128-cbc';
124  $this->cryptParams['type'] = 'symmetric';
125  $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes128-cbc';
126  $this->cryptParams['keysize'] = 16;
127  $this->cryptParams['blocksize'] = 16;
128  break;
129  case (self::AES192_CBC):
130  $this->cryptParams['library'] = 'openssl';
131  $this->cryptParams['cipher'] = 'aes-192-cbc';
132  $this->cryptParams['type'] = 'symmetric';
133  $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes192-cbc';
134  $this->cryptParams['keysize'] = 24;
135  $this->cryptParams['blocksize'] = 16;
136  break;
137  case (self::AES256_CBC):
138  $this->cryptParams['library'] = 'openssl';
139  $this->cryptParams['cipher'] = 'aes-256-cbc';
140  $this->cryptParams['type'] = 'symmetric';
141  $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes256-cbc';
142  $this->cryptParams['keysize'] = 32;
143  $this->cryptParams['blocksize'] = 16;
144  break;
145  case (self::RSA_1_5):
146  $this->cryptParams['library'] = 'openssl';
147  $this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING;
148  $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5';
149  if (is_array($params) && ! empty($params['type'])) {
150  if ($params['type'] == 'public' || $params['type'] == 'private') {
151  $this->cryptParams['type'] = $params['type'];
152  break;
153  }
154  }
155  throw new Exception('Certificate "type" (private/public) must be passed via parameters');
156  case (self::RSA_OAEP_MGF1P):
157  $this->cryptParams['library'] = 'openssl';
158  $this->cryptParams['padding'] = OPENSSL_PKCS1_OAEP_PADDING;
159  $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p';
160  $this->cryptParams['hash'] = null;
161  if (is_array($params) && ! empty($params['type'])) {
162  if ($params['type'] == 'public' || $params['type'] == 'private') {
163  $this->cryptParams['type'] = $params['type'];
164  break;
165  }
166  }
167  throw new Exception('Certificate "type" (private/public) must be passed via parameters');
168  case (self::RSA_SHA1):
169  $this->cryptParams['library'] = 'openssl';
170  $this->cryptParams['method'] = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1';
171  $this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING;
172  if (is_array($params) && ! empty($params['type'])) {
173  if ($params['type'] == 'public' || $params['type'] == 'private') {
174  $this->cryptParams['type'] = $params['type'];
175  break;
176  }
177  }
178  throw new Exception('Certificate "type" (private/public) must be passed via parameters');
179  case (self::RSA_SHA256):
180  $this->cryptParams['library'] = 'openssl';
181  $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256';
182  $this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING;
183  $this->cryptParams['digest'] = 'SHA256';
184  if (is_array($params) && ! empty($params['type'])) {
185  if ($params['type'] == 'public' || $params['type'] == 'private') {
186  $this->cryptParams['type'] = $params['type'];
187  break;
188  }
189  }
190  throw new Exception('Certificate "type" (private/public) must be passed via parameters');
191  case (self::RSA_SHA384):
192  $this->cryptParams['library'] = 'openssl';
193  $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384';
194  $this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING;
195  $this->cryptParams['digest'] = 'SHA384';
196  if (is_array($params) && ! empty($params['type'])) {
197  if ($params['type'] == 'public' || $params['type'] == 'private') {
198  $this->cryptParams['type'] = $params['type'];
199  break;
200  }
201  }
202  throw new Exception('Certificate "type" (private/public) must be passed via parameters');
203  case (self::RSA_SHA512):
204  $this->cryptParams['library'] = 'openssl';
205  $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512';
206  $this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING;
207  $this->cryptParams['digest'] = 'SHA512';
208  if (is_array($params) && ! empty($params['type'])) {
209  if ($params['type'] == 'public' || $params['type'] == 'private') {
210  $this->cryptParams['type'] = $params['type'];
211  break;
212  }
213  }
214  throw new Exception('Certificate "type" (private/public) must be passed via parameters');
215  case (self::HMAC_SHA1):
216  $this->cryptParams['library'] = $type;
217  $this->cryptParams['method'] = 'http://www.w3.org/2000/09/xmldsig#hmac-sha1';
218  break;
219  default:
220  throw new Exception('Invalid Key Type');
221  }
222  $this->type = $type;
223  }
224 
233  public function getSymmetricKeySize()
234  {
235  if (! isset($this->cryptParams['keysize'])) {
236  return null;
237  }
238  return $this->cryptParams['keysize'];
239  }
240 
247  public function generateSessionKey()
248  {
249  if (!isset($this->cryptParams['keysize'])) {
250  throw new Exception('Unknown key size for type "' . $this->type . '".');
251  }
252  $keysize = $this->cryptParams['keysize'];
253 
254  $key = openssl_random_pseudo_bytes($keysize);
255 
256  if ($this->type === self::TRIPLEDES_CBC) {
257  /* Make sure that the generated key has the proper parity bits set.
258  * Mcrypt doesn't care about the parity bits, but others may care.
259  */
260  for ($i = 0; $i < strlen($key); $i++) {
261  $byte = ord($key[$i]) & 0xfe;
262  $parity = 1;
263  for ($j = 1; $j < 8; $j++) {
264  $parity ^= ($byte >> $j) & 1;
265  }
266  $byte |= $parity;
267  $key[$i] = chr($byte);
268  }
269  }
270 
271  $this->key = $key;
272  return $key;
273  }
274 
281  public static function getRawThumbprint($cert)
282  {
283 
284  $arCert = explode("\n", $cert);
285  $data = '';
286  $inData = false;
287 
288  foreach ($arCert AS $curData) {
289  if (! $inData) {
290  if (strncmp($curData, '-----BEGIN CERTIFICATE', 22) == 0) {
291  $inData = true;
292  }
293  } else {
294  if (strncmp($curData, '-----END CERTIFICATE', 20) == 0) {
295  break;
296  }
297  $data .= trim($curData);
298  }
299  }
300 
301  if (! empty($data)) {
302  return strtolower(sha1(base64_decode($data)));
303  }
304 
305  return null;
306  }
307 
316  public function loadKey($key, $isFile=false, $isCert = false)
317  {
318  if ($isFile) {
319  $this->key = file_get_contents($key);
320  } else {
321  $this->key = $key;
322  }
323  if ($isCert) {
324  $this->key = openssl_x509_read($this->key);
325  openssl_x509_export($this->key, $str_cert);
326  $this->x509Certificate = $str_cert;
327  $this->key = $str_cert;
328  } else {
329  $this->x509Certificate = null;
330  }
331  if ($this->cryptParams['library'] == 'openssl') {
332  switch ($this->cryptParams['type']) {
333  case 'public':
334  if ($isCert) {
335  /* Load the thumbprint if this is an X509 certificate. */
336  $this->X509Thumbprint = self::getRawThumbprint($this->key);
337  }
338  $this->key = openssl_get_publickey($this->key);
339  if (! $this->key) {
340  throw new Exception('Unable to extract public key');
341  }
342  break;
343 
344  case 'private':
345  $this->key = openssl_get_privatekey($this->key, $this->passphrase);
346  break;
347 
348  case'symmetric':
349  if (strlen($this->key) < $this->cryptParams['keysize']) {
350  throw new Exception('Key must contain at least 25 characters for this cipher');
351  }
352  break;
353 
354  default:
355  throw new Exception('Unknown type');
356  }
357  }
358  }
359 
368  private function padISO10126($data, $blockSize)
369  {
370  if ($blockSize > 256) {
371  throw new Exception('Block size higher than 256 not allowed');
372  }
373  $padChr = $blockSize - (strlen($data) % $blockSize);
374  $pattern = chr($padChr);
375  return $data . str_repeat($pattern, $padChr);
376  }
377 
384  private function unpadISO10126($data)
385  {
386  $padChr = substr($data, -1);
387  $padLen = ord($padChr);
388  return substr($data, 0, -$padLen);
389  }
390 
397  private function encryptSymmetric($data)
398  {
399  $this->iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($this->cryptParams['cipher']));
400  $data = $this->padISO10126($data, $this->cryptParams['blocksize']);
401  $encrypted = openssl_encrypt($data, $this->cryptParams['cipher'], $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->iv);
402  if (false === $encrypted) {
403  throw new Exception('Failure encrypting Data (openssl symmetric) - ' . openssl_error_string());
404  }
405  return $this->iv . $encrypted;
406  }
407 
414  private function decryptSymmetric($data)
415  {
416  $iv_length = openssl_cipher_iv_length($this->cryptParams['cipher']);
417  $this->iv = substr($data, 0, $iv_length);
418  $data = substr($data, $iv_length);
419  $decrypted = openssl_decrypt($data, $this->cryptParams['cipher'], $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->iv);
420  if (false === $decrypted) {
421  throw new Exception('Failure decrypting Data (openssl symmetric) - ' . openssl_error_string());
422  }
423  return $this->unpadISO10126($decrypted);
424  }
425 
433  private function encryptPublic($data)
434  {
435  if (! openssl_public_encrypt($data, $encrypted, $this->key, $this->cryptParams['padding'])) {
436  throw new Exception('Failure encrypting Data (openssl public) - ' . openssl_error_string());
437  }
438  return $encrypted;
439  }
440 
448  private function decryptPublic($data)
449  {
450  if (! openssl_public_decrypt($data, $decrypted, $this->key, $this->cryptParams['padding'])) {
451  throw new Exception('Failure decrypting Data (openssl public) - ' . openssl_error_string());
452  }
453  return $decrypted;
454  }
455 
463  private function encryptPrivate($data)
464  {
465  if (! openssl_private_encrypt($data, $encrypted, $this->key, $this->cryptParams['padding'])) {
466  throw new Exception('Failure encrypting Data (openssl private) - ' . openssl_error_string());
467  }
468  return $encrypted;
469  }
470 
478  private function decryptPrivate($data)
479  {
480  if (! openssl_private_decrypt($data, $decrypted, $this->key, $this->cryptParams['padding'])) {
481  throw new Exception('Failure decrypting Data (openssl private) - ' . openssl_error_string());
482  }
483  return $decrypted;
484  }
485 
493  private function signOpenSSL($data)
494  {
495  $algo = OPENSSL_ALGO_SHA1;
496  if (! empty($this->cryptParams['digest'])) {
497  $algo = $this->cryptParams['digest'];
498  }
499  if (! openssl_sign($data, $signature, $this->key, $algo)) {
500  throw new Exception('Failure Signing Data: ' . openssl_error_string() . ' - ' . $algo);
501  }
502  return $signature;
503  }
504 
521  private function verifyOpenSSL($data, $signature)
522  {
523  $algo = OPENSSL_ALGO_SHA1;
524  if (! empty($this->cryptParams['digest'])) {
525  $algo = $this->cryptParams['digest'];
526  }
527  return openssl_verify($data, $signature, $this->key, $algo);
528  }
529 
536  public function encryptData($data)
537  {
538  if ($this->cryptParams['library'] === 'openssl') {
539  switch ($this->cryptParams['type']) {
540  case 'symmetric':
541  return $this->encryptSymmetric($data);
542  case 'public':
543  return $this->encryptPublic($data);
544  case 'private':
545  return $this->encryptPrivate($data);
546  }
547  }
548  }
549 
556  public function decryptData($data)
557  {
558  if ($this->cryptParams['library'] === 'openssl') {
559  switch ($this->cryptParams['type']) {
560  case 'symmetric':
561  return $this->decryptSymmetric($data);
562  case 'public':
563  return $this->decryptPublic($data);
564  case 'private':
565  return $this->decryptPrivate($data);
566  }
567  }
568  }
569 
576  public function signData($data)
577  {
578  switch ($this->cryptParams['library']) {
579  case 'openssl':
580  return $this->signOpenSSL($data);
581  case (self::HMAC_SHA1):
582  return hash_hmac("sha1", $data, $this->key, true);
583  }
584  }
585 
602  public function verifySignature($data, $signature)
603  {
604  switch ($this->cryptParams['library']) {
605  case 'openssl':
606  return $this->verifyOpenSSL($data, $signature);
607  case (self::HMAC_SHA1):
608  $expectedSignature = hash_hmac("sha1", $data, $this->key, true);
609  return strcmp($signature, $expectedSignature) == 0;
610  }
611  }
612 
618  public function getAlgorith()
619  {
620  return $this->getAlgorithm();
621  }
622 
626  public function getAlgorithm()
627  {
628  return $this->cryptParams['method'];
629  }
630 
637  public static function makeAsnSegment($type, $string)
638  {
639  switch ($type) {
640  case 0x02:
641  if (ord($string) > 0x7f)
642  $string = chr(0).$string;
643  break;
644  case 0x03:
645  $string = chr(0).$string;
646  break;
647  }
648 
649  $length = strlen($string);
650 
651  if ($length < 128) {
652  $output = sprintf("%c%c%s", $type, $length, $string);
653  } else if ($length < 0x0100) {
654  $output = sprintf("%c%c%c%s", $type, 0x81, $length, $string);
655  } else if ($length < 0x010000) {
656  $output = sprintf("%c%c%c%c%s", $type, 0x82, $length / 0x0100, $length % 0x0100, $string);
657  } else {
658  $output = null;
659  }
660  return $output;
661  }
662 
670  public static function convertRSA($modulus, $exponent)
671  {
672  /* make an ASN publicKeyInfo */
673  $exponentEncoding = self::makeAsnSegment(0x02, $exponent);
674  $modulusEncoding = self::makeAsnSegment(0x02, $modulus);
675  $sequenceEncoding = self::makeAsnSegment(0x30, $modulusEncoding.$exponentEncoding);
676  $bitstringEncoding = self::makeAsnSegment(0x03, $sequenceEncoding);
677  $rsaAlgorithmIdentifier = pack("H*", "300D06092A864886F70D0101010500");
678  $publicKeyInfo = self::makeAsnSegment(0x30, $rsaAlgorithmIdentifier.$bitstringEncoding);
679 
680  /* encode the publicKeyInfo in base64 and add PEM brackets */
681  $publicKeyInfoBase64 = base64_encode($publicKeyInfo);
682  $encoding = "-----BEGIN PUBLIC KEY-----\n";
683  $offset = 0;
684  while ($segment = substr($publicKeyInfoBase64, $offset, 64)) {
685  $encoding = $encoding.$segment."\n";
686  $offset += 64;
687  }
688  return $encoding."-----END PUBLIC KEY-----\n";
689  }
690 
694  public function serializeKey($parent)
695  {
696 
697  }
698 
707  public function getX509Certificate()
708  {
709  return $this->x509Certificate;
710  }
711 
721  public function getX509Thumbprint()
722  {
723  return $this->X509Thumbprint;
724  }
725 
726 
735  public static function fromEncryptedKeyElement(DOMElement $element)
736  {
737 
738  $objenc = new XMLSecEnc();
739  $objenc->setNode($element);
740  if (! $objKey = $objenc->locateKey()) {
741  throw new Exception("Unable to locate algorithm for this Encrypted Key");
742  }
743  $objKey->isEncrypted = true;
744  $objKey->encryptedCtx = $objenc;
745  XMLSecEnc::staticLocateKeyInfo($objKey, $element);
746  return $objKey;
747  }
748 
749 }
$params
Definition: disable.php:11
getSymmetricKeySize()
Retrieve the key size for the symmetric encryption algorithm.
static getRawThumbprint($cert)
Get the raw thumbprint of a certificate.
static fromEncryptedKeyElement(DOMElement $element)
Create key from an EncryptedKey-element.
signOpenSSL($data)
Signs the given data (string) using the openssl-extension.
encryptPublic($data)
Encrypts the given public data (string) using the openssl-extension.
signData($data)
Signs the data (string) using the extension assigned to the type in the constructor.
$algo
Definition: pwgen.php:34
encryptSymmetric($data)
Encrypts the given data (string) using the openssl-extension.
static convertRSA($modulus, $exponent)
Hint: Modulus and Exponent must already be base64 decoded.
if(!is_dir( $entity_dir)) exit("Fatal Error ([A-Za-z0-9]+)\+" &#(? foreach( $entity_files as $file) $output
encryptPrivate($data)
Encrypts the given private data (string) using the openssl-extension.
decryptSymmetric($data)
Decrypts the given data (string) using the openssl-extension.
loadKey($key, $isFile=false, $isCert=false)
Loads the given key, or - with isFile set true - the key from the keyfile.
verifyOpenSSL($data, $signature)
Verifies the given data (string) belonging to the given signature using the openssl-extension.
static staticLocateKeyInfo($objBaseKey=null, $node=null)
Definition: XMLSecEnc.php:410
unpadISO10126($data)
Remove ISO 10126 Padding.
verifySignature($data, $signature)
Verifies the data (string) against the given signature using the extension assigned to the type in th...
Create styles array
The data for the language used.
getX509Thumbprint()
Get the thumbprint of this X509 certificate.
generateSessionKey()
Generates a session key using the openssl-extension.
$i
Definition: disco.tpl.php:19
encryptData($data)
Encrypts the given data (string) using the regarding php-extension, depending on the library assigned...
decryptPublic($data)
Decrypts the given public data (string) using the openssl-extension.
padISO10126($data, $blockSize)
ISO 10126 Padding.
decryptPrivate($data)
Decrypts the given private data (string) using the openssl-extension.
getX509Certificate()
Retrieve the X509 certificate this key represents.
decryptData($data)
Decrypts the given data (string) using the regarding php-extension, depending on the library assigned...