Test Setup Failed
Push — master ( f71949...6c6bd7 )
by Julito
55:21
created

WSSESoap::AddReference()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 19
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 15
nc 6
nop 2
dl 0
loc 19
rs 8.8571
c 0
b 0
f 0
1
<?php
2
/**
3
 * soap-wsse.php
4
 *
5
 * Copyright (c) 2010, Robert Richards <[email protected]>.
6
 * All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 *
12
 *   * Redistributions of source code must retain the above copyright
13
 *     notice, this list of conditions and the following disclaimer.
14
 *
15
 *   * Redistributions in binary form must reproduce the above copyright
16
 *     notice, this list of conditions and the following disclaimer in
17
 *     the documentation and/or other materials provided with the
18
 *     distribution.
19
 *
20
 *   * Neither the name of Robert Richards nor the names of his
21
 *     contributors may be used to endorse or promote products derived
22
 *     from this software without specific prior written permission.
23
 *
24
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35
 * POSSIBILITY OF SUCH DAMAGE.
36
 *
37
 * @author     Robert Richards <[email protected]>
38
 * @copyright  2007-2010 Robert Richards <[email protected]>
39
 * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
40
 * @version    1.1.0-dev
41
 */
42
43
require('xmlseclibs.php');
44
45
class WSSESoap {
46
    const WSSENS = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd';
47
    const WSUNS = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd';
48
    const WSUNAME = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0';
49
    const WSSEPFX = 'wsse';
50
    const WSUPFX = 'wsu';
51
    private $soapNS, $soapPFX;
52
    private $soapDoc = NULL;
53
    private $envelope = NULL;
54
    private $SOAPXPath = NULL;
55
    private $secNode = NULL;
56
    public $signAllHeaders = FALSE;
57
58
    private function locateSecurityHeader($bMustUnderstand = TRUE, $setActor = NULL) {
59
        if ($this->secNode == NULL) {
60
            $headers = $this->SOAPXPath->query('//wssoap:Envelope/wssoap:Header');
61
            $header = $headers->item(0);
62 View Code Duplication
            if (! $header) {
63
                $header = $this->soapDoc->createElementNS($this->soapNS, $this->soapPFX.':Header');
64
                $this->envelope->insertBefore($header, $this->envelope->firstChild);
65
            }
66
            $secnodes = $this->SOAPXPath->query('./wswsse:Security', $header);
67
            $secnode = NULL;
68
            foreach ($secnodes AS $node) {
69
                $actor = $node->getAttributeNS($this->soapNS, 'actor');
70
                if ($actor == $setActor) {
71
                    $secnode = $node;
72
                    break;
73
                }
74
            }
75
            if (! $secnode) {
76
                $secnode = $this->soapDoc->createElementNS(self::WSSENS, self::WSSEPFX.':Security');
77
                ///if (isset($secnode) && !empty($secnode)) {
78
                    $header->appendChild($secnode);
79
                //}
80
                if ($bMustUnderstand) {
81
                    $secnode->setAttributeNS($this->soapNS, $this->soapPFX.':mustUnderstand', '1');
82
                }
83
                if (! empty($setActor)) {
84
                    $ename = 'actor';
85
                    if ($this->soapNS == 'http://www.w3.org/2003/05/soap-envelope') {
86
                        $ename = 'role';
87
                    }
88
                    $secnode->setAttributeNS($this->soapNS, $this->soapPFX.':'.$ename, $setActor);
89
                }
90
            }
91
            $this->secNode = $secnode;
92
        }
93
        return $this->secNode;
94
    }
95
96
    public function __construct($doc, $bMustUnderstand = TRUE, $setActor=NULL) {
97
        $this->soapDoc = $doc;
98
        $this->envelope = $doc->documentElement;
99
        $this->soapNS = $this->envelope->namespaceURI;
100
        $this->soapPFX = $this->envelope->prefix;
101
        $this->SOAPXPath = new DOMXPath($doc);
102
        $this->SOAPXPath->registerNamespace('wssoap', $this->soapNS);
103
        $this->SOAPXPath->registerNamespace('wswsse', self::WSSENS);
104
        $this->locateSecurityHeader($bMustUnderstand, $setActor);
105
    }
106
107
    public function addTimestamp($secondsToExpire=3600) {
108
        /* Add the WSU timestamps */
109
        $security = $this->locateSecurityHeader();
110
111
        $timestamp = $this->soapDoc->createElementNS(self::WSUNS, self::WSUPFX.':Timestamp');
112
        $security->insertBefore($timestamp, $security->firstChild);
113
        $currentTime = time();
114
        $created = $this->soapDoc->createElementNS(self::WSUNS,  self::WSUPFX.':Created', gmdate("Y-m-d\TH:i:s", $currentTime).'Z');
115
        $timestamp->appendChild($created);
116
        if (! is_null($secondsToExpire)) {
117
            $expire = $this->soapDoc->createElementNS(self::WSUNS,  self::WSUPFX.':Expires', gmdate("Y-m-d\TH:i:s", $currentTime + $secondsToExpire).'Z');
118
            $timestamp->appendChild($expire);
119
        }
120
    }
121
122
    public function addUserToken($userName, $password=NULL, $passwordDigest=FALSE) {
123
        if ($passwordDigest && empty($password)) {
124
            throw new Exception("Cannot calculate the digest without a password");
125
        }
126
127
        $security = $this->locateSecurityHeader();
128
129
        $token = $this->soapDoc->createElementNS(self::WSSENS, self::WSSEPFX.':UsernameToken');
130
        $security->insertBefore($token, $security->firstChild);
131
132
        $username = $this->soapDoc->createElementNS(self::WSSENS,  self::WSSEPFX.':Username', $userName);
133
        $token->appendChild($username);
134
135
        /* Generate nonce - create a 256 bit session key to be used */
136
        $objKey = new XMLSecurityKey(XMLSecurityKey::AES256_CBC);
137
        $nonce = $objKey->generateSessionKey();
138
        unset($objKey);
139
        $createdate = gmdate("Y-m-d\TH:i:s").'Z';
140
141
        if ($password) {
142
            $passType = self::WSUNAME.'#PasswordText';
143
            if ($passwordDigest) {
144
                $password = base64_encode(sha1($nonce.$createdate. $password, true));
145
                $passType = self::WSUNAME.'#PasswordDigest';
146
            }
147
            $passwordNode = $this->soapDoc->createElementNS(self::WSSENS,  self::WSSEPFX.':Password', $password);
148
            $token->appendChild($passwordNode);
149
            $passwordNode->setAttribute('Type', $passType);
150
        }
151
152
        $nonceNode = $this->soapDoc->createElementNS(self::WSSENS,  self::WSSEPFX.':Nonce', base64_encode($nonce));
153
        $token->appendChild($nonceNode);
154
155
        $created = $this->soapDoc->createElementNS(self::WSUNS,  self::WSUPFX.':Created', $createdate);
156
        $token->appendChild($created);
157
    }
158
159
    public function addBinaryToken($cert, $isPEMFormat=TRUE, $isDSig=TRUE) {
160
        $security = $this->locateSecurityHeader();
161
        $data = XMLSecurityDSig::get509XCert($cert, $isPEMFormat);
162
163
        $token = $this->soapDoc->createElementNS(self::WSSENS, self::WSSEPFX.':BinarySecurityToken', $data);
164
        $security->insertBefore($token, $security->firstChild);
165
166
        $token->setAttribute('EncodingType', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary');
167
        $token->setAttributeNS(self::WSUNS, self::WSUPFX.':Id', XMLSecurityDSig::generate_GUID());
168
        $token->setAttribute('ValueType', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3');
169
170
        return $token;
171
    }
172
173
    public function attachTokentoSig($token) {
174
        if (! ($token instanceof DOMElement)) {
175
            throw new Exception('Invalid parameter: BinarySecurityToken element expected');
176
        }
177
        $objXMLSecDSig = new XMLSecurityDSig();
178
        if ($objDSig = $objXMLSecDSig->locateSignature($this->soapDoc)) {
179
            $tokenURI = '#'.$token->getAttributeNS(self::WSUNS, "Id");
180
            $this->SOAPXPath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
181
            $query = "./secdsig:KeyInfo";
182
            $nodeset = $this->SOAPXPath->query($query, $objDSig);
183
            $keyInfo = $nodeset->item(0);
184
            if (! $keyInfo) {
185
                $keyInfo = $objXMLSecDSig->createNewSignNode('KeyInfo');
186
                $objDSig->appendChild($keyInfo);
187
            }
188
189
            $tokenRef = $this->soapDoc->createElementNS(self::WSSENS, self::WSSEPFX.':SecurityTokenReference');
190
            $keyInfo->appendChild($tokenRef);
191
            $reference = $this->soapDoc->createElementNS(self::WSSENS, self::WSSEPFX.':Reference');
192
            $reference->setAttribute("URI", $tokenURI);
193
            $tokenRef->appendChild($reference);
194
        } else {
195
            throw new Exception('Unable to locate digital signature');
196
        }
197
    }
198
199
    public function signSoapDoc($objKey, $options = NULL) {
200
        $objDSig = new XMLSecurityDSig();
201
202
        $objDSig->setCanonicalMethod(XMLSecurityDSig::EXC_C14N);
203
204
        $arNodes = array();
205
        foreach ($this->secNode->childNodes AS $node) {
206
            if ($node->nodeType == XML_ELEMENT_NODE) {
207
                $arNodes[] = $node;
208
            }
209
        }
210
211
        if ($this->signAllHeaders) {
212
            foreach ($this->secNode->parentNode->childNodes AS $node) {
213
                if (($node->nodeType == XML_ELEMENT_NODE) &&
214
                ($node->namespaceURI != self::WSSENS)) {
215
                    $arNodes[] = $node;
216
                }
217
            }
218
        }
219
220 View Code Duplication
        foreach ($this->envelope->childNodes AS $node) {
221
            if ($node->namespaceURI == $this->soapNS && $node->localName == 'Body') {
222
                $arNodes[] = $node;
223
                break;
224
            }
225
        }
226
227
        $algorithm = XMLSecurityDSig::SHA1;
228
        if (is_array($options) && isset($options["algorithm"])) {
229
            $algorithm = $options["algorithm"];
230
        }
231
232
        $arOptions = array('prefix'=>self::WSUPFX, 'prefix_ns'=>self::WSUNS);
233
        $objDSig->addReferenceList($arNodes, $algorithm, NULL, $arOptions);
234
235
        $objDSig->sign($objKey);
236
237
        $insertTop = TRUE;
238
        if (is_array($options) && isset($options["insertBefore"])) {
239
            $insertTop = (bool)$options["insertBefore"];
240
        }
241
        $objDSig->appendSignature($this->secNode, $insertTop);
242
243
/* New suff */
244
245
        if (is_array($options)) {
246
            if (! empty($options["KeyInfo"]) ) {
247
                if (! empty($options["KeyInfo"]["X509SubjectKeyIdentifier"])) {
248
                    $sigNode = $this->secNode->firstChild->nextSibling;
249
                    $objDoc = $sigNode->ownerDocument;
250
                    $keyInfo = $sigNode->ownerDocument->createElementNS(XMLSecurityDSig::XMLDSIGNS, 'ds:KeyInfo');
251
                    $sigNode->appendChild($keyInfo);
252
				    $tokenRef = $objDoc->createElementNS(self::WSSENS, self::WSSEPFX . ':SecurityTokenReference');
253
				    $keyInfo->appendChild($tokenRef);
254
				    $reference = $objDoc->createElementNS(self::WSSENS, self::WSSEPFX . ':KeyIdentifier');
255
				    $reference->setAttribute("ValueType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509SubjectKeyIdentifier");
256
				    $reference->setAttribute("EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary");
257
                    $tokenRef->appendChild($reference);
258
					$x509 = openssl_x509_parse($objKey->getX509Certificate());
259
					$keyid = $x509["extensions"]["subjectKeyIdentifier"];
260
					$arkeyid = split(":", $keyid);
261
262
					$data = "";
263
					foreach ($arkeyid AS $hexchar) {
264
					    $data .= chr(hexdec($hexchar));
265
					}
266
					$dataNode = new DOMText(base64_encode($data));
267
					$reference->appendChild($dataNode);
268
                }
269
            }
270
        }
271
    }
272
273
    public function addEncryptedKey($node, $key, $token, $options = NULL) {
274
        if (! $key->encKey) {
275
            return FALSE;
276
        }
277
        $encKey = $key->encKey;
278
        $security = $this->locateSecurityHeader();
279
        $doc = $security->ownerDocument;
280
        if (! $doc->isSameNode($encKey->ownerDocument)) {
281
            $key->encKey = $security->ownerDocument->importNode($encKey, TRUE);
282
            $encKey = $key->encKey;
283
        }
284
        if (! empty($key->guid)) {
285
            return TRUE;
286
        }
287
288
        $lastToken = NULL;
289
        $findTokens = $security->firstChild;
290
        while ($findTokens) {
291
            if ($findTokens->localName == 'BinarySecurityToken') {
292
                $lastToken = $findTokens;
293
            }
294
            $findTokens = $findTokens->nextSibling;
295
        }
296
        if ($lastToken) {
297
            $lastToken = $lastToken->nextSibling;
298
        }
299
300
        $security->insertBefore($encKey, $lastToken);
301
        $key->guid = XMLSecurityDSig::generate_GUID();
302
        $encKey->setAttribute('Id', $key->guid);
303
        $encMethod = $encKey->firstChild;
304
        while ($encMethod && $encMethod->localName != 'EncryptionMethod') {
305
            $encMethod = $encMethod->nextChild;
306
        }
307
        if ($encMethod) {
308
            $encMethod = $encMethod->nextSibling;
309
        }
310
        $objDoc = $encKey->ownerDocument;
311
        $keyInfo = $objDoc->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyInfo');
312
        $encKey->insertBefore($keyInfo, $encMethod);
313
        $tokenRef = $objDoc->createElementNS(self::WSSENS, self::WSSEPFX.':SecurityTokenReference');
314
        $keyInfo->appendChild($tokenRef);
315
/* New suff */
316
        if (is_array($options)) {
317
            if (! empty($options["KeyInfo"]) ) {
318
                if (! empty($options["KeyInfo"]["X509SubjectKeyIdentifier"])) {
319
				    $reference = $objDoc->createElementNS(self::WSSENS, self::WSSEPFX . ':KeyIdentifier');
320
				    $reference->setAttribute("ValueType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509SubjectKeyIdentifier");
321
				    $reference->setAttribute("EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary");
322
				    $tokenRef->appendChild($reference);
323
					$x509 = openssl_x509_parse($token->getX509Certificate());
324
					$keyid = $x509["extensions"]["subjectKeyIdentifier"];
325
					$arkeyid = split(":", $keyid);
326
					$data = "";
327
					foreach ($arkeyid AS $hexchar) {
328
					    $data .= chr(hexdec($hexchar));
329
					}
330
					$dataNode = new DOMText(base64_encode($data));
331
					$reference->appendChild($dataNode);
332
                    return TRUE;
333
                }
334
            }
335
        }
336
337
        $tokenURI = '#'.$token->getAttributeNS(self::WSUNS, "Id");
338
        $reference = $objDoc->createElementNS(self::WSSENS, self::WSSEPFX.':Reference');
339
        $reference->setAttribute("URI", $tokenURI);
340
        $tokenRef->appendChild($reference);
341
342
        return TRUE;
343
    }
344
345
    public function AddReference($baseNode, $guid) {
346
        $refList = NULL;
347
        $child = $baseNode->firstChild;
348
        while($child) {
349
            if (($child->namespaceURI == XMLSecEnc::XMLENCNS) && ($child->localName == 'ReferenceList')) {
350
                $refList = $child;
351
                break;
352
            }
353
            $child = $child->nextSibling;
354
        }
355
        $doc = $baseNode->ownerDocument;
356
        if (is_null($refList)) {
357
            $refList = $doc->createElementNS(XMLSecEnc::XMLENCNS, 'xenc:ReferenceList');
358
            $baseNode->appendChild($refList);
359
        }
360
        $dataref = $doc->createElementNS(XMLSecEnc::XMLENCNS, 'xenc:DataReference');
361
        $refList->appendChild($dataref);
362
        $dataref->setAttribute('URI', '#'.$guid);
363
    }
364
365
    public function EncryptBody($siteKey, $objKey, $token) {
366
367
        $enc = new XMLSecEnc();
368
        $node = false;
369 View Code Duplication
        foreach ($this->envelope->childNodes AS $node) {
370
            if ($node->namespaceURI == $this->soapNS && $node->localName == 'Body') {
371
                break;
372
            }
373
        }
374
        $enc->setNode($node);
375
        /* encrypt the symmetric key */
376
        $enc->encryptKey($siteKey, $objKey, FALSE);
377
378
        $enc->type = XMLSecEnc::Content;
379
        /* Using the symmetric key to actually encrypt the data */
380
        $encNode = $enc->encryptNode($objKey);
381
382
        $guid = XMLSecurityDSig::generate_GUID();
383
        $encNode->setAttribute('Id', $guid);
384
385
        $refNode = $encNode->firstChild;
386
        while($refNode && $refNode->nodeType != XML_ELEMENT_NODE) {
387
            $refNode = $refNode->nextSibling;
388
        }
389
        if ($refNode) {
390
            $refNode = $refNode->nextSibling;
391
        }
392
        if ($this->addEncryptedKey($encNode, $enc, $token)) {
393
            $this->AddReference($enc->encKey, $guid);
394
        }
395
    }
396
397
    public function encryptSoapDoc($siteKey, $objKey, $options=NULL, $encryptSignature=TRUE) {
398
399
		$enc = new XMLSecEnc();
400
401
		$xpath = new DOMXPath($this->envelope->ownerDocument);
402
		if ($encryptSignature ==  FALSE) {
403
			$nodes = $xpath->query('//*[local-name()="Body"]');
404
		} else {
405
			$nodes = $xpath->query('//*[local-name()="Signature"] | //*[local-name()="Body"]');
406
		}
407
408
		foreach ($nodes AS $node) {
409
			$type = XMLSecEnc::Element;
410
			$name = $node->localName;
411
			if ($name == "Body") {
412
				$type = XMLSecEnc::Content;
413
			}
414
			$enc->addReference($name, $node, $type);
415
		}
416
417
		$enc->encryptReferences($objKey);
418
419
		$enc->encryptKey($siteKey, $objKey, false);
420
421
		$nodes = $xpath->query('//*[local-name()="Security"]');
422
		$signode = $nodes->item(0);
423
		$this->addEncryptedKey($signode, $enc, $siteKey, $options);
424
    }
425
426
    public function decryptSoapDoc($doc, $options) {
427
428
		$privKey = NULL;
429
		$privKey_isFile = FALSE;
430
		$privKey_isCert = FALSE;
431
432
		if (is_array($options)) {
433
			$privKey = (! empty($options["keys"]["private"]["key"]) ? $options["keys"]["private"]["key"] : NULL);
434
			$privKey_isFile = (! empty($options["keys"]["private"]["isFile"]) ? TRUE : FALSE);
435
			$privKey_isCert = (! empty($options["keys"]["private"]["isCert"])  ? TRUE : FALSE);
436
		}
437
438
		$objenc = new XMLSecEnc();
439
440
		$xpath = new DOMXPath($doc);
441
		$envns = $doc->documentElement->namespaceURI;
442
		$xpath->registerNamespace("soapns", $envns);
443
		$xpath->registerNamespace("soapenc", "http://www.w3.org/2001/04/xmlenc#");
444
445
		$nodes = $xpath->query('/soapns:Envelope/soapns:Header/*[local-name()="Security"]/soapenc:EncryptedKey');
446
447
		$references = array();
448
		if ($node = $nodes->item(0)) {
449
			$objenc = new XMLSecEnc();
450
			$objenc->setNode($node);
451
		    if (! $objKey = $objenc->locateKey()) {
452
		        throw new Exception("Unable to locate algorithm for this Encrypted Key");
453
		    }
454
		    $objKey->isEncrypted = TRUE;
455
		    $objKey->encryptedCtx = $objenc;
456
		    XMLSecEnc::staticLocateKeyInfo($objKey, $node);
457
			if ($objKey && $objKey->isEncrypted) {
458
				$objencKey = $objKey->encryptedCtx;
459
				$objKey->loadKey($privKey, $privKey_isFile, $privKey_isCert);
460
				$key = $objencKey->decryptKey($objKey);
461
				$objKey->loadKey($key);
462
			}
463
464
			$refnodes = $xpath->query('./soapenc:ReferenceList/soapenc:DataReference/@URI', $node);
465
			foreach ($refnodes as $reference) {
466
				$references[] = $reference->nodeValue;
467
			}
468
		}
469
470
		foreach ($references AS $reference) {
471
			$arUrl = parse_url($reference);
472
			$reference = $arUrl['fragment'];
473
			$query = '//*[@Id="'.$reference.'"]';
474
			$nodes = $xpath->query($query);
475
			$encData = $nodes->item(0);
476
477
			if ($algo = $xpath->evaluate("string(./soapenc:EncryptionMethod/@Algorithm)", $encData)) {
478
				$objKey = new XMLSecurityKey($algo);
479
				$objKey->loadKey($key);
480
			}
481
482
			$objenc->setNode($encData);
483
			$objenc->type = $encData->getAttribute("Type");
484
			$decrypt = $objenc->decryptNode($objKey, TRUE);
485
		}
486
487
		return TRUE;
488
    }
489
490
    public function saveXML() {
491
        return $this->soapDoc->saveXML();
492
    }
493
494
    public function save($file) {
495
        return $this->soapDoc->save($file);
496
    }
497
}
498
499