ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Message.php
Go to the documentation of this file.
1<?php
2
4
11{
12
20 public static function addSign(
21 SimpleSAML_Configuration $srcMetadata,
22 SimpleSAML_Configuration $dstMetadata,
23 \SAML2\SignedElement $element
24 ) {
25 $dstPrivateKey = $dstMetadata->getString('signature.privatekey', null);
26
27 if ($dstPrivateKey !== null) {
28 $keyArray = SimpleSAML\Utils\Crypto::loadPrivateKey($dstMetadata, true, 'signature.');
29 $certArray = SimpleSAML\Utils\Crypto::loadPublicKey($dstMetadata, false, 'signature.');
30 } else {
31 $keyArray = SimpleSAML\Utils\Crypto::loadPrivateKey($srcMetadata, true);
32 $certArray = SimpleSAML\Utils\Crypto::loadPublicKey($srcMetadata, false);
33 }
34
35 $algo = $dstMetadata->getString('signature.algorithm', null);
36 if ($algo === null) {
37 $algo = $srcMetadata->getString('signature.algorithm', XMLSecurityKey::RSA_SHA256);
38 }
39
40 $privateKey = new XMLSecurityKey($algo, array('type' => 'private'));
41 if (array_key_exists('password', $keyArray)) {
42 $privateKey->passphrase = $keyArray['password'];
43 }
44 $privateKey->loadKey($keyArray['PEM'], false);
45
46 $element->setSignatureKey($privateKey);
47
48 if ($certArray === null) {
49 // we don't have a certificate to add
50 return;
51 }
52
53 if (!array_key_exists('PEM', $certArray)) {
54 // we have a public key with only a fingerprint
55 return;
56 }
57
58 $element->setCertificates(array($certArray['PEM']));
59 }
60
61
69 private static function addRedirectSign(
70 SimpleSAML_Configuration $srcMetadata,
71 SimpleSAML_Configuration $dstMetadata,
72 \SAML2\Message $message
73 ) {
74
75 $signingEnabled = null;
76 if ($message instanceof \SAML2\LogoutRequest || $message instanceof \SAML2\LogoutResponse) {
77 $signingEnabled = $srcMetadata->getBoolean('sign.logout', null);
78 if ($signingEnabled === null) {
79 $signingEnabled = $dstMetadata->getBoolean('sign.logout', null);
80 }
81 } elseif ($message instanceof \SAML2\AuthnRequest) {
82 $signingEnabled = $srcMetadata->getBoolean('sign.authnrequest', null);
83 if ($signingEnabled === null) {
84 $signingEnabled = $dstMetadata->getBoolean('sign.authnrequest', null);
85 }
86 }
87
88 if ($signingEnabled === null) {
89 $signingEnabled = $dstMetadata->getBoolean('redirect.sign', null);
90 if ($signingEnabled === null) {
91 $signingEnabled = $srcMetadata->getBoolean('redirect.sign', false);
92 }
93 }
94 if (!$signingEnabled) {
95 return;
96 }
97
98 self::addSign($srcMetadata, $dstMetadata, $message);
99 }
100
101
114 private static function findCertificate(array $certFingerprints, array $certificates)
115 {
116 $candidates = array();
117
118 foreach ($certificates as $cert) {
119 $fp = strtolower(sha1(base64_decode($cert)));
120 if (!in_array($fp, $certFingerprints, true)) {
121 $candidates[] = $fp;
122 continue;
123 }
124
125 /* We have found a matching fingerprint. */
126 $pem = "-----BEGIN CERTIFICATE-----\n".
127 chunk_split($cert, 64).
128 "-----END CERTIFICATE-----\n";
129 return $pem;
130 }
131
132 $candidates = "'".implode("', '", $candidates)."'";
133 $fps = "'".implode("', '", $certFingerprints)."'";
134 throw new SimpleSAML_Error_Exception('Unable to find a certificate matching the configured '.
135 'fingerprint. Candidates: '.$candidates.'; certFingerprint: '.$fps.'.');
136 }
137
138
149 public static function checkSign(SimpleSAML_Configuration $srcMetadata, \SAML2\SignedElement $element)
150 {
151 // find the public key that should verify signatures by this entity
152 $keys = $srcMetadata->getPublicKeys('signing');
153 if (!empty($keys)) {
154 $pemKeys = array();
155 foreach ($keys as $key) {
156 switch ($key['type']) {
157 case 'X509Certificate':
158 $pemKeys[] = "-----BEGIN CERTIFICATE-----\n".
159 chunk_split($key['X509Certificate'], 64).
160 "-----END CERTIFICATE-----\n";
161 break;
162 default:
163 SimpleSAML\Logger::debug('Skipping unknown key type: '.$key['type']);
164 }
165 }
166 } elseif ($srcMetadata->hasValue('certFingerprint')) {
168 "Validating certificates by fingerprint is deprecated. Please use ".
169 "certData or certificate options in your remote metadata configuration."
170 );
171
172 $certFingerprint = $srcMetadata->getArrayizeString('certFingerprint');
173 foreach ($certFingerprint as &$fp) {
174 $fp = strtolower(str_replace(':', '', $fp));
175 }
176
177 $certificates = $element->getCertificates();
178
179 // we don't have the full certificate stored. Try to find it in the message or the assertion instead
180 if (count($certificates) === 0) {
181 /* We need the full certificate in order to match it against the fingerprint. */
182 SimpleSAML\Logger::debug('No certificate in message when validating against fingerprint.');
183 return false;
184 } else {
185 SimpleSAML\Logger::debug('Found '.count($certificates).' certificates in '.get_class($element));
186 }
187
188 $pemCert = self::findCertificate($certFingerprint, $certificates);
189 $pemKeys = array($pemCert);
190 } else {
192 'Missing certificate in metadata for '.
193 var_export($srcMetadata->getString('entityid'), true)
194 );
195 }
196
197 SimpleSAML\Logger::debug('Has '.count($pemKeys).' candidate keys for validation.');
198
199 $lastException = null;
200 foreach ($pemKeys as $i => $pem) {
201 $key = new XMLSecurityKey(XMLSecurityKey::RSA_SHA256, array('type' => 'public'));
202 $key->loadKey($pem);
203
204 try {
205 // make sure that we have a valid signature on either the response or the assertion
206 $res = $element->validate($key);
207 if ($res) {
208 SimpleSAML\Logger::debug('Validation with key #'.$i.' succeeded.');
209 return true;
210 }
211 SimpleSAML\Logger::debug('Validation with key #'.$i.' failed without exception.');
212 } catch (Exception $e) {
213 SimpleSAML\Logger::debug('Validation with key #'.$i.' failed with exception: '.$e->getMessage());
214 $lastException = $e;
215 }
216 }
217
218 // we were unable to validate the signature with any of our keys
219 if ($lastException !== null) {
220 throw $lastException;
221 } else {
222 return false;
223 }
224 }
225
226
236 public static function validateMessage(
237 SimpleSAML_Configuration $srcMetadata,
238 SimpleSAML_Configuration $dstMetadata,
239 \SAML2\Message $message
240 ) {
241 $enabled = null;
242 if ($message instanceof \SAML2\LogoutRequest || $message instanceof \SAML2\LogoutResponse) {
243 $enabled = $srcMetadata->getBoolean('validate.logout', null);
244 if ($enabled === null) {
245 $enabled = $dstMetadata->getBoolean('validate.logout', null);
246 }
247 } elseif ($message instanceof \SAML2\AuthnRequest) {
248 $enabled = $srcMetadata->getBoolean('validate.authnrequest', null);
249 if ($enabled === null) {
250 $enabled = $dstMetadata->getBoolean('validate.authnrequest', null);
251 }
252 }
253
254 if ($enabled === null) {
255 $enabled = $srcMetadata->getBoolean('redirect.validate', null);
256 if ($enabled === null) {
257 $enabled = $dstMetadata->getBoolean('redirect.validate', false);
258 }
259 }
260
261 if (!$enabled) {
262 return;
263 }
264
265 if (!self::checkSign($srcMetadata, $message)) {
267 'Validation of received messages enabled, but no signature found on message.'
268 );
269 }
270 }
271
272
281 public static function getDecryptionKeys(
282 SimpleSAML_Configuration $srcMetadata,
283 SimpleSAML_Configuration $dstMetadata
284 ) {
285 $sharedKey = $srcMetadata->getString('sharedkey', null);
286 if ($sharedKey !== null) {
287 $key = new XMLSecurityKey(XMLSecurityKey::AES128_CBC);
288 $key->loadKey($sharedKey);
289 return array($key);
290 }
291
292 $keys = array();
293
294 // load the new private key if it exists
295 $keyArray = SimpleSAML\Utils\Crypto::loadPrivateKey($dstMetadata, false, 'new_');
296 if ($keyArray !== null) {
297 assert(isset($keyArray['PEM']));
298
299 $key = new XMLSecurityKey(XMLSecurityKey::RSA_1_5, array('type' => 'private'));
300 if (array_key_exists('password', $keyArray)) {
301 $key->passphrase = $keyArray['password'];
302 }
303 $key->loadKey($keyArray['PEM']);
304 $keys[] = $key;
305 }
306
307 // find the existing private key
308 $keyArray = SimpleSAML\Utils\Crypto::loadPrivateKey($dstMetadata, true);
309 assert(isset($keyArray['PEM']));
310
311 $key = new XMLSecurityKey(XMLSecurityKey::RSA_1_5, array('type' => 'private'));
312 if (array_key_exists('password', $keyArray)) {
313 $key->passphrase = $keyArray['password'];
314 }
315 $key->loadKey($keyArray['PEM']);
316 $keys[] = $key;
317
318 return $keys;
319 }
320
321
332 public static function getBlacklistedAlgorithms(
333 SimpleSAML_Configuration $srcMetadata,
334 SimpleSAML_Configuration $dstMetadata
335 ) {
336 $blacklist = $srcMetadata->getArray('encryption.blacklisted-algorithms', null);
337 if ($blacklist === null) {
338 $blacklist = $dstMetadata->getArray('encryption.blacklisted-algorithms', array(XMLSecurityKey::RSA_1_5));
339 }
340 return $blacklist;
341 }
342
343
357 private static function decryptAssertion(
358 SimpleSAML_Configuration $srcMetadata,
359 SimpleSAML_Configuration $dstMetadata,
360 $assertion
361 ) {
362 assert($assertion instanceof \SAML2\Assertion || $assertion instanceof \SAML2\EncryptedAssertion);
363
364 if ($assertion instanceof \SAML2\Assertion) {
365 $encryptAssertion = $srcMetadata->getBoolean('assertion.encryption', null);
366 if ($encryptAssertion === null) {
367 $encryptAssertion = $dstMetadata->getBoolean('assertion.encryption', false);
368 }
369 if ($encryptAssertion) {
370 /* The assertion was unencrypted, but we have encryption enabled. */
371 throw new Exception('Received unencrypted assertion, but encryption was enabled.');
372 }
373
374 return $assertion;
375 }
376
377 try {
378 $keys = self::getDecryptionKeys($srcMetadata, $dstMetadata);
379 } catch (Exception $e) {
380 throw new SimpleSAML_Error_Exception('Error decrypting assertion: '.$e->getMessage());
381 }
382
383 $blacklist = self::getBlacklistedAlgorithms($srcMetadata, $dstMetadata);
384
385 $lastException = null;
386 foreach ($keys as $i => $key) {
387 try {
388 $ret = $assertion->getAssertion($key, $blacklist);
389 SimpleSAML\Logger::debug('Decryption with key #'.$i.' succeeded.');
390 return $ret;
391 } catch (Exception $e) {
392 SimpleSAML\Logger::debug('Decryption with key #'.$i.' failed with exception: '.$e->getMessage());
393 $lastException = $e;
394 }
395 }
396 throw $lastException;
397 }
398
399
411 private static function decryptAttributes(
412 SimpleSAML_Configuration $srcMetadata,
413 SimpleSAML_Configuration $dstMetadata,
414 \SAML2\Assertion &$assertion
415 ) {
416 if (!$assertion->hasEncryptedAttributes()) {
417 return;
418 }
419
420 try {
421 $keys = self::getDecryptionKeys($srcMetadata, $dstMetadata);
422 } catch (Exception $e) {
423 throw new SimpleSAML_Error_Exception('Error decrypting attributes: '.$e->getMessage());
424 }
425
426 $blacklist = self::getBlacklistedAlgorithms($srcMetadata, $dstMetadata);
427
428 $error = true;
429 foreach ($keys as $i => $key) {
430 try {
431 $assertion->decryptAttributes($key, $blacklist);
432 SimpleSAML\Logger::debug('Attribute decryption with key #'.$i.' succeeded.');
433 $error = false;
434 break;
435 } catch (Exception $e) {
436 SimpleSAML\Logger::debug('Attribute decryption failed with exception: '.$e->getMessage());
437 }
438 }
439 if ($error) {
440 throw new SimpleSAML_Error_Exception('Could not decrypt the attributes');
441 }
442 }
443
444
452 public static function getResponseError(\SAML2\StatusResponse $response)
453 {
454 $status = $response->getStatus();
455 return new sspmod_saml_Error($status['Code'], $status['SubCode'], $status['Message']);
456 }
457
458
466 public static function buildAuthnRequest(
469 ) {
470 $ar = new \SAML2\AuthnRequest();
471
472 // get the NameIDPolicy to apply. IdP metadata has precedence.
473 $nameIdPolicy = array();
474 if ($idpMetadata->hasValue('NameIDPolicy')) {
475 $nameIdPolicy = $idpMetadata->getValue('NameIDPolicy');
476 } elseif ($spMetadata->hasValue('NameIDPolicy')) {
477 $nameIdPolicy = $spMetadata->getValue('NameIDPolicy');
478 }
479
480 if (!is_array($nameIdPolicy)) {
481 // handle old configurations where 'NameIDPolicy' was used to specify just the format
482 $nameIdPolicy = array('Format' => $nameIdPolicy);
483 }
484
485 $nameIdPolicy_cf = SimpleSAML_Configuration::loadFromArray($nameIdPolicy);
486 $policy = array(
487 'Format' => $nameIdPolicy_cf->getString('Format', \SAML2\Constants::NAMEID_TRANSIENT),
488 'AllowCreate' => $nameIdPolicy_cf->getBoolean('AllowCreate', true),
489 );
490 $spNameQualifier = $nameIdPolicy_cf->getString('SPNameQualifier', false);
491 if ($spNameQualifier !== false) {
492 $policy['SPNameQualifier'] = $spNameQualifier;
493 }
494 $ar->setNameIdPolicy($policy);
495
496 $ar->setForceAuthn($spMetadata->getBoolean('ForceAuthn', false));
497 $ar->setIsPassive($spMetadata->getBoolean('IsPassive', false));
498
499 $protbind = $spMetadata->getValueValidate('ProtocolBinding', array(
500 \SAML2\Constants::BINDING_HTTP_POST,
501 \SAML2\Constants::BINDING_HOK_SSO,
502 \SAML2\Constants::BINDING_HTTP_ARTIFACT,
503 \SAML2\Constants::BINDING_HTTP_REDIRECT,
504 ), \SAML2\Constants::BINDING_HTTP_POST);
505
506 // Shoaib: setting the appropriate binding based on parameter in sp-metadata defaults to HTTP_POST
507 $ar->setProtocolBinding($protbind);
508 $ar->setIssuer($spMetadata->getString('entityid'));
509 $ar->setAssertionConsumerServiceIndex($spMetadata->getInteger('AssertionConsumerServiceIndex', null));
510 $ar->setAttributeConsumingServiceIndex($spMetadata->getInteger('AttributeConsumingServiceIndex', null));
511
512 if ($spMetadata->hasValue('AuthnContextClassRef')) {
513 $accr = $spMetadata->getArrayizeString('AuthnContextClassRef');
514 $comp = $spMetadata->getValueValidate('AuthnContextComparison', array(
515 \SAML2\Constants::COMPARISON_EXACT,
516 \SAML2\Constants::COMPARISON_MINIMUM,
517 \SAML2\Constants::COMPARISON_MAXIMUM,
518 \SAML2\Constants::COMPARISON_BETTER,
519 ), \SAML2\Constants::COMPARISON_EXACT);
520 $ar->setRequestedAuthnContext(array('AuthnContextClassRef' => $accr, 'Comparison' => $comp));
521 }
522
524
525 return $ar;
526 }
527
528
536 public static function buildLogoutRequest(
537 SimpleSAML_Configuration $srcMetadata,
538 SimpleSAML_Configuration $dstMetadata
539 ) {
540 $lr = new \SAML2\LogoutRequest();
541 $lr->setIssuer($srcMetadata->getString('entityid'));
542
543 self::addRedirectSign($srcMetadata, $dstMetadata, $lr);
544
545 return $lr;
546 }
547
548
556 public static function buildLogoutResponse(
557 SimpleSAML_Configuration $srcMetadata,
558 SimpleSAML_Configuration $dstMetadata
559 ) {
560 $lr = new \SAML2\LogoutResponse();
561 $lr->setIssuer($srcMetadata->getString('entityid'));
562
563 self::addRedirectSign($srcMetadata, $dstMetadata, $lr);
564
565 return $lr;
566 }
567
568
583 public static function processResponse(
586 \SAML2\Response $response
587 ) {
588 if (!$response->isSuccess()) {
590 }
591
592 // validate Response-element destination
594 $msgDestination = $response->getDestination();
595 if ($msgDestination !== null && $msgDestination !== $currentURL) {
596 throw new Exception('Destination in response doesn\'t match the current URL. Destination is "'.
597 $msgDestination.'", current URL is "'.$currentURL.'".');
598 }
599
600 $responseSigned = self::checkSign($idpMetadata, $response);
601
602 /*
603 * When we get this far, the response itself is valid.
604 * We only need to check signatures and conditions of the response.
605 */
606 $assertion = $response->getAssertions();
607 if (empty($assertion)) {
608 throw new SimpleSAML_Error_Exception('No assertions found in response from IdP.');
609 }
610
611 $ret = array();
612 foreach ($assertion as $a) {
614 }
615
616 return $ret;
617 }
618
619
636 private static function processAssertion(
639 \SAML2\Response $response,
640 $assertion,
641 $responseSigned
642 ) {
643 assert($assertion instanceof \SAML2\Assertion || $assertion instanceof \SAML2\EncryptedAssertion);
644 assert(is_bool($responseSigned));
645
646 $assertion = self::decryptAssertion($idpMetadata, $spMetadata, $assertion);
648
649 if (!self::checkSign($idpMetadata, $assertion)) {
650 if (!$responseSigned) {
651 throw new SimpleSAML_Error_Exception('Neither the assertion nor the response was signed.');
652 }
653 } // at least one valid signature found
654
656
657 // check various properties of the assertion
658 $notBefore = $assertion->getNotBefore();
659 if ($notBefore !== null && $notBefore > time() + 60) {
661 'Received an assertion that is valid in the future. Check clock synchronization on IdP and SP.'
662 );
663 }
664 $notOnOrAfter = $assertion->getNotOnOrAfter();
665 if ($notOnOrAfter !== null && $notOnOrAfter <= time() - 60) {
667 'Received an assertion that has expired. Check clock synchronization on IdP and SP.'
668 );
669 }
670 $sessionNotOnOrAfter = $assertion->getSessionNotOnOrAfter();
671 if ($sessionNotOnOrAfter !== null && $sessionNotOnOrAfter <= time() - 60) {
673 'Received an assertion with a session that has expired. Check clock synchronization on IdP and SP.'
674 );
675 }
676 $validAudiences = $assertion->getValidAudiences();
677 if ($validAudiences !== null) {
678 $spEntityId = $spMetadata->getString('entityid');
679 if (!in_array($spEntityId, $validAudiences, true)) {
680 $candidates = '['.implode('], [', $validAudiences).']';
681 throw new SimpleSAML_Error_Exception('This SP ['.$spEntityId.
682 '] is not a valid audience for the assertion. Candidates were: '.$candidates);
683 }
684 }
685
686 $found = false;
687 $lastError = 'No SubjectConfirmation element in Subject.';
688 $validSCMethods = array(\SAML2\Constants::CM_BEARER, \SAML2\Constants::CM_HOK, \SAML2\Constants::CM_VOUCHES);
689 foreach ($assertion->getSubjectConfirmation() as $sc) {
690 if (!in_array($sc->Method, $validSCMethods, true)) {
691 $lastError = 'Invalid Method on SubjectConfirmation: '.var_export($sc->Method, true);
692 continue;
693 }
694
695 // is SSO with HoK enabled? IdP remote metadata overwrites SP metadata configuration
696 $hok = $idpMetadata->getBoolean('saml20.hok.assertion', null);
697 if ($hok === null) {
698 $hok = $spMetadata->getBoolean('saml20.hok.assertion', false);
699 }
700 if ($sc->Method === \SAML2\Constants::CM_BEARER && $hok) {
701 $lastError = 'Bearer SubjectConfirmation received, but Holder-of-Key SubjectConfirmation needed';
702 continue;
703 }
704 if ($sc->Method === \SAML2\Constants::CM_HOK && !$hok) {
705 $lastError = 'Holder-of-Key SubjectConfirmation received, '.
706 'but the Holder-of-Key profile is not enabled.';
707 continue;
708 }
709
710 $scd = $sc->SubjectConfirmationData;
711 if ($sc->Method === \SAML2\Constants::CM_HOK) {
712 // check HoK Assertion
713 if (\SimpleSAML\Utils\HTTP::isHTTPS() === false) {
714 $lastError = 'No HTTPS connection, but required for Holder-of-Key SSO';
715 continue;
716 }
717 if (isset($_SERVER['SSL_CLIENT_CERT']) && empty($_SERVER['SSL_CLIENT_CERT'])) {
718 $lastError = 'No client certificate provided during TLS Handshake with SP';
719 continue;
720 }
721 // extract certificate data (if this is a certificate)
722 $clientCert = $_SERVER['SSL_CLIENT_CERT'];
723 $pattern = '/^-----BEGIN CERTIFICATE-----([^-]*)^-----END CERTIFICATE-----/m';
724 if (!preg_match($pattern, $clientCert, $matches)) {
725 $lastError = 'Error while looking for client certificate during TLS handshake with SP, the client '.
726 'certificate does not have the expected structure';
727 continue;
728 }
729 // we have a valid client certificate from the browser
730 $clientCert = str_replace(array("\r", "\n", " "), '', $matches[1]);
731
732 $keyInfo = array();
733 foreach ($scd->info as $thing) {
734 if ($thing instanceof \SAML2\XML\ds\KeyInfo) {
735 $keyInfo[] = $thing;
736 }
737 }
738 if (count($keyInfo) != 1) {
739 $lastError = 'Error validating Holder-of-Key assertion: Only one <ds:KeyInfo> element in '.
740 '<SubjectConfirmationData> allowed';
741 continue;
742 }
743
744 $x509data = array();
745 foreach ($keyInfo[0]->info as $thing) {
746 if ($thing instanceof \SAML2\XML\ds\X509Data) {
747 $x509data[] = $thing;
748 }
749 }
750 if (count($x509data) != 1) {
751 $lastError = 'Error validating Holder-of-Key assertion: Only one <ds:X509Data> element in '.
752 '<ds:KeyInfo> within <SubjectConfirmationData> allowed';
753 continue;
754 }
755
756 $x509cert = array();
757 foreach ($x509data[0]->data as $thing) {
758 if ($thing instanceof \SAML2\XML\ds\X509Certificate) {
759 $x509cert[] = $thing;
760 }
761 }
762 if (count($x509cert) != 1) {
763 $lastError = 'Error validating Holder-of-Key assertion: Only one <ds:X509Certificate> element in '.
764 '<ds:X509Data> within <SubjectConfirmationData> allowed';
765 continue;
766 }
767
768 $HoKCertificate = $x509cert[0]->certificate;
769 if ($HoKCertificate !== $clientCert) {
770 $lastError = 'Provided client certificate does not match the certificate bound to the '.
771 'Holder-of-Key assertion';
772 continue;
773 }
774 }
775
776 // if no SubjectConfirmationData then don't do anything.
777 if ($scd === null) {
778 $lastError = 'No SubjectConfirmationData provided';
779 continue;
780 }
781
782 if ($scd->NotBefore && $scd->NotBefore > time() + 60) {
783 $lastError = 'NotBefore in SubjectConfirmationData is in the future: '.$scd->NotBefore;
784 continue;
785 }
786 if ($scd->NotOnOrAfter && $scd->NotOnOrAfter <= time() - 60) {
787 $lastError = 'NotOnOrAfter in SubjectConfirmationData is in the past: '.$scd->NotOnOrAfter;
788 continue;
789 }
790 if ($scd->Recipient !== null && $scd->Recipient !== $currentURL) {
791 $lastError = 'Recipient in SubjectConfirmationData does not match the current URL. Recipient is '.
792 var_export($scd->Recipient, true).', current URL is '.var_export($currentURL, true).'.';
793 continue;
794 }
795 if ($scd->InResponseTo !== null && $response->getInResponseTo() !== null &&
796 $scd->InResponseTo !== $response->getInResponseTo()
797 ) {
798 $lastError = 'InResponseTo in SubjectConfirmationData does not match the Response. Response has '.
799 var_export($response->getInResponseTo(), true).
800 ', SubjectConfirmationData has '.var_export($scd->InResponseTo, true).'.';
801 continue;
802 }
803 $found = true;
804 break;
805 }
806 if (!$found) {
807 throw new SimpleSAML_Error_Exception('Error validating SubjectConfirmation in Assertion: '.$lastError);
808 } // as far as we can tell, the assertion is valid
809
810 // maybe we need to base64 decode the attributes in the assertion?
811 if ($idpMetadata->getBoolean('base64attributes', false)) {
812 $attributes = $assertion->getAttributes();
813 $newAttributes = array();
814 foreach ($attributes as $name => $values) {
815 $newAttributes[$name] = array();
816 foreach ($values as $value) {
817 foreach (explode('_', $value) as $v) {
818 $newAttributes[$name][] = base64_decode($v);
819 }
820 }
821 }
822 $assertion->setAttributes($newAttributes);
823 }
824
825 // decrypt the NameID element if it is encrypted
826 if ($assertion->isNameIdEncrypted()) {
827 try {
829 } catch (Exception $e) {
830 throw new SimpleSAML_Error_Exception('Error decrypting NameID: '.$e->getMessage());
831 }
832
834
835 $lastException = null;
836 foreach ($keys as $i => $key) {
837 try {
838 $assertion->decryptNameId($key, $blacklist);
839 SimpleSAML\Logger::debug('Decryption with key #'.$i.' succeeded.');
840 $lastException = null;
841 break;
842 } catch (Exception $e) {
843 SimpleSAML\Logger::debug('Decryption with key #'.$i.' failed with exception: '.$e->getMessage());
844 $lastException = $e;
845 }
846 }
847 if ($lastException !== null) {
848 throw $lastException;
849 }
850 }
851
852 return $assertion;
853 }
854
855
866 {
867
868 $sharedKey = $metadata->getString('sharedkey', null);
869 if ($sharedKey !== null) {
870 $key = new XMLSecurityKey(XMLSecurityKey::AES128_CBC);
871 $key->loadKey($sharedKey);
872 return $key;
873 }
874
875 $keys = $metadata->getPublicKeys('encryption', true);
876 foreach ($keys as $key) {
877 switch ($key['type']) {
878 case 'X509Certificate':
879 $pemKey = "-----BEGIN CERTIFICATE-----\n".
880 chunk_split($key['X509Certificate'], 64).
881 "-----END CERTIFICATE-----\n";
882 $key = new XMLSecurityKey(XMLSecurityKey::RSA_OAEP_MGF1P, array('type' => 'public'));
883 $key->loadKey($pemKey);
884 return $key;
885 }
886 }
887
888 throw new SimpleSAML_Error_Exception('No supported encryption key in '.
889 var_export($metadata->getString('entityid'), true));
890 }
891}
$metadata['__DYNAMIC:1__']
$spEntityId
An exception for terminatinating execution or to throw for unit testing.
static debug($string)
Definition: Logger.php:211
static notice($string)
Definition: Logger.php:188
static loadPublicKey(\SimpleSAML_Configuration $metadata, $required=false, $prefix='')
Get public key or certificate from metadata.
Definition: Crypto.php:265
static loadPrivateKey(\SimpleSAML_Configuration $metadata, $required=false, $prefix='', $full_path=false)
Load a private key from metadata.
Definition: Crypto.php:195
static getSelfURLNoQuery()
Retrieve the current URL using the base URL in the configuration, without the query parameters.
Definition: HTTP.php:843
getString($name, $default=self::REQUIRED_OPTION)
This function retrieves a string configuration option.
getBoolean($name, $default=self::REQUIRED_OPTION)
This function retrieves a boolean configuration option.
static loadFromArray($config, $location='[ARRAY]', $instance=null)
Loads a configuration from the given array.
getArrayizeString($name, $default=self::REQUIRED_OPTION)
This function retrieves a configuration option with a string or an array of strings.
getArray($name, $default=self::REQUIRED_OPTION)
This function retrieves an array configuration option.
getPublicKeys($use=null, $required=false, $prefix='')
Get public key from metadata.
hasValue($name)
Check whether a key in the configuration exists or not.
static getResponseError(\SAML2\StatusResponse $response)
Retrieve the status code of a response as a sspmod_saml_Error.
Definition: Message.php:452
static addSign(SimpleSAML_Configuration $srcMetadata, SimpleSAML_Configuration $dstMetadata, \SAML2\SignedElement $element)
Add signature key and sender certificate to an element (Message or Assertion).
Definition: Message.php:20
static buildLogoutRequest(SimpleSAML_Configuration $srcMetadata, SimpleSAML_Configuration $dstMetadata)
Build a logout request based on information in the metadata.
Definition: Message.php:536
static buildAuthnRequest(SimpleSAML_Configuration $spMetadata, SimpleSAML_Configuration $idpMetadata)
Build an authentication request based on information in the metadata.
Definition: Message.php:466
static checkSign(SimpleSAML_Configuration $srcMetadata, \SAML2\SignedElement $element)
Check the signature on a SAML2 message or assertion.
Definition: Message.php:149
static getDecryptionKeys(SimpleSAML_Configuration $srcMetadata, SimpleSAML_Configuration $dstMetadata)
Retrieve the decryption keys from metadata.
Definition: Message.php:281
static decryptAssertion(SimpleSAML_Configuration $srcMetadata, SimpleSAML_Configuration $dstMetadata, $assertion)
Decrypt an assertion.
Definition: Message.php:357
static buildLogoutResponse(SimpleSAML_Configuration $srcMetadata, SimpleSAML_Configuration $dstMetadata)
Build a logout response based on information in the metadata.
Definition: Message.php:556
static processResponse(SimpleSAML_Configuration $spMetadata, SimpleSAML_Configuration $idpMetadata, \SAML2\Response $response)
Process a response message.
Definition: Message.php:583
static getBlacklistedAlgorithms(SimpleSAML_Configuration $srcMetadata, SimpleSAML_Configuration $dstMetadata)
Retrieve blacklisted algorithms.
Definition: Message.php:332
static getEncryptionKey(SimpleSAML_Configuration $metadata)
Retrieve the encryption key for the given entity.
Definition: Message.php:865
static validateMessage(SimpleSAML_Configuration $srcMetadata, SimpleSAML_Configuration $dstMetadata, \SAML2\Message $message)
Check signature on a SAML2 message if enabled.
Definition: Message.php:236
static processAssertion(SimpleSAML_Configuration $spMetadata, SimpleSAML_Configuration $idpMetadata, \SAML2\Response $response, $assertion, $responseSigned)
Process an assertion in a response.
Definition: Message.php:636
static findCertificate(array $certFingerprints, array $certificates)
Find the certificate used to sign a message or assertion.
Definition: Message.php:114
static addRedirectSign(SimpleSAML_Configuration $srcMetadata, SimpleSAML_Configuration $dstMetadata, \SAML2\Message $message)
Add signature key and and senders certificate to message.
Definition: Message.php:69
static decryptAttributes(SimpleSAML_Configuration $srcMetadata, SimpleSAML_Configuration $dstMetadata, \SAML2\Assertion &$assertion)
Decrypt any encrypted attributes in an assertion.
Definition: Message.php:411
$key
Definition: croninfo.php:18
$i
Definition: disco.tpl.php:19
if(array_key_exists('yes', $_REQUEST)) $attributes
Definition: getconsent.php:85
info()
Definition: info.php:2
catch(Exception $e) $message
$lr
$idpMetadata
$spMetadata
$keys
$certificates
Definition: metarefresh.php:39
Attribute-related utility methods.
$ret
Definition: parser.php:6
$response
$algo
Definition: pwgen.php:34
foreach($_POST as $key=> $value) $res
if((!isset($_SERVER['DOCUMENT_ROOT'])) OR(empty($_SERVER['DOCUMENT_ROOT']))) $_SERVER['DOCUMENT_ROOT']
$this data['403_header']
$values