Issues (377)

Security Analysis    22 potential vulnerabilities

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Manipulation (2)
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection (1)
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  File Exposure (5)
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Code Injection (13)
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Cross-Site Scripting (1)
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  Header Injection
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/SimpleSAML/Metadata/SAMLBuilder.php (1 issue)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\Metadata;
6
7
use DOMElement;
8
use SimpleSAML\{Configuration, Module, Logger, Utils};
9
use SimpleSAML\Assert\{Assert, AssertionFailedException};
10
use SimpleSAML\Module\adfs\SAML2\XML\fed\SecurityTokenServiceType;
11
use SimpleSAML\SAML2\Constants as C;
12
use SimpleSAML\SAML2\Exception\ArrayValidationException;
13
use SimpleSAML\SAML2\XML\md\{AbstractIndexedEndpointType, ContactPerson, Extensions, KeyDescriptor, NameIDFormat};
14
use SimpleSAML\SAML2\XML\md\{ArtifactResolutionService, AssertionConsumerService, AssertionIDRequestService};
15
use SimpleSAML\SAML2\XML\md\{AttributeConsumingService, AttributeService, SingleLogoutService, SingleSignOnService};
16
use SimpleSAML\SAML2\XML\md\{AttributeAuthorityDescriptor, EntityDescriptor, IDPSSODescriptor, SPSSODescriptor};
17
use SimpleSAML\SAML2\XML\md\{Organization, RequestedAttribute, RoleDescriptor, ServiceDescription, ServiceName};
18
use SimpleSAML\SAML2\XML\mdattr\EntityAttributes;
19
use SimpleSAML\SAML2\XML\mdrpi\RegistrationInfo;
20
use SimpleSAML\SAML2\XML\mdui\{DiscoHints, UIInfo};
21
use SimpleSAML\SAML2\XML\saml\{Attribute, AttributeValue};
22
use SimpleSAML\SAML2\XML\shibmd\Scope;
23
use SimpleSAML\XML\Chunk;
24
use SimpleSAML\XMLSecurity\XML\ds\{KeyInfo, KeyName, X509Certificate, X509Data};
25
26
use function array_key_exists;
27
use function array_keys;
28
use function array_map;
29
use function array_merge;
30
use function class_parents;
31
use function count;
32
use function in_array;
33
use function is_int;
34
use function preg_match;
35
use function time;
36
37
/**
38
 * Class for generating SAML 2.0 metadata from SimpleSAMLphp metadata arrays.
39
 *
40
 * This class builds SAML 2.0 metadata for an entity by examining the metadata for the entity.
41
 *
42
 * @package SimpleSAMLphp
43
 */
44
45
class SAMLBuilder
46
{
47
    /**
48
     * The EntityDescriptor we are building.
49
     *
50
     * @var \SimpleSAML\SAML2\XML\md\EntityDescriptor
51
     */
52
    private EntityDescriptor $entityDescriptor;
53
54
55
    /**
56
     * Initialize the SAML builder.
57
     *
58
     * @param string   $entityId The entity id of the entity.
59
     * @param int|null $maxCache The maximum time in seconds the metadata should be cached. Defaults to null
60
     * @param int|null $maxDuration The maximum time in seconds this metadata should be considered valid. Defaults
61
     * to null.
62
     */
63
    public function __construct(
64
        string $entityId,
65
        private ?int $maxCache = null,
66
        private ?int $maxDuration = null,
67
    ) {
68
        $this->entityDescriptor = new EntityDescriptor();
69
        $this->entityDescriptor->setEntityID($entityId);
70
    }
71
72
73
    /**
74
     * @param array $metadata
75
     */
76
    private function setExpiration(array $metadata): void
77
    {
78
        if (array_key_exists('expire', $metadata)) {
79
            if ($metadata['expire'] - time() < $this->maxDuration) {
80
                $this->maxDuration = $metadata['expire'] - time();
81
            }
82
        }
83
84
        if ($this->maxCache !== null) {
85
            $this->entityDescriptor->setCacheDuration('PT' . $this->maxCache . 'S');
86
        }
87
        if ($this->maxDuration !== null) {
88
            $this->entityDescriptor->setValidUntil(time() + $this->maxDuration);
89
        }
90
    }
91
92
93
    /**
94
     * Retrieve the EntityDescriptor element which is generated for this entity.
95
     *
96
     * @return \DOMElement The EntityDescriptor element of this entity.
97
     */
98
    public function getEntityDescriptor(): DOMElement
99
    {
100
        $xml = $this->entityDescriptor->toXML();
101
        $xml->ownerDocument->appendChild($xml);
102
103
        return $xml;
104
    }
105
106
107
    /**
108
     * Retrieve the EntityDescriptor as text.
109
     *
110
     * This function serializes this EntityDescriptor, and returns it as text.
111
     *
112
     * @param bool $formatted Whether the returned EntityDescriptor should be formatted first.
113
     *
114
     * @return string The serialized EntityDescriptor.
115
     */
116
    public function getEntityDescriptorText(bool $formatted = true): string
117
    {
118
        $xml = $this->getEntityDescriptor();
119
        if ($formatted) {
120
            $xmlUtils = new Utils\XML();
121
            $xmlUtils->formatDOMElement($xml);
122
        }
123
124
        $xml->ownerDocument->encoding = "utf-8";
125
126
        return $xml->ownerDocument->saveXML();
127
    }
128
129
130
    /**
131
     * Add a SecurityTokenServiceType for ADFS metadata.
132
     *
133
     * @param array $metadata The metadata with the information about the SecurityTokenServiceType.
134
     */
135
    public function addSecurityTokenServiceType(array $metadata): void
136
    {
137
        Assert::notNull($metadata['entityid']);
138
        Assert::notNull($metadata['metadata-set']);
139
140
        $metadata = Configuration::loadFromArray($metadata, $metadata['entityid']);
141
        $defaultEndpoint = $metadata->getDefaultEndpoint('SingleSignOnService');
142
143
        $e = new SecurityTokenServiceType();
144
        $e->setLocation($defaultEndpoint['Location']);
145
146
        $this->addCertificate($e, $metadata);
147
148
        $this->entityDescriptor->addRoleDescriptor($e);
149
    }
150
151
152
    /**
153
     * Add extensions to the metadata.
154
     *
155
     * @param \SimpleSAML\Configuration    $metadata The metadata to get extensions from.
156
     * @param \SimpleSAML\SAML2\XML\md\RoleDescriptor $e Reference to the element where the
157
     *   Extensions element should be included.
158
     */
159
    private function addExtensions(Configuration $metadata, RoleDescriptor $e): void
160
    {
161
        $extensions = [];
162
163
        if ($metadata->hasValue('scope')) {
164
            foreach ($metadata->getArray('scope') as $scopetext) {
165
                $isRegexpScope = (1 === preg_match('/[\$\^\)\(\*\|\\\\]/', $scopetext));
166
                $extensions[] = new Scope($scopetext, $isRegexpScope);
167
            }
168
        }
169
170
        if ($metadata->hasValue('EntityAttributes')) {
171
            $attr = [];
172
            foreach ($metadata->getArray('EntityAttributes') as $attributeName => $attributeValues) {
173
                $attrValues = [];
174
                foreach ($attributeValues as $attributeValue) {
175
                    $attrValues[] = new AttributeValue($attributeValue);
176
                }
177
178
                // Attribute names that is not URI is prefixed as this: '{nameformat}name'
179
                if (preg_match('/^\{(.*?)\}(.*)$/', $attributeName, $matches)) {
180
                    $attr[] = new Attribute(
181
                        name: $matches[2],
182
                        nameFormat: $matches[1] === C::NAMEFORMAT_UNSPECIFIED ? null : $matches[1],
183
                        attributeValue: $attrValues,
184
                    );
185
                } else {
186
                    $attr[] = new Attribute(
187
                        name: $attributeName,
188
                        nameFormat: C::NAMEFORMAT_URI,
189
                        attributeValue: $attrValues,
190
                    );
191
                }
192
            }
193
194
            $extensions[] = new EntityAttributes($attr);
195
        }
196
197
        if ($metadata->hasValue('saml:Extensions')) {
198
            $chunks = $metadata->getArray('saml:Extensions');
199
            Assert::allIsInstanceOf($chunks, Chunk::class);
200
            $extensions = array_merge($extensions, $chunks);
201
        }
202
203
        if ($metadata->hasValue('RegistrationInfo')) {
204
            try {
205
                $extensions[] = RegistrationInfo::fromArray($metadata->getArray('RegistrationInfo'));
206
            } catch (ArrayValidationException $err) {
207
                Logger::error('Metadata: invalid content found in RegistrationInfo: ' . $err->getMessage());
208
            }
209
        }
210
211
        if ($metadata->hasValue('UIInfo')) {
212
            try {
213
                $extensions[] = UIInfo::fromArray($metadata->getArray('UIInfo'));
214
            } catch (ArrayValidationException $err) {
215
                Logger::error('Metadata: invalid content found in UIInfo: ' . $err->getMessage());
216
            }
217
        }
218
219
        if ($metadata->hasValue('DiscoHints')) {
220
            try {
221
                $extensions[] = DiscoHints::fromArray($metadata->getArray('DiscoHints'));
222
            } catch (ArrayValidationException $err) {
223
                Logger::error('Metadata: invalid content found in DiscoHints: ' . $err->getMessage());
224
            }
225
        }
226
227
        $e->setExtensions(new Extensions($extensions));
228
    }
229
230
231
    /**
232
     * Add an Organization element based on metadata array.
233
     *
234
     * @param array $metadata The metadata we should extract the organization information from.
235
     */
236
    public function addOrganizationInfo(array $metadata): void
237
    {
238
        if (
239
            empty($metadata['OrganizationName']) ||
240
            empty($metadata['OrganizationDisplayName']) ||
241
            empty($metadata['OrganizationURL'])
242
        ) {
243
            // empty or incomplete organization information
244
            return;
245
        }
246
247
        $arrayUtils = new Utils\Arrays();
248
        $org = null;
249
250
        try {
251
            $org = Organization::fromArray([
252
                'OrganizationName' => $arrayUtils->arrayize($metadata['OrganizationName'], 'en'),
253
                'OrganizationDisplayName' => $arrayUtils->arrayize($metadata['OrganizationDisplayName'], 'en'),
254
                'OrganizationURL' => $arrayUtils->arrayize($metadata['OrganizationURL'], 'en'),
255
            ]);
256
        } catch (ArrayValidationException $e) {
257
            Logger::error('Federation: invalid content found in contact: ' . $e->getMessage());
258
        }
259
260
        $this->entityDescriptor->setOrganization($org);
261
    }
262
263
264
    /**
265
     * Add a list of endpoints to metadata.
266
     *
267
     * @param array $endpoints The endpoints.
268
     * @param class-string $class The type of endpoint to create
269
     *
270
     * @return array An array of endpoint objects,
271
     *     either \SimpleSAML\SAML2\XML\md\AbstractEndpointType or \SimpleSAML\SAML2\XML\md\AbstractIndexedEndpointType.
272
     */
273
    private static function createEndpoints(array $endpoints, string $class): array
274
    {
275
        $indexed = in_array(AbstractIndexedEndpointType::class, class_parents($class), true);
276
        $ret = [];
277
278
        // Set an index if it wasn't already set
279
        if ($indexed) {
280
            foreach ($endpoints as &$ep) {
281
                if (!isset($ep['index'])) {
282
                    // Find the maximum index
283
                    $maxIndex = -1;
284
                    foreach ($endpoints as $ep) {
0 ignored issues
show
Comprehensibility Bug introduced by
$ep is overwriting a variable from outer foreach loop.
Loading history...
285
                        if (!isset($ep['index'])) {
286
                            continue;
287
                        }
288
289
                        if ($ep['index'] > $maxIndex) {
290
                            $maxIndex = $ep['index'];
291
                        }
292
                    }
293
294
                    $ep['index'] = $maxIndex + 1;
295
                }
296
            }
297
        }
298
299
        foreach ($endpoints as $endpoint) {
300
            $ret[] = $class::fromArray($endpoint);
301
        }
302
303
        return $ret;
304
    }
305
306
307
    /**
308
     * Add an AttributeConsumingService element to the metadata.
309
     *
310
     * @param \SimpleSAML\SAML2\XML\md\SPSSODescriptor $spDesc The SPSSODescriptor element.
311
     * @param \SimpleSAML\Configuration     $metadata The metadata.
312
     */
313
    private function addAttributeConsumingService(
314
        SPSSODescriptor $spDesc,
315
        Configuration $metadata,
316
    ): void {
317
        $attributes = $metadata->getOptionalArray('attributes', []);
318
        $serviceName = $metadata->getOptionalLocalizedString('name', []);
319
320
        if (count($serviceName) === 0 || count($attributes) == 0) {
321
            // we cannot add an AttributeConsumingService without name and attributes
322
            return;
323
        }
324
325
        $attributesrequired = $metadata->getOptionalArray('attributes.required', []);
326
        $nameFormat = $metadata->getOptionalString('attributes.NameFormat', C::NAMEFORMAT_URI);
327
        $serviceDescription = $metadata->getOptionalLocalizedString('description', []);
328
329
        $requestedAttributes = [];
330
        foreach ($attributes as $friendlyName => $attribute) {
331
            $requestedAttributes[] = new RequestedAttribute(
332
                $attribute,
333
                in_array($attribute, $attributesrequired, true) ?: null,
334
                $nameFormat !== C::NAMEFORMAT_UNSPECIFIED ? $nameFormat : null,
335
                !is_int($friendlyName) ? $friendlyName : null,
336
            );
337
        }
338
339
        /**
340
         * Add an AttributeConsumingService element with information as name and description and list
341
         * of requested attributes
342
         */
343
        $attributeconsumer = new AttributeConsumingService(
344
            $metadata->getOptionalInteger('attributes.index', 0),
345
            array_map(
346
                function ($lang, $sName) {
347
                    return new ServiceName($lang, $sName);
348
                },
349
                array_keys($serviceName),
350
                $serviceName,
351
            ),
352
            $requestedAttributes,
353
            $metadata->hasValue('attributes.isDefault')
354
                ? $metadata->getOptionalBoolean('attributes.isDefault', false)
355
                : null,
356
            array_map(
357
                function ($lang, $sDesc) {
358
                    return new ServiceDescription($lang, $sDesc);
359
                },
360
                array_keys($serviceDescription),
361
                $serviceDescription,
362
            ),
363
        );
364
365
        $spDesc->addAttributeConsumingService($attributeconsumer);
366
    }
367
368
369
    /**
370
     * Add a specific type of metadata to an entity.
371
     *
372
     * @param string $set The metadata set this metadata comes from.
373
     * @param array  $metadata The metadata.
374
     */
375
    public function addMetadata(string $set, array $metadata): void
376
    {
377
        $this->setExpiration($metadata);
378
379
        switch ($set) {
380
            case 'saml20-sp-remote':
381
                $this->addMetadataSP20($metadata);
382
                break;
383
            case 'saml20-idp-remote':
384
                $this->addMetadataIdP20($metadata);
385
                break;
386
            case 'attributeauthority-remote':
387
                $this->addAttributeAuthority($metadata);
388
                break;
389
            default:
390
                Logger::warning('Unable to generate metadata for unknown type \'' . $set . '\'.');
391
        }
392
    }
393
394
395
    /**
396
     * Add SAML 2.0 SP metadata.
397
     *
398
     * @param array $metadata The metadata.
399
     * @param string[] $protocols The protocols supported. Defaults to \SimpleSAML\SAML2\Constants::NS_SAMLP.
400
     */
401
    public function addMetadataSP20(array $metadata, array $protocols = [C::NS_SAMLP]): void
402
    {
403
        Assert::notNull($metadata['entityid']);
404
        Assert::notNull($metadata['metadata-set']);
405
406
        $metadata = Configuration::loadFromArray($metadata, $metadata['entityid']);
407
408
        $e = new SPSSODescriptor();
409
        $e->setProtocolSupportEnumeration($protocols);
410
411
        if ($metadata->hasValue('saml20.sign.assertion')) {
412
            $e->setWantAssertionsSigned($metadata->getBoolean('saml20.sign.assertion'));
413
        }
414
415
        if ($metadata->hasValue('redirect.validate')) {
416
            $e->setAuthnRequestsSigned($metadata->getBoolean('redirect.validate'));
417
        } elseif ($metadata->hasValue('validate.authnrequest')) {
418
            $e->setAuthnRequestsSigned($metadata->getBoolean('validate.authnrequest'));
419
        }
420
421
        $this->addExtensions($metadata, $e);
422
423
        $this->addCertificate($e, $metadata);
424
425
        $e->setSingleLogoutService(self::createEndpoints(
426
            $metadata->getEndpoints('SingleLogoutService'),
427
            SingleLogoutService::class,
428
        ));
429
430
        $nids = [];
431
        foreach ($metadata->getOptionalArrayizeString('NameIDFormat', []) as $nid) {
432
            $nids[] = new NameIDFormat($nid);
433
        }
434
        $e->setNameIDFormat($nids);
435
436
        $endpoints = $metadata->getEndpoints('AssertionConsumerService');
437
        foreach ($metadata->getOptionalArrayizeString('AssertionConsumerService.artifact', []) as $acs) {
438
            $endpoints[] = [
439
                'Binding'  => C::BINDING_HTTP_ARTIFACT,
440
                'Location' => $acs,
441
            ];
442
        }
443
        $e->setAssertionConsumerService(self::createEndpoints($endpoints, AssertionConsumerService::class));
444
445
        $this->addAttributeConsumingService($e, $metadata);
446
447
        $this->entityDescriptor->addRoleDescriptor($e);
448
449
        foreach ($metadata->getOptionalArray('contacts', []) as $contact) {
450
            if (array_key_exists('ContactType', $contact) && array_key_exists('EmailAddress', $contact)) {
451
                $this->addContact(ContactPerson::fromArray($contact));
452
            }
453
        }
454
    }
455
456
457
    /**
458
     * Add metadata of a SAML 2.0 identity provider.
459
     *
460
     * @param array $metadata The metadata.
461
     */
462
    public function addMetadataIdP20(array $metadata): void
463
    {
464
        Assert::notNull($metadata['entityid']);
465
        Assert::notNull($metadata['metadata-set']);
466
467
        $metadata = Configuration::loadFromArray($metadata, $metadata['entityid']);
468
469
        $e = new IDPSSODescriptor();
470
        $e->setProtocolSupportEnumeration(array_merge($e->getProtocolSupportEnumeration(), [C::NS_SAMLP]));
471
472
        if ($metadata->hasValue('sign.authnrequest')) {
473
            $e->setWantAuthnRequestsSigned($metadata->getBoolean('sign.authnrequest'));
474
        } elseif ($metadata->hasValue('redirect.sign')) {
475
            $e->setWantAuthnRequestsSigned($metadata->getBoolean('redirect.sign'));
476
        }
477
478
        if ($metadata->hasValue('errorURL')) {
479
            $e->setErrorURL($metadata->getString('errorURL'));
480
        } else {
481
            $e->setErrorURL(Module::getModuleURL(
482
                'core/error/ERRORURL_CODE?ts=ERRORURL_TS&rp=ERRORURL_RP&tid=ERRORURL_TID&ctx=ERRORURL_CTX',
483
            ));
484
        }
485
486
        $this->addExtensions($metadata, $e);
487
488
        $this->addCertificate($e, $metadata);
489
490
        if ($metadata->hasValue('ArtifactResolutionService')) {
491
            $e->setArtifactResolutionService(self::createEndpoints(
492
                $metadata->getEndpoints('ArtifactResolutionService'),
493
                ArtifactResolutionService::class,
494
            ));
495
        }
496
497
        $e->setSingleLogoutService(self::createEndpoints(
498
            $metadata->getEndpoints('SingleLogoutService'),
499
            SingleLogoutService::class,
500
        ));
501
502
        $nids = [];
503
        foreach ($metadata->getOptionalArrayizeString('NameIDFormat', []) as $nid) {
504
            $nids[] = new NameIDFormat($nid);
505
        }
506
        $e->setNameIDFormat($nids);
507
508
        $e->setSingleSignOnService(self::createEndpoints(
509
            $metadata->getEndpoints('SingleSignOnService'),
510
            SingleSignOnService::class,
511
        ));
512
513
        $this->entityDescriptor->addRoleDescriptor($e);
514
515
        foreach ($metadata->getOptionalArray('contacts', []) as $contact) {
516
            if (array_key_exists('ContactType', $contact) && array_key_exists('EmailAddress', $contact)) {
517
                try {
518
                    $this->addContact(ContactPerson::fromArray($contact));
519
                } catch (ArrayValidationException $e) {
520
                    Logger::error('IdP Metadata: invalid content found in contact: ' . $e->getMessage());
521
                    continue;
522
                }
523
            }
524
        }
525
    }
526
527
528
    /**
529
     * Add metadata of a SAML attribute authority.
530
     *
531
     * @param array $metadata The AttributeAuthorityDescriptor, in the format returned by
532
     * \SimpleSAML\Metadata\SAMLParser.
533
     */
534
    public function addAttributeAuthority(array $metadata): void
535
    {
536
        Assert::notNull($metadata['entityid']);
537
        Assert::notNull($metadata['metadata-set']);
538
539
        $metadata = Configuration::loadFromArray($metadata, $metadata['entityid']);
540
541
        $e = new AttributeAuthorityDescriptor();
542
        $e->setProtocolSupportEnumeration($metadata->getOptionalArray('protocols', [C::NS_SAMLP]));
543
544
        $this->addExtensions($metadata, $e);
545
        $this->addCertificate($e, $metadata);
546
547
        $e->setAttributeService(self::createEndpoints(
548
            $metadata->getEndpoints('AttributeService'),
549
            AttributeService::class,
550
        ));
551
        $e->setAssertionIDRequestService(self::createEndpoints(
552
            $metadata->getEndpoints('AssertionIDRequestService'),
553
            AssertionIDRequestService::class,
554
        ));
555
556
        $nids = [];
557
        foreach ($metadata->getOptionalArrayizeString('NameIDFormat', []) as $nid) {
558
            $nids[] = new NameIDFormat($nid);
559
        }
560
        $e->setNameIDFormat($nids);
561
562
        $this->entityDescriptor->addRoleDescriptor($e);
563
    }
564
565
566
    /**
567
     * Add contact information.
568
     *
569
     * @param \SimpleSAML\SAML2\XML\md\ContactPerson $contact The details about the contact.
570
     */
571
    public function addContact(ContactPerson $contact): void
572
    {
573
        $this->entityDescriptor->addContactPerson($contact);
574
    }
575
576
577
    /**
578
     * Add a KeyDescriptor with an X509 certificate.
579
     *
580
     * @param \SimpleSAML\SAML2\XML\md\RoleDescriptor $rd The RoleDescriptor the certificate should be added to.
581
     * @param string                      $use The value of the 'use' attribute.
582
     * @param string                      $x509cert The certificate data.
583
     * @param string|null                 $keyName The name of the key. Should be valid for usage in an ID attribute,
584
     *                                             e.g. not start with a digit.
585
     */
586
    private function addX509KeyDescriptor(
587
        RoleDescriptor $rd,
588
        string $use,
589
        string $x509cert,
590
        ?string $keyName = null,
591
    ): void {
592
        Assert::oneOf($use, ['encryption', 'signing']);
593
        $info = [
594
            new X509Data([
595
                new X509Certificate($x509cert),
596
            ]),
597
        ];
598
        if ($keyName !== null) {
599
            $info[] = new KeyName($keyName);
600
        }
601
        $keyDescriptor = new KeyDescriptor(
602
            new KeyInfo($info),
603
            $use,
604
        );
605
        $rd->addKeyDescriptor($keyDescriptor);
606
    }
607
608
609
    /**
610
     * Add a certificate.
611
     *
612
     * Helper function for adding a certificate to the metadata.
613
     *
614
     * @param \SimpleSAML\SAML2\XML\md\RoleDescriptor $rd The RoleDescriptor the certificate should be added to.
615
     * @param \SimpleSAML\Configuration    $metadata The metadata of the entity.
616
     */
617
    private function addCertificate(RoleDescriptor $rd, Configuration $metadata): void
618
    {
619
        $keys = $metadata->getPublicKeys();
620
        foreach ($keys as $key) {
621
            if ($key['type'] !== 'X509Certificate') {
622
                continue;
623
            }
624
            if (!isset($key['signing']) || $key['signing'] === true) {
625
                $this->addX509KeyDescriptor($rd, 'signing', $key['X509Certificate'], $key['name'] ?? null);
626
            }
627
            if (!isset($key['encryption']) || $key['encryption'] === true) {
628
                $this->addX509KeyDescriptor($rd, 'encryption', $key['X509Certificate'], $key['name'] ?? null);
629
            }
630
        }
631
632
        if ($metadata->hasValue('https.certData')) {
633
            $this->addX509KeyDescriptor($rd, 'signing', $metadata->getString('https.certData'));
634
        }
635
    }
636
}
637