ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
sspmod_saml_Message Class Reference
+ Collaboration diagram for sspmod_saml_Message:

Static Public Member Functions

static addSign (SimpleSAML_Configuration $srcMetadata, SimpleSAML_Configuration $dstMetadata, \SAML2\SignedElement $element)
 Add signature key and sender certificate to an element (Message or Assertion). More...
 
static checkSign (SimpleSAML_Configuration $srcMetadata, \SAML2\SignedElement $element)
 Check the signature on a SAML2 message or assertion. More...
 
static validateMessage (SimpleSAML_Configuration $srcMetadata, SimpleSAML_Configuration $dstMetadata, \SAML2\Message $message)
 Check signature on a SAML2 message if enabled. More...
 
static getDecryptionKeys (SimpleSAML_Configuration $srcMetadata, SimpleSAML_Configuration $dstMetadata)
 Retrieve the decryption keys from metadata. More...
 
static getBlacklistedAlgorithms (SimpleSAML_Configuration $srcMetadata, SimpleSAML_Configuration $dstMetadata)
 Retrieve blacklisted algorithms. More...
 
static getResponseError (\SAML2\StatusResponse $response)
 Retrieve the status code of a response as a sspmod_saml_Error. More...
 
static buildAuthnRequest (SimpleSAML_Configuration $spMetadata, SimpleSAML_Configuration $idpMetadata)
 Build an authentication request based on information in the metadata. More...
 
static buildLogoutRequest (SimpleSAML_Configuration $srcMetadata, SimpleSAML_Configuration $dstMetadata)
 Build a logout request based on information in the metadata. More...
 
static buildLogoutResponse (SimpleSAML_Configuration $srcMetadata, SimpleSAML_Configuration $dstMetadata)
 Build a logout response based on information in the metadata. More...
 
static processResponse (SimpleSAML_Configuration $spMetadata, SimpleSAML_Configuration $idpMetadata, \SAML2\Response $response)
 Process a response message. More...
 
static getEncryptionKey (SimpleSAML_Configuration $metadata)
 Retrieve the encryption key for the given entity. More...
 

Static Private Member Functions

static addRedirectSign (SimpleSAML_Configuration $srcMetadata, SimpleSAML_Configuration $dstMetadata, \SAML2\Message $message)
 Add signature key and and senders certificate to message. More...
 
static findCertificate (array $certFingerprints, array $certificates)
 Find the certificate used to sign a message or assertion. More...
 
static decryptAssertion (SimpleSAML_Configuration $srcMetadata, SimpleSAML_Configuration $dstMetadata, $assertion)
 Decrypt an assertion. More...
 
static processAssertion (SimpleSAML_Configuration $spMetadata, SimpleSAML_Configuration $idpMetadata, \SAML2\Response $response, $assertion, $responseSigned)
 Process an assertion in a response. More...
 

Detailed Description

Definition at line 10 of file Message.php.

Member Function Documentation

◆ addRedirectSign()

static sspmod_saml_Message::addRedirectSign ( SimpleSAML_Configuration  $srcMetadata,
SimpleSAML_Configuration  $dstMetadata,
\SAML2\Message  $message 
)
staticprivate

Add signature key and and senders certificate to message.

Parameters
SimpleSAML_Configuration$srcMetadataThe metadata of the sender.
SimpleSAML_Configuration$dstMetadataThe metadata of the recipient.
\SAML2\Message$messageThe message we should add the data to.

Definition at line 79 of file Message.php.

References SimpleSAML_Configuration\getBoolean().

83  {
84 
85  $signingEnabled = null;
86  if ($message instanceof \SAML2\LogoutRequest || $message instanceof \SAML2\LogoutResponse) {
87  $signingEnabled = $srcMetadata->getBoolean('sign.logout', null);
88  if ($signingEnabled === null) {
89  $signingEnabled = $dstMetadata->getBoolean('sign.logout', null);
90  }
91  } elseif ($message instanceof \SAML2\AuthnRequest) {
92  $signingEnabled = $srcMetadata->getBoolean('sign.authnrequest', null);
93  if ($signingEnabled === null) {
94  $signingEnabled = $dstMetadata->getBoolean('sign.authnrequest', null);
95  }
96  }
97 
98  if ($signingEnabled === null) {
99  $signingEnabled = $dstMetadata->getBoolean('redirect.sign', null);
100  if ($signingEnabled === null) {
101  $signingEnabled = $srcMetadata->getBoolean('redirect.sign', false);
102  }
103  }
104  if (!$signingEnabled) {
105  return;
106  }
107 
108  self::addSign($srcMetadata, $dstMetadata, $message);
109  }
catch(Exception $e) $message
getBoolean($name, $default=self::REQUIRED_OPTION)
This function retrieves a boolean configuration option.
+ Here is the call graph for this function:

◆ addSign()

static sspmod_saml_Message::addSign ( SimpleSAML_Configuration  $srcMetadata,
SimpleSAML_Configuration  $dstMetadata,
\SAML2\SignedElement  $element 
)
static

Add signature key and sender certificate to an element (Message or Assertion).

Parameters
SimpleSAML_Configuration$srcMetadataThe metadata of the sender.
SimpleSAML_Configuration$dstMetadataThe metadata of the recipient.
\SAML2\SignedElement$elementThe element we should add the data to.

Definition at line 20 of file Message.php.

References $algo, array, SimpleSAML_Configuration\getString(), SimpleSAML\Utils\Crypto\loadPrivateKey(), and SimpleSAML\Utils\Crypto\loadPublicKey().

Referenced by sspmod_saml_IdP_SAML2\buildAssertion(), sspmod_saml_IdP_SAML2\buildResponse(), and SAML2\HTTPArtifact\send().

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  /*
38  * In the NIST Special Publication 800-131A, SHA-1 became deprecated for generating
39  * new digital signatures in 2011, and will be explicitly disallowed starting the 1st
40  * of January, 2014. We'll keep this as a default for the next release and mark it
41  * as deprecated, as part of the transition to SHA-256.
42  *
43  * See http://csrc.nist.gov/publications/nistpubs/800-131A/sp800-131A.pdf for more info.
44  *
45  * TODO: change default to XMLSecurityKey::RSA_SHA256.
46  */
47  $algo = $srcMetadata->getString('signature.algorithm', XMLSecurityKey::RSA_SHA1);
48  }
49 
50  $privateKey = new XMLSecurityKey($algo, array('type' => 'private'));
51  if (array_key_exists('password', $keyArray)) {
52  $privateKey->passphrase = $keyArray['password'];
53  }
54  $privateKey->loadKey($keyArray['PEM'], false);
55 
56  $element->setSignatureKey($privateKey);
57 
58  if ($certArray === null) {
59  // we don't have a certificate to add
60  return;
61  }
62 
63  if (!array_key_exists('PEM', $certArray)) {
64  // we have a public key with only a fingerprint
65  return;
66  }
67 
68  $element->setCertificates(array($certArray['PEM']));
69  }
$algo
Definition: pwgen.php:34
static loadPrivateKey(\SimpleSAML_Configuration $metadata, $required=false, $prefix='', $full_path=false)
Load a private key from metadata.
Definition: Crypto.php:195
static loadPublicKey(\SimpleSAML_Configuration $metadata, $required=false, $prefix='')
Get public key or certificate from metadata.
Definition: Crypto.php:265
Create styles array
The data for the language used.
getString($name, $default=self::REQUIRED_OPTION)
This function retrieves a string configuration option.
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ buildAuthnRequest()

static sspmod_saml_Message::buildAuthnRequest ( SimpleSAML_Configuration  $spMetadata,
SimpleSAML_Configuration  $idpMetadata 
)
static

Build an authentication request based on information in the metadata.

Parameters
SimpleSAML_Configuration$spMetadataThe metadata of the service provider.
SimpleSAML_Configuration$idpMetadataThe metadata of the identity provider.
Returns
An authentication request object.

Definition at line 431 of file Message.php.

References array, SimpleSAML_Configuration\getArrayizeString(), SimpleSAML_Configuration\getBoolean(), SimpleSAML_Configuration\getInteger(), SimpleSAML_Configuration\getString(), SimpleSAML_Configuration\getValue(), SimpleSAML_Configuration\getValueValidate(), SimpleSAML_Configuration\hasValue(), and SimpleSAML_Configuration\loadFromArray().

Referenced by sspmod_saml_Auth_Source_SP\startSSO2().

434  {
435  $ar = new \SAML2\AuthnRequest();
436 
437  // get the NameIDPolicy to apply. IdP metadata has precedence.
438  $nameIdPolicy = array();
439  if ($idpMetadata->hasValue('NameIDPolicy')) {
440  $nameIdPolicy = $idpMetadata->getValue('NameIDPolicy');
441  } elseif ($spMetadata->hasValue('NameIDPolicy')) {
442  $nameIdPolicy = $spMetadata->getValue('NameIDPolicy');
443  }
444 
445  if (!is_array($nameIdPolicy)) {
446  // handle old configurations where 'NameIDPolicy' was used to specify just the format
447  $nameIdPolicy = array('Format' => $nameIdPolicy);
448  }
449 
450  $nameIdPolicy_cf = SimpleSAML_Configuration::loadFromArray($nameIdPolicy);
451  $policy = array(
452  'Format' => $nameIdPolicy_cf->getString('Format', \SAML2\Constants::NAMEID_TRANSIENT),
453  'AllowCreate' => $nameIdPolicy_cf->getBoolean('AllowCreate', true),
454  );
455  $spNameQualifier = $nameIdPolicy_cf->getString('SPNameQualifier', false);
456  if ($spNameQualifier !== false) {
457  $policy['SPNameQualifier'] = $spNameQualifier;
458  }
459  $ar->setNameIdPolicy($policy);
460 
461  $ar->setForceAuthn($spMetadata->getBoolean('ForceAuthn', false));
462  $ar->setIsPassive($spMetadata->getBoolean('IsPassive', false));
463 
464  $protbind = $spMetadata->getValueValidate('ProtocolBinding', array(
465  \SAML2\Constants::BINDING_HTTP_POST,
466  \SAML2\Constants::BINDING_HOK_SSO,
467  \SAML2\Constants::BINDING_HTTP_ARTIFACT,
468  \SAML2\Constants::BINDING_HTTP_REDIRECT,
469  ), \SAML2\Constants::BINDING_HTTP_POST);
470 
471  // Shoaib: setting the appropriate binding based on parameter in sp-metadata defaults to HTTP_POST
472  $ar->setProtocolBinding($protbind);
473  $ar->setIssuer($spMetadata->getString('entityid'));
474  $ar->setAssertionConsumerServiceIndex($spMetadata->getInteger('AssertionConsumerServiceIndex', null));
475  $ar->setAttributeConsumingServiceIndex($spMetadata->getInteger('AttributeConsumingServiceIndex', null));
476 
477  if ($spMetadata->hasValue('AuthnContextClassRef')) {
478  $accr = $spMetadata->getArrayizeString('AuthnContextClassRef');
479  $comp = $spMetadata->getValueValidate('AuthnContextComparison', array(
480  \SAML2\Constants::COMPARISON_EXACT,
481  \SAML2\Constants::COMPARISON_MINIMUM,
482  \SAML2\Constants::COMPARISON_MAXIMUM,
483  \SAML2\Constants::COMPARISON_BETTER,
484  ), \SAML2\Constants::COMPARISON_EXACT);
485  $ar->setRequestedAuthnContext(array('AuthnContextClassRef' => $accr, 'Comparison' => $comp));
486  }
487 
488  self::addRedirectSign($spMetadata, $idpMetadata, $ar);
489 
490  return $ar;
491  }
hasValue($name)
Check whether a key in the configuration exists or not.
getValue($name, $default=null)
Retrieve a configuration option set in config.php.
getValueValidate($name, $allowedValues, $default=self::REQUIRED_OPTION)
Retrieve a configuration option with one of the given values.
getBoolean($name, $default=self::REQUIRED_OPTION)
This function retrieves a boolean configuration option.
getArrayizeString($name, $default=self::REQUIRED_OPTION)
This function retrieves a configuration option with a string or an array of strings.
Create styles array
The data for the language used.
getInteger($name, $default=self::REQUIRED_OPTION)
This function retrieves an integer configuration option.
getString($name, $default=self::REQUIRED_OPTION)
This function retrieves a string configuration option.
static loadFromArray($config, $location='[ARRAY]', $instance=null)
Loads a configuration from the given array.
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ buildLogoutRequest()

static sspmod_saml_Message::buildLogoutRequest ( SimpleSAML_Configuration  $srcMetadata,
SimpleSAML_Configuration  $dstMetadata 
)
static

Build a logout request based on information in the metadata.

Parameters
SimpleSAML_Configuration$srcMetadataThe metadata of the sender.
SimpleSAML_Configuration$dstMetadataThe metadata of the recipient.
Returns
A logout request object.

Definition at line 501 of file Message.php.

References $lr, and SimpleSAML_Configuration\getString().

Referenced by sspmod_saml_IdP_SAML2\buildLogoutRequest(), and sspmod_saml_Auth_Source_SP\startSLO2().

504  {
505  $lr = new \SAML2\LogoutRequest();
506  $lr->setIssuer($srcMetadata->getString('entityid'));
507 
508  self::addRedirectSign($srcMetadata, $dstMetadata, $lr);
509 
510  return $lr;
511  }
$lr
getString($name, $default=self::REQUIRED_OPTION)
This function retrieves a string configuration option.
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ buildLogoutResponse()

static sspmod_saml_Message::buildLogoutResponse ( SimpleSAML_Configuration  $srcMetadata,
SimpleSAML_Configuration  $dstMetadata 
)
static

Build a logout response based on information in the metadata.

Parameters
SimpleSAML_Configuration$srcMetadataThe metadata of the sender.
SimpleSAML_Configuration$dstMetadataThe metadata of the recipient.
Returns
A logout response object.

Definition at line 521 of file Message.php.

References $lr, and SimpleSAML_Configuration\getString().

Referenced by sspmod_saml_IdP_SAML2\sendLogoutResponse().

524  {
525  $lr = new \SAML2\LogoutResponse();
526  $lr->setIssuer($srcMetadata->getString('entityid'));
527 
528  self::addRedirectSign($srcMetadata, $dstMetadata, $lr);
529 
530  return $lr;
531  }
$lr
getString($name, $default=self::REQUIRED_OPTION)
This function retrieves a string configuration option.
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ checkSign()

static sspmod_saml_Message::checkSign ( SimpleSAML_Configuration  $srcMetadata,
\SAML2\SignedElement  $element 
)
static

Check the signature on a SAML2 message or assertion.

Parameters
SimpleSAML_Configuration$srcMetadataThe metadata of the sender.
\SAML2\SignedElement$elementEither a or a .
Returns
boolean True if the signature is correct, false otherwise.
Exceptions

Definition at line 159 of file Message.php.

References $certificates, $i, $key, $keys, $res, array, SimpleSAML\Logger\debug(), SimpleSAML_Configuration\getArrayizeString(), SimpleSAML_Configuration\getPublicKeys(), SimpleSAML_Configuration\getString(), SimpleSAML_Configuration\hasValue(), and SimpleSAML\Logger\notice().

160  {
161  // find the public key that should verify signatures by this entity
162  $keys = $srcMetadata->getPublicKeys('signing');
163  if ($keys !== null) {
164  $pemKeys = array();
165  foreach ($keys as $key) {
166  switch ($key['type']) {
167  case 'X509Certificate':
168  $pemKeys[] = "-----BEGIN CERTIFICATE-----\n".
169  chunk_split($key['X509Certificate'], 64).
170  "-----END CERTIFICATE-----\n";
171  break;
172  default:
173  SimpleSAML\Logger::debug('Skipping unknown key type: '.$key['type']);
174  }
175  }
176  } elseif ($srcMetadata->hasValue('certFingerprint')) {
178  "Validating certificates by fingerprint is deprecated. Please use ".
179  "certData or certificate options in your remote metadata configuration."
180  );
181 
182  $certFingerprint = $srcMetadata->getArrayizeString('certFingerprint');
183  foreach ($certFingerprint as &$fp) {
184  $fp = strtolower(str_replace(':', '', $fp));
185  }
186 
187  $certificates = $element->getCertificates();
188 
189  // we don't have the full certificate stored. Try to find it in the message or the assertion instead
190  if (count($certificates) === 0) {
191  /* We need the full certificate in order to match it against the fingerprint. */
192  SimpleSAML\Logger::debug('No certificate in message when validating against fingerprint.');
193  return false;
194  } else {
195  SimpleSAML\Logger::debug('Found '.count($certificates).' certificates in '.get_class($element));
196  }
197 
198  $pemCert = self::findCertificate($certFingerprint, $certificates);
199  $pemKeys = array($pemCert);
200  } else {
201  throw new SimpleSAML_Error_Exception(
202  'Missing certificate in metadata for '.
203  var_export($srcMetadata->getString('entityid'), true)
204  );
205  }
206 
207  SimpleSAML\Logger::debug('Has '.count($pemKeys).' candidate keys for validation.');
208 
209  $lastException = null;
210  foreach ($pemKeys as $i => $pem) {
211  $key = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type' => 'public'));
212  $key->loadKey($pem);
213 
214  try {
215  // make sure that we have a valid signature on either the response or the assertion
216  $res = $element->validate($key);
217  if ($res) {
218  SimpleSAML\Logger::debug('Validation with key #'.$i.' succeeded.');
219  return true;
220  }
221  SimpleSAML\Logger::debug('Validation with key #'.$i.' failed without exception.');
222  } catch (Exception $e) {
223  SimpleSAML\Logger::debug('Validation with key #'.$i.' failed with exception: '.$e->getMessage());
224  $lastException = $e;
225  }
226  }
227 
228  // we were unable to validate the signature with any of our keys
229  if ($lastException !== null) {
230  throw $lastException;
231  } else {
232  return false;
233  }
234  }
static debug($string)
Definition: Logger.php:213
hasValue($name)
Check whether a key in the configuration exists or not.
$certificates
Definition: metarefresh.php:39
$keys
static notice($string)
Definition: Logger.php:190
foreach($_POST as $key=> $value) $res
getArrayizeString($name, $default=self::REQUIRED_OPTION)
This function retrieves a configuration option with a string or an array of strings.
Create styles array
The data for the language used.
getPublicKeys($use=null, $required=false, $prefix='')
Get public key from metadata.
getString($name, $default=self::REQUIRED_OPTION)
This function retrieves a string configuration option.
$i
Definition: disco.tpl.php:19
$key
Definition: croninfo.php:18
+ Here is the call graph for this function:

◆ decryptAssertion()

static sspmod_saml_Message::decryptAssertion ( SimpleSAML_Configuration  $srcMetadata,
SimpleSAML_Configuration  $dstMetadata,
  $assertion 
)
staticprivate

Decrypt an assertion.

Parameters
SimpleSAML_Configuration$srcMetadataThe metadata of the sender (IdP).
SimpleSAML_Configuration$dstMetadataThe metadata of the recipient (SP).
\SAML2\Assertion | \SAML2\EncryptedAssertion$assertionThe assertion we are decrypting.
Returns
The assertion.
Exceptions

Definition at line 367 of file Message.php.

References $i, $key, $keys, $ret, SimpleSAML\Logger\debug(), and SimpleSAML_Configuration\getBoolean().

371  {
372  assert('$assertion instanceof \SAML2\Assertion || $assertion instanceof \SAML2\EncryptedAssertion');
373 
374  if ($assertion instanceof \SAML2\Assertion) {
375  $encryptAssertion = $srcMetadata->getBoolean('assertion.encryption', null);
376  if ($encryptAssertion === null) {
377  $encryptAssertion = $dstMetadata->getBoolean('assertion.encryption', false);
378  }
379  if ($encryptAssertion) {
380  /* The assertion was unencrypted, but we have encryption enabled. */
381  throw new Exception('Received unencrypted assertion, but encryption was enabled.');
382  }
383 
384  return $assertion;
385  }
386 
387  try {
388  $keys = self::getDecryptionKeys($srcMetadata, $dstMetadata);
389  } catch (Exception $e) {
390  throw new SimpleSAML_Error_Exception('Error decrypting assertion: '.$e->getMessage());
391  }
392 
393  $blacklist = self::getBlacklistedAlgorithms($srcMetadata, $dstMetadata);
394 
395  $lastException = null;
396  foreach ($keys as $i => $key) {
397  try {
398  $ret = $assertion->getAssertion($key, $blacklist);
399  SimpleSAML\Logger::debug('Decryption with key #'.$i.' succeeded.');
400  return $ret;
401  } catch (Exception $e) {
402  SimpleSAML\Logger::debug('Decryption with key #'.$i.' failed with exception: '.$e->getMessage());
403  $lastException = $e;
404  }
405  }
406  throw $lastException;
407  }
static debug($string)
Definition: Logger.php:213
$keys
getBoolean($name, $default=self::REQUIRED_OPTION)
This function retrieves a boolean configuration option.
$ret
Definition: parser.php:6
$i
Definition: disco.tpl.php:19
$key
Definition: croninfo.php:18
+ Here is the call graph for this function:

◆ findCertificate()

static sspmod_saml_Message::findCertificate ( array  $certFingerprints,
array  $certificates 
)
staticprivate

Find the certificate used to sign a message or assertion.

An exception is thrown if we are unable to locate the certificate.

Parameters
array$certFingerprintsThe fingerprints we are looking for.
array$certificatesArray of certificates.
Returns
string Certificate, in PEM-format.
Exceptions
SimpleSAML_Error_Exceptionif we cannot find the certificate matching the fingerprint.

Definition at line 124 of file Message.php.

References array.

125  {
126  $candidates = array();
127 
128  foreach ($certificates as $cert) {
129  $fp = strtolower(sha1(base64_decode($cert)));
130  if (!in_array($fp, $certFingerprints, true)) {
131  $candidates[] = $fp;
132  continue;
133  }
134 
135  /* We have found a matching fingerprint. */
136  $pem = "-----BEGIN CERTIFICATE-----\n".
137  chunk_split($cert, 64).
138  "-----END CERTIFICATE-----\n";
139  return $pem;
140  }
141 
142  $candidates = "'".implode("', '", $candidates)."'";
143  $fps = "'".implode("', '", $certFingerprints)."'";
144  throw new SimpleSAML_Error_Exception('Unable to find a certificate matching the configured '.
145  'fingerprint. Candidates: '.$candidates.'; certFingerprint: '.$fps.'.');
146  }
$certificates
Definition: metarefresh.php:39
Create styles array
The data for the language used.

◆ getBlacklistedAlgorithms()

static sspmod_saml_Message::getBlacklistedAlgorithms ( SimpleSAML_Configuration  $srcMetadata,
SimpleSAML_Configuration  $dstMetadata 
)
static

Retrieve blacklisted algorithms.

Remote configuration overrides local configuration.

Parameters
SimpleSAML_Configuration$srcMetadataThe metadata of the sender.
SimpleSAML_Configuration$dstMetadataThe metadata of the recipient.
Returns
array Array of blacklisted algorithms.

Definition at line 342 of file Message.php.

References array, and SimpleSAML_Configuration\getArray().

345  {
346  $blacklist = $srcMetadata->getArray('encryption.blacklisted-algorithms', null);
347  if ($blacklist === null) {
348  $blacklist = $dstMetadata->getArray('encryption.blacklisted-algorithms', array(XMLSecurityKey::RSA_1_5));
349  }
350  return $blacklist;
351  }
getArray($name, $default=self::REQUIRED_OPTION)
This function retrieves an array configuration option.
Create styles array
The data for the language used.
+ Here is the call graph for this function:

◆ getDecryptionKeys()

static sspmod_saml_Message::getDecryptionKeys ( SimpleSAML_Configuration  $srcMetadata,
SimpleSAML_Configuration  $dstMetadata 
)
static

Retrieve the decryption keys from metadata.

Parameters
SimpleSAML_Configuration$srcMetadataThe metadata of the sender (IdP).
SimpleSAML_Configuration$dstMetadataThe metadata of the recipient (SP).
Returns
array Array of decryption keys.

Definition at line 291 of file Message.php.

References $key, $keys, array, SimpleSAML_Configuration\getString(), and SimpleSAML\Utils\Crypto\loadPrivateKey().

294  {
295  $sharedKey = $srcMetadata->getString('sharedkey', null);
296  if ($sharedKey !== null) {
297  $key = new XMLSecurityKey(XMLSecurityKey::AES128_CBC);
298  $key->loadKey($sharedKey);
299  return array($key);
300  }
301 
302  $keys = array();
303 
304  // load the new private key if it exists
305  $keyArray = SimpleSAML\Utils\Crypto::loadPrivateKey($dstMetadata, false, 'new_');
306  if ($keyArray !== null) {
307  assert('isset($keyArray["PEM"])');
308 
309  $key = new XMLSecurityKey(XMLSecurityKey::RSA_1_5, array('type' => 'private'));
310  if (array_key_exists('password', $keyArray)) {
311  $key->passphrase = $keyArray['password'];
312  }
313  $key->loadKey($keyArray['PEM']);
314  $keys[] = $key;
315  }
316 
317  // find the existing private key
318  $keyArray = SimpleSAML\Utils\Crypto::loadPrivateKey($dstMetadata, true);
319  assert('isset($keyArray["PEM"])');
320 
321  $key = new XMLSecurityKey(XMLSecurityKey::RSA_1_5, array('type' => 'private'));
322  if (array_key_exists('password', $keyArray)) {
323  $key->passphrase = $keyArray['password'];
324  }
325  $key->loadKey($keyArray['PEM']);
326  $keys[] = $key;
327 
328  return $keys;
329  }
$keys
static loadPrivateKey(\SimpleSAML_Configuration $metadata, $required=false, $prefix='', $full_path=false)
Load a private key from metadata.
Definition: Crypto.php:195
Create styles array
The data for the language used.
getString($name, $default=self::REQUIRED_OPTION)
This function retrieves a string configuration option.
$key
Definition: croninfo.php:18
+ Here is the call graph for this function:

◆ getEncryptionKey()

static sspmod_saml_Message::getEncryptionKey ( SimpleSAML_Configuration  $metadata)
static

Retrieve the encryption key for the given entity.

Parameters
SimpleSAML_Configuration$metadataThe metadata of the entity.
Returns
The encryption key.
Exceptions

Definition at line 829 of file Message.php.

References $key, $keys, array, SimpleSAML_Configuration\getPublicKeys(), and SimpleSAML_Configuration\getString().

Referenced by sspmod_saml_IdP_SAML2\buildAssertion(), sspmod_saml_IdP_SAML2\buildLogoutRequest(), and sspmod_saml_Auth_Source_SP\startSLO2().

830  {
831 
832  $sharedKey = $metadata->getString('sharedkey', null);
833  if ($sharedKey !== null) {
834  $key = new XMLSecurityKey(XMLSecurityKey::AES128_CBC);
835  $key->loadKey($sharedKey);
836  return $key;
837  }
838 
839  $keys = $metadata->getPublicKeys('encryption', true);
840  foreach ($keys as $key) {
841  switch ($key['type']) {
842  case 'X509Certificate':
843  $pemKey = "-----BEGIN CERTIFICATE-----\n".
844  chunk_split($key['X509Certificate'], 64).
845  "-----END CERTIFICATE-----\n";
846  $key = new XMLSecurityKey(XMLSecurityKey::RSA_OAEP_MGF1P, array('type' => 'public'));
847  $key->loadKey($pemKey);
848  return $key;
849  }
850  }
851 
852  throw new SimpleSAML_Error_Exception('No supported encryption key in '.
853  var_export($metadata->getString('entityid'), true));
854  }
$keys
Create styles array
The data for the language used.
getPublicKeys($use=null, $required=false, $prefix='')
Get public key from metadata.
getString($name, $default=self::REQUIRED_OPTION)
This function retrieves a string configuration option.
$key
Definition: croninfo.php:18
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ getResponseError()

static sspmod_saml_Message::getResponseError ( \SAML2\StatusResponse  $response)
static

Retrieve the status code of a response as a sspmod_saml_Error.

Parameters
\SAML2\StatusResponse$responseThe response.
Returns
sspmod_saml_Error The error.

Definition at line 417 of file Message.php.

Referenced by sspmod_saml_IdP_SAML2\receiveLogoutMessage().

418  {
419  $status = $response->getStatus();
420  return new sspmod_saml_Error($status['Code'], $status['SubCode'], $status['Message']);
421  }
$response
+ Here is the caller graph for this function:

◆ processAssertion()

static sspmod_saml_Message::processAssertion ( SimpleSAML_Configuration  $spMetadata,
SimpleSAML_Configuration  $idpMetadata,
\SAML2\Response  $response,
  $assertion,
  $responseSigned 
)
staticprivate

Process an assertion in a response.

Parameters
SimpleSAML_Configuration$spMetadataThe metadata of the service provider.
SimpleSAML_Configuration$idpMetadataThe metadata of the identity provider.
\SAML2\Response$responseThe response containing the assertion.
\SAML2\Assertion | \SAML2\EncryptedAssertion$assertionThe assertion.
bool$responseSignedWhether the response is signed.
Returns
The assertion, if it is valid.
Exceptions

Definition at line 601 of file Message.php.

References $_SERVER, $attributes, $i, $key, $keys, $name, $sc, $spEntityId, array, data, SimpleSAML\Logger\debug(), SimpleSAML_Configuration\getBoolean(), SimpleSAML\Utils\HTTP\getSelfURLNoQuery(), SimpleSAML_Configuration\getString(), and time.

607  {
608  assert('$assertion instanceof \SAML2\Assertion || $assertion instanceof \SAML2\EncryptedAssertion');
609  assert('is_bool($responseSigned)');
610 
611  $assertion = self::decryptAssertion($idpMetadata, $spMetadata, $assertion);
612 
613  if (!self::checkSign($idpMetadata, $assertion)) {
614  if (!$responseSigned) {
615  throw new SimpleSAML_Error_Exception('Neither the assertion nor the response was signed.');
616  }
617  } // at least one valid signature found
618 
620 
621  // check various properties of the assertion
622  $notBefore = $assertion->getNotBefore();
623  if ($notBefore !== null && $notBefore > time() + 60) {
624  throw new SimpleSAML_Error_Exception(
625  'Received an assertion that is valid in the future. Check clock synchronization on IdP and SP.'
626  );
627  }
628  $notOnOrAfter = $assertion->getNotOnOrAfter();
629  if ($notOnOrAfter !== null && $notOnOrAfter <= time() - 60) {
630  throw new SimpleSAML_Error_Exception(
631  'Received an assertion that has expired. Check clock synchronization on IdP and SP.'
632  );
633  }
634  $sessionNotOnOrAfter = $assertion->getSessionNotOnOrAfter();
635  if ($sessionNotOnOrAfter !== null && $sessionNotOnOrAfter <= time() - 60) {
636  throw new SimpleSAML_Error_Exception(
637  'Received an assertion with a session that has expired. Check clock synchronization on IdP and SP.'
638  );
639  }
640  $validAudiences = $assertion->getValidAudiences();
641  if ($validAudiences !== null) {
642  $spEntityId = $spMetadata->getString('entityid');
643  if (!in_array($spEntityId, $validAudiences, true)) {
644  $candidates = '['.implode('], [', $validAudiences).']';
645  throw new SimpleSAML_Error_Exception('This SP ['.$spEntityId.
646  '] is not a valid audience for the assertion. Candidates were: '.$candidates);
647  }
648  }
649 
650  $found = false;
651  $lastError = 'No SubjectConfirmation element in Subject.';
652  $validSCMethods = array(\SAML2\Constants::CM_BEARER, \SAML2\Constants::CM_HOK, \SAML2\Constants::CM_VOUCHES);
653  foreach ($assertion->getSubjectConfirmation() as $sc) {
654  if (!in_array($sc->Method, $validSCMethods, true)) {
655  $lastError = 'Invalid Method on SubjectConfirmation: '.var_export($sc->Method, true);
656  continue;
657  }
658 
659  // is SSO with HoK enabled? IdP remote metadata overwrites SP metadata configuration
660  $hok = $idpMetadata->getBoolean('saml20.hok.assertion', null);
661  if ($hok === null) {
662  $hok = $spMetadata->getBoolean('saml20.hok.assertion', false);
663  }
664  if ($sc->Method === \SAML2\Constants::CM_BEARER && $hok) {
665  $lastError = 'Bearer SubjectConfirmation received, but Holder-of-Key SubjectConfirmation needed';
666  continue;
667  }
668  if ($sc->Method === \SAML2\Constants::CM_HOK && !$hok) {
669  $lastError = 'Holder-of-Key SubjectConfirmation received, '.
670  'but the Holder-of-Key profile is not enabled.';
671  continue;
672  }
673 
674  $scd = $sc->SubjectConfirmationData;
675  if ($sc->Method === \SAML2\Constants::CM_HOK) {
676  // check HoK Assertion
677  if (\SimpleSAML\Utils\HTTP::isHTTPS() === false) {
678  $lastError = 'No HTTPS connection, but required for Holder-of-Key SSO';
679  continue;
680  }
681  if (isset($_SERVER['SSL_CLIENT_CERT']) && empty($_SERVER['SSL_CLIENT_CERT'])) {
682  $lastError = 'No client certificate provided during TLS Handshake with SP';
683  continue;
684  }
685  // extract certificate data (if this is a certificate)
686  $clientCert = $_SERVER['SSL_CLIENT_CERT'];
687  $pattern = '/^-----BEGIN CERTIFICATE-----([^-]*)^-----END CERTIFICATE-----/m';
688  if (!preg_match($pattern, $clientCert, $matches)) {
689  $lastError = 'Error while looking for client certificate during TLS handshake with SP, the client '.
690  'certificate does not have the expected structure';
691  continue;
692  }
693  // we have a valid client certificate from the browser
694  $clientCert = str_replace(array("\r", "\n", " "), '', $matches[1]);
695 
696  $keyInfo = array();
697  foreach ($scd->info as $thing) {
698  if ($thing instanceof \SAML2\XML\ds\KeyInfo) {
699  $keyInfo[] = $thing;
700  }
701  }
702  if (count($keyInfo) != 1) {
703  $lastError = 'Error validating Holder-of-Key assertion: Only one <ds:KeyInfo> element in '.
704  '<SubjectConfirmationData> allowed';
705  continue;
706  }
707 
708  $x509data = array();
709  foreach ($keyInfo[0]->info as $thing) {
710  if ($thing instanceof \SAML2\XML\ds\X509Data) {
711  $x509data[] = $thing;
712  }
713  }
714  if (count($x509data) != 1) {
715  $lastError = 'Error validating Holder-of-Key assertion: Only one <ds:X509Data> element in '.
716  '<ds:KeyInfo> within <SubjectConfirmationData> allowed';
717  continue;
718  }
719 
720  $x509cert = array();
721  foreach ($x509data[0]->data as $thing) {
722  if ($thing instanceof \SAML2\XML\ds\X509Certificate) {
723  $x509cert[] = $thing;
724  }
725  }
726  if (count($x509cert) != 1) {
727  $lastError = 'Error validating Holder-of-Key assertion: Only one <ds:X509Certificate> element in '.
728  '<ds:X509Data> within <SubjectConfirmationData> allowed';
729  continue;
730  }
731 
732  $HoKCertificate = $x509cert[0]->certificate;
733  if ($HoKCertificate !== $clientCert) {
734  $lastError = 'Provided client certificate does not match the certificate bound to the '.
735  'Holder-of-Key assertion';
736  continue;
737  }
738  }
739 
740  // if no SubjectConfirmationData then don't do anything.
741  if ($scd === null) {
742  $lastError = 'No SubjectConfirmationData provided';
743  continue;
744  }
745 
746  if ($scd->NotBefore && $scd->NotBefore > time() + 60) {
747  $lastError = 'NotBefore in SubjectConfirmationData is in the future: '.$scd->NotBefore;
748  continue;
749  }
750  if ($scd->NotOnOrAfter && $scd->NotOnOrAfter <= time() - 60) {
751  $lastError = 'NotOnOrAfter in SubjectConfirmationData is in the past: '.$scd->NotOnOrAfter;
752  continue;
753  }
754  if ($scd->Recipient !== null && $scd->Recipient !== $currentURL) {
755  $lastError = 'Recipient in SubjectConfirmationData does not match the current URL. Recipient is '.
756  var_export($scd->Recipient, true).', current URL is '.var_export($currentURL, true).'.';
757  continue;
758  }
759  if ($scd->InResponseTo !== null && $response->getInResponseTo() !== null &&
760  $scd->InResponseTo !== $response->getInResponseTo()
761  ) {
762  $lastError = 'InResponseTo in SubjectConfirmationData does not match the Response. Response has '.
763  var_export($response->getInResponseTo(), true).
764  ', SubjectConfirmationData has '.var_export($scd->InResponseTo, true).'.';
765  continue;
766  }
767  $found = true;
768  break;
769  }
770  if (!$found) {
771  throw new SimpleSAML_Error_Exception('Error validating SubjectConfirmation in Assertion: '.$lastError);
772  } // as far as we can tell, the assertion is valid
773 
774  // maybe we need to base64 decode the attributes in the assertion?
775  if ($idpMetadata->getBoolean('base64attributes', false)) {
776  $attributes = $assertion->getAttributes();
777  $newAttributes = array();
778  foreach ($attributes as $name => $values) {
779  $newAttributes[$name] = array();
780  foreach ($values as $value) {
781  foreach (explode('_', $value) as $v) {
782  $newAttributes[$name][] = base64_decode($v);
783  }
784  }
785  }
786  $assertion->setAttributes($newAttributes);
787  }
788 
789  // decrypt the NameID element if it is encrypted
790  if ($assertion->isNameIdEncrypted()) {
791  try {
792  $keys = self::getDecryptionKeys($idpMetadata, $spMetadata);
793  } catch (Exception $e) {
794  throw new SimpleSAML_Error_Exception('Error decrypting NameID: '.$e->getMessage());
795  }
796 
797  $blacklist = self::getBlacklistedAlgorithms($idpMetadata, $spMetadata);
798 
799  $lastException = null;
800  foreach ($keys as $i => $key) {
801  try {
802  $assertion->decryptNameId($key, $blacklist);
803  SimpleSAML\Logger::debug('Decryption with key #'.$i.' succeeded.');
804  $lastException = null;
805  break;
806  } catch (Exception $e) {
807  SimpleSAML\Logger::debug('Decryption with key #'.$i.' failed with exception: '.$e->getMessage());
808  $lastException = $e;
809  }
810  }
811  if ($lastException !== null) {
812  throw $lastException;
813  }
814  }
815 
816  return $assertion;
817  }
Add some data
if((!isset($_SERVER['DOCUMENT_ROOT'])) OR(empty($_SERVER['DOCUMENT_ROOT']))) $_SERVER['DOCUMENT_ROOT']
static debug($string)
Definition: Logger.php:213
$spEntityId
$attributes
$keys
Attribute-related utility methods.
if($format !==null) $name
Definition: metadata.php:146
static getSelfURLNoQuery()
Retrieve the current URL using the base URL in the configuration, without the query parameters...
Definition: HTTP.php:846
getBoolean($name, $default=self::REQUIRED_OPTION)
This function retrieves a boolean configuration option.
Create styles array
The data for the language used.
getString($name, $default=self::REQUIRED_OPTION)
This function retrieves a string configuration option.
$i
Definition: disco.tpl.php:19
Add data(end) time
Method that wraps PHPs time in order to allow simulations with the workflow.
$response
$key
Definition: croninfo.php:18
+ Here is the call graph for this function:

◆ processResponse()

static sspmod_saml_Message::processResponse ( SimpleSAML_Configuration  $spMetadata,
SimpleSAML_Configuration  $idpMetadata,
\SAML2\Response  $response 
)
static

Process a response message.

If the response is an error response, we will throw a sspmod_saml_Error exception with the error.

Parameters
SimpleSAML_Configuration$spMetadataThe metadata of the service provider.
SimpleSAML_Configuration$idpMetadataThe metadata of the identity provider.
\SAML2\Response$responseThe response.
Returns
array Array with objects, containing valid assertions from the response.
Exceptions

Definition at line 548 of file Message.php.

References $ret, array, and SimpleSAML\Utils\HTTP\getSelfURLNoQuery().

552  {
553  if (!$response->isSuccess()) {
554  throw self::getResponseError($response);
555  }
556 
557  // validate Response-element destination
559  $msgDestination = $response->getDestination();
560  if ($msgDestination !== null && $msgDestination !== $currentURL) {
561  throw new Exception('Destination in response doesn\'t match the current URL. Destination is "'.
562  $msgDestination.'", current URL is "'.$currentURL.'".');
563  }
564 
565  $responseSigned = self::checkSign($idpMetadata, $response);
566 
567  /*
568  * When we get this far, the response itself is valid.
569  * We only need to check signatures and conditions of the response.
570  */
571  $assertion = $response->getAssertions();
572  if (empty($assertion)) {
573  throw new SimpleSAML_Error_Exception('No assertions found in response from IdP.');
574  }
575 
576  $ret = array();
577  foreach ($assertion as $a) {
578  $ret[] = self::processAssertion($spMetadata, $idpMetadata, $response, $a, $responseSigned);
579  }
580 
581  return $ret;
582  }
static getSelfURLNoQuery()
Retrieve the current URL using the base URL in the configuration, without the query parameters...
Definition: HTTP.php:846
Create styles array
The data for the language used.
$ret
Definition: parser.php:6
$response
+ Here is the call graph for this function:

◆ validateMessage()

static sspmod_saml_Message::validateMessage ( SimpleSAML_Configuration  $srcMetadata,
SimpleSAML_Configuration  $dstMetadata,
\SAML2\Message  $message 
)
static

Check signature on a SAML2 message if enabled.

Parameters
SimpleSAML_Configuration$srcMetadataThe metadata of the sender.
SimpleSAML_Configuration$dstMetadataThe metadata of the recipient.
\SAML2\Message$messageThe message we should check the signature on.
Exceptions

Definition at line 246 of file Message.php.

References SimpleSAML_Configuration\getBoolean().

Referenced by sspmod_saml_IdP_SAML2\receiveAuthnRequest(), and sspmod_saml_IdP_SAML2\receiveLogoutMessage().

250  {
251  $enabled = null;
252  if ($message instanceof \SAML2\LogoutRequest || $message instanceof \SAML2\LogoutResponse) {
253  $enabled = $srcMetadata->getBoolean('validate.logout', null);
254  if ($enabled === null) {
255  $enabled = $dstMetadata->getBoolean('validate.logout', null);
256  }
257  } elseif ($message instanceof \SAML2\AuthnRequest) {
258  $enabled = $srcMetadata->getBoolean('validate.authnrequest', null);
259  if ($enabled === null) {
260  $enabled = $dstMetadata->getBoolean('validate.authnrequest', null);
261  }
262  }
263 
264  if ($enabled === null) {
265  $enabled = $srcMetadata->getBoolean('redirect.validate', null);
266  if ($enabled === null) {
267  $enabled = $dstMetadata->getBoolean('redirect.validate', false);
268  }
269  }
270 
271  if (!$enabled) {
272  return;
273  }
274 
275  if (!self::checkSign($srcMetadata, $message)) {
276  throw new SimpleSAML_Error_Exception(
277  'Validation of received messages enabled, but no signature found on message.'
278  );
279  }
280  }
catch(Exception $e) $message
getBoolean($name, $default=self::REQUIRED_OPTION)
This function retrieves a boolean configuration option.
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

The documentation for this class was generated from the following file: