1<?php
2namespace RobRichards\XMLSecLibs;
3
4use DOMElement;
5use Exception;
6
7/**
8 * xmlseclibs.php
9 *
10 * Copyright (c) 2007-2019, Robert Richards <rrichards@cdatazone.org>.
11 * All rights reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 *
17 *   * Redistributions of source code must retain the above copyright
18 *     notice, this list of conditions and the following disclaimer.
19 *
20 *   * Redistributions in binary form must reproduce the above copyright
21 *     notice, this list of conditions and the following disclaimer in
22 *     the documentation and/or other materials provided with the
23 *     distribution.
24 *
25 *   * Neither the name of Robert Richards nor the names of his
26 *     contributors may be used to endorse or promote products derived
27 *     from this software without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
32 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
33 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
34 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
35 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
36 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
37 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
39 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
40 * POSSIBILITY OF SUCH DAMAGE.
41 *
42 * @author    Robert Richards <rrichards@cdatazone.org>
43 * @copyright 2007-2019 Robert Richards <rrichards@cdatazone.org>
44 * @license   http://www.opensource.org/licenses/bsd-license.php  BSD License
45 */
46
47class XMLSecurityKey
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
62    /** @var array */
63    private $cryptParams = array();
64
65    /** @var int|string */
66    public $type = 0;
67
68    /** @var mixed|null */
69    public $key = null;
70
71    /** @var string  */
72    public $passphrase = "";
73
74    /** @var string|null */
75    public $iv = null;
76
77    /** @var string|null */
78    public $name = null;
79
80    /** @var mixed|null */
81    public $keyChain = null;
82
83    /** @var bool */
84    public $isEncrypted = false;
85
86    /** @var XMLSecEnc|null */
87    public $encryptedCtx = null;
88
89    /** @var mixed|null */
90    public $guid = null;
91
92    /**
93     * This variable contains the certificate as a string if this key represents an X509-certificate.
94     * If this key doesn't represent a certificate, this will be null.
95     * @var string|null
96     */
97    private $x509Certificate = null;
98
99    /**
100     * This variable contains the certificate thumbprint if we have loaded an X509-certificate.
101     * @var string|null
102     */
103    private $X509Thumbprint = null;
104
105    /**
106     * @param string $type
107     * @param null|array $params
108     * @throws Exception
109     */
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
225    /**
226     * Retrieve the key size for the symmetric encryption algorithm..
227     *
228     * If the key size is unknown, or this isn't a symmetric encryption algorithm,
229     * null is returned.
230     *
231     * @return int|null  The number of bytes in the key.
232     */
233    public function getSymmetricKeySize()
234    {
235        if (! isset($this->cryptParams['keysize'])) {
236            return null;
237        }
238        return $this->cryptParams['keysize'];
239    }
240
241    /**
242     * Generates a session key using the openssl-extension.
243     * In case of using DES3-CBC the key is checked for a proper parity bits set.
244     * @return string
245     * @throws Exception
246     */
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
275    /**
276     * Get the raw thumbprint of a certificate
277     *
278     * @param string $cert
279     * @return null|string
280     */
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
308    /**
309     * Loads the given key, or - with isFile set true - the key from the keyfile.
310     *
311     * @param string $key
312     * @param bool $isFile
313     * @param bool $isCert
314     * @throws Exception
315     */
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
360    /**
361     * ISO 10126 Padding
362     *
363     * @param string $data
364     * @param integer $blockSize
365     * @throws Exception
366     * @return string
367     */
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
378    /**
379     * Remove ISO 10126 Padding
380     *
381     * @param string $data
382     * @return string
383     */
384    private function unpadISO10126($data)
385    {
386        $padChr = substr($data, -1);
387        $padLen = ord($padChr);
388        return substr($data, 0, -$padLen);
389    }
390
391    /**
392     * Encrypts the given data (string) using the openssl-extension
393     *
394     * @param string $data
395     * @return string
396     */
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
408    /**
409     * Decrypts the given data (string) using the openssl-extension
410     *
411     * @param string $data
412     * @return string
413     */
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
426    /**
427     * Encrypts the given public data (string) using the openssl-extension
428     *
429     * @param string $data
430     * @return string
431     * @throws Exception
432     */
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
441    /**
442     * Decrypts the given public data (string) using the openssl-extension
443     *
444     * @param string $data
445     * @return string
446     * @throws Exception
447     */
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
456    /**
457     * Encrypts the given private data (string) using the openssl-extension
458     *
459     * @param string $data
460     * @return string
461     * @throws Exception
462     */
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
471    /**
472     * Decrypts the given private data (string) using the openssl-extension
473     *
474     * @param string $data
475     * @return string
476     * @throws Exception
477     */
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
486    /**
487     * Signs the given data (string) using the openssl-extension
488     *
489     * @param string $data
490     * @return string
491     * @throws Exception
492     */
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
505    /**
506     * Verifies the given data (string) belonging to the given signature using the openssl-extension
507     *
508     * Returns:
509     *  1 on succesful signature verification,
510     *  0 when signature verification failed,
511     *  -1 if an error occurred during processing.
512     *
513     * NOTE: be very careful when checking the return value, because in PHP,
514     * -1 will be cast to True when in boolean context. So always check the
515     * return value in a strictly typed way, e.g. "$obj->verify(...) === 1".
516     *
517     * @param string $data
518     * @param string $signature
519     * @return int
520     */
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
530    /**
531     * Encrypts the given data (string) using the regarding php-extension, depending on the library assigned to algorithm in the contructor.
532     *
533     * @param string $data
534     * @return mixed|string
535     */
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
550    /**
551     * Decrypts the given data (string) using the regarding php-extension, depending on the library assigned to algorithm in the contructor.
552     *
553     * @param string $data
554     * @return mixed|string
555     */
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
570    /**
571     * Signs the data (string) using the extension assigned to the type in the constructor.
572     *
573     * @param string $data
574     * @return mixed|string
575     */
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
586    /**
587     * Verifies the data (string) against the given signature using the extension assigned to the type in the constructor.
588     *
589     * Returns in case of openSSL:
590     *  1 on succesful signature verification,
591     *  0 when signature verification failed,
592     *  -1 if an error occurred during processing.
593     *
594     * NOTE: be very careful when checking the return value, because in PHP,
595     * -1 will be cast to True when in boolean context. So always check the
596     * return value in a strictly typed way, e.g. "$obj->verify(...) === 1".
597     *
598     * @param string $data
599     * @param string $signature
600     * @return bool|int
601     */
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
613    /**
614     * @deprecated
615     * @see getAlgorithm()
616     * @return mixed
617     */
618    public function getAlgorith()
619    {
620        return $this->getAlgorithm();
621    }
622
623    /**
624     * @return mixed
625     */
626    public function getAlgorithm()
627    {
628        return $this->cryptParams['method'];
629    }
630
631    /**
632     *
633     * @param int $type
634     * @param string $string
635     * @return null|string
636     */
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
663    /**
664     *
665     * Hint: Modulus and Exponent must already be base64 decoded
666     * @param string $modulus
667     * @param string $exponent
668     * @return string
669     */
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
691    /**
692     * @param mixed $parent
693     */
694    public function serializeKey($parent)
695    {
696
697    }
698
699    /**
700     * Retrieve the X509 certificate this key represents.
701     *
702     * Will return the X509 certificate in PEM-format if this key represents
703     * an X509 certificate.
704     *
705     * @return string The X509 certificate or null if this key doesn't represent an X509-certificate.
706     */
707    public function getX509Certificate()
708    {
709        return $this->x509Certificate;
710    }
711
712    /**
713     * Get the thumbprint of this X509 certificate.
714     *
715     * Returns:
716     *  The thumbprint as a lowercase 40-character hexadecimal number, or null
717     *  if this isn't a X509 certificate.
718     *
719     *  @return string Lowercase 40-character hexadecimal number of thumbprint
720     */
721    public function getX509Thumbprint()
722    {
723        return $this->X509Thumbprint;
724    }
725
726
727    /**
728     * Create key from an EncryptedKey-element.
729     *
730     * @param DOMElement $element The EncryptedKey-element.
731     * @throws Exception
732     *
733     * @return XMLSecurityKey The new key.
734     */
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}
750