Skip to content

Commit aee91b8

Browse files
committed
Add support to Key Rollover. Be able to register future SP x509cert on the settings and publish it on SP metadata
1 parent 304a43d commit aee91b8

File tree

8 files changed

+223
-20
lines changed

8 files changed

+223
-20
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
/demo-old/settings.php
77
/certs/sp.key
88
/certs/sp.crt
9+
/certs/sp_new.crt
910
/certs/metadata.key
1011
/certs/metadata.crt
1112
/tests/build

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ Sometimes we could need a signature on the metadata published by the SP, in
183183
this case we could use the x.509 cert previously mentioned or use a new x.509
184184
cert: `metadata.crt` and `metadata.key`.
185185

186+
Use `sp_new.crt` if you are in a key rollover process and you want to
187+
publish that x509certificate on Service Provider metadata.
186188

187189
#### `extlib/` ####
188190

@@ -337,6 +339,14 @@ $settings = array (
337339
'x509cert' => '',
338340
'privateKey' => '',
339341

342+
/*
343+
* Key rollover
344+
* If you plan to update the SP x509cert and privateKey
345+
* you can define here the new x509cert and it will be
346+
* published on the SP metadata so Identity Providers can
347+
* read them and get ready for rollover.
348+
*/
349+
// 'x509certNew' => '',
340350
),
341351

342352
// Identity Provider Data that we want connected with our SP.
@@ -1250,6 +1260,7 @@ Configuration of the OneLogin PHP Toolkit
12501260
* `checkSPCerts` - Checks if the x509 certs of the SP exists and are valid.
12511261
* `getSPkey` - Returns the x509 private key of the SP.
12521262
* `getSPcert` - Returns the x509 public cert of the SP.
1263+
* `getSPcertNew` - Returns the future x509 public cert of the SP.
12531264
* `getIdPData` - Gets the IdP data.
12541265
* `getSPData`Gets the SP data.
12551266
* `getSecurityData` - Gets security data.

certs/README

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Onelogin PHP Toolkit expects certs for the SP stored at:
44

55
* sp.key Private Key
66
* sp.crt Public cert
7+
* sp_new.crt Future Public cert
78

89
Also you can use other cert to sign the metadata of the SP using the:
910

lib/Saml2/Settings.php

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,28 @@ public function getSPcert()
699699
return $cert;
700700
}
701701

702+
/**
703+
* Returns the x509 public of the SP that is
704+
* planed to be used soon instead the other
705+
* public cert
706+
* @return string SP public cert New
707+
*/
708+
public function getSPcertNew()
709+
{
710+
$cert = null;
711+
712+
if (isset($this->_sp['x509certNew']) && !empty($this->_sp['x509certNew'])) {
713+
$cert = $this->_sp['x509certNew'];
714+
} else {
715+
$certFile = $this->_paths['cert'].'sp_new.crt';
716+
717+
if (file_exists($certFile)) {
718+
$cert = file_get_contents($certFile);
719+
}
720+
}
721+
return $cert;
722+
}
723+
702724
/**
703725
* Gets the IdP data.
704726
*
@@ -780,8 +802,16 @@ public function getSPMetadata()
780802
{
781803
$metadata = OneLogin_Saml2_Metadata::builder($this->_sp, $this->_security['authnRequestsSigned'], $this->_security['wantAssertionsSigned'], null, null, $this->getContacts(), $this->getOrganization());
782804

783-
$cert = $this->getSPcert();
805+
$certNew = $this->getSPcertNew();
806+
if (!empty($certNew)) {
807+
$metadata = OneLogin_Saml2_Metadata::addX509KeyDescriptors(
808+
$metadata,
809+
$certNew,
810+
$this->_security['wantNameIdEncrypted'] || $this->_security['wantAssertionsEncrypted']
811+
);
812+
}
784813

814+
$cert = $this->getSPcert();
785815
if (!empty($cert)) {
786816
$metadata = OneLogin_Saml2_Metadata::addX509KeyDescriptors(
787817
$metadata,
@@ -848,6 +878,7 @@ public function getSPMetadata()
848878
$digestAlgorithm = $this->_security['digestAlgorithm'];
849879
$metadata = OneLogin_Saml2_Metadata::signMetadata($metadata, $keyMetadata, $certMetadata, $signatureAlgorithm, $digestAlgorithm);
850880
}
881+
// print_r($metadata);
851882
return $metadata;
852883
}
853884

settings_example.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
// Enable debug mode (to print errors)
1111
'debug' => false,
1212

13-
// Set a BaseURL to be used instead of try to guess
13+
// Set a BaseURL to be used instead of try to guess
1414
// the BaseURL of the view that process the SAML Message.
1515
// Ex. http://sp.example.com/
16-
// http://example.com/sp/
16+
// http://example.com/sp/
1717
'baseurl' => null,
1818

1919
// Service Provider Data that we are deploying
@@ -32,7 +32,7 @@
3232
),
3333
// If you need to specify requested attributes, set a
3434
// attributeConsumingService. nameFormat, attributeValue and
35-
// friendlyName can be omitted. Otherwise remove this section.
35+
// friendlyName can be omitted. Otherwise remove this section.
3636
"attributeConsumingService"=> array(
3737
"ServiceName" => "SP test",
3838
"serviceDescription" => "Test Service",
@@ -65,6 +65,15 @@
6565
// the certs folder. But we can also provide them with the following parameters
6666
'x509cert' => '',
6767
'privateKey' => '',
68+
69+
/*
70+
* Key rollover
71+
* If you plan to update the SP x509cert and privateKey
72+
* you can define here the new x509cert and it will be
73+
* published on the SP metadata so Identity Providers can
74+
* read them and get ready for rollover.
75+
*/
76+
// 'x509certNew' => '',
6877
),
6978

7079
// Identity Provider Data that we want connect with our SP

tests/settings/settings5.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
$settingsInfo = array (
3+
'strict' => false,
4+
'debug' => false,
5+
'sp' => array (
6+
'entityId' => 'http://stuff.com/endpoints/metadata.php',
7+
'assertionConsumerService' => array (
8+
'url' => 'http://stuff.com/endpoints/endpoints/acs.php',
9+
),
10+
'singleLogoutService' => array (
11+
'url' => 'http://stuff.com/endpoints/endpoints/sls.php',
12+
),
13+
'NameIDFormat' => 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified',
14+
'privateKey' => 'MIICXgIBAAKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABAoGAD4/Z4LWVWV6D1qMIp1Gzr0ZmdWTE1SPdZ7Ej8glGnCzPdguCPuzbhGXmIg0VJ5D+02wsqws1zd48JSMXXM8zkYZVwQYIPUsNn5FetQpwxDIMPmhHg+QNBgwOnk8JK2sIjjLPL7qY7Itv7LT7Gvm5qSOkZ33RCgXcgz+okEIQMYkCQQDzbTOyDL0c5WQV6A2k06T/azdhUdGXF9C0+WkWSfNaovmTgRXh1G+jMlr82Snz4p4/STt7P/XtyWzF3pkVgZr3AkEA7nPjXwHlttNEMo6AtxHd47nizK2NUN803ElIUT8P9KSCoERmSXq66PDekGNic4ldpsSvOeYCk8MAYoDBy9kvVwJBAMLgX4xg6lzhv7hR5+pWjTb1rIY6rCHbrPfU264+UZXz9v2BT/VUznLF81WMvStD9xAPHpFS6R0OLghSZhdzhI0CQQDL8Duvfxzrn4b9QlmduV8wLERoT6rEVxKLsPVz316TGrxJvBZLk/cV0SRZE1cZf4ukXSWMfEcJ/0Zt+LdG1CqjAkEAqwLSglJ9Dy3HpgMz4vAAyZWzAxvyA1zW0no9GOLcPQnYaNUN/Fy2SYtETXTb0CQ9X1rt8ffkFP7ya+5TC83aMg==',
15+
'x509cert' => 'MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYDVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xiZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2ZlaWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2QarQ4/67OZfHd7R+POBXhophSMv1ZOo',
16+
'x509certNew' => 'MIICVDCCAb2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBHMQswCQYDVQQGEwJ1czEQMA4GA1UECAwHZXhhbXBsZTEQMA4GA1UECgwHZXhhbXBsZTEUMBIGA1UEAwwLZXhhbXBsZS5jb20wHhcNMTcwNDA3MDgzMDAzWhcNMjcwNDA1MDgzMDAzWjBHMQswCQYDVQQGEwJ1czEQMA4GA1UECAwHZXhhbXBsZTEQMA4GA1UECgwHZXhhbXBsZTEUMBIGA1UEAwwLZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKhPS4/0azxbQekHHewQGKD7Pivr3CDpsrKxY3xlVanxj427OwzOb5KUVzsDEazumt6sZFY8HfidsjXY4EYA4ZzyL7ciIAR5vlAsIYN9nJ4AwVDnN/RjVwj+TN6BqWPLpVIpHc6Dl005HyE0zJnk1DZDn2tQVrIzbD3FhCp7YeotAgMBAAGjUDBOMB0GA1UdDgQWBBRYZx4thASfNvR/E7NsCF2IaZ7wIDAfBgNVHSMEGDAWgBRYZx4thASfNvR/E7NsCF2IaZ7wIDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBACz4aobx9aG3kh+rNyrlgM3K6dYfnKG1/YH5sJCAOvg8kDr0fQAQifH8lFVWumKUMoAe0bFTfwWtp/VJ8MprrEJth6PFeZdczpuv+fpLcNj2VmNVJqvQYvS4m36OnBFh1QFZW8UrbFIfdtm2nuZ+twSKqfKwjLdqcoX0p39h7Uw/'
17+
),
18+
'idp' => array (
19+
'entityId' => 'http://idp.example.com/',
20+
'singleSignOnService' => array (
21+
'url' => 'http://idp.example.com/SSOService.php',
22+
),
23+
'singleLogoutService' => array (
24+
'url' => 'http://idp.example.com/SingleLogoutService.php',
25+
),
26+
'x509cert' => 'MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYDVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xiZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2ZlaWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2QarQ4/67OZfHd7R+POBXhophSMv1ZOo'
27+
),
28+
'compress' => array(
29+
'requests' => true,
30+
'responses' => true
31+
),
32+
'security' => array (
33+
'authnRequestsSigned' => false,
34+
'wantAssertionsSigned' => false,
35+
'signMetadata' => false,
36+
),
37+
'contactPerson' => array (
38+
'technical' => array (
39+
'givenName' => 'technical_name',
40+
'emailAddress' => '[email protected]',
41+
),
42+
'support' => array (
43+
'givenName' => 'support_name',
44+
'emailAddress' => '[email protected]',
45+
),
46+
),
47+
48+
'organization' => array (
49+
'en-US' => array(
50+
'name' => 'sp_test',
51+
'displayname' => 'SP test',
52+
'url' => 'http://sp.example.com',
53+
),
54+
),
55+
);

tests/src/OneLogin/Saml2/MetadataTest.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,4 +263,54 @@ public function testAddX509KeyDescriptors()
263263
$this->assertContains('Error parsing metadata', $e->getMessage());
264264
}
265265
}
266+
267+
/**
268+
* Tests the addX509KeyDescriptors method of the OneLogin_Saml2_Metadata
269+
* Case: Execute 2 addX509KeyDescriptors calls
270+
*
271+
* @covers OneLogin_Saml2_Metadata::addX509KeyDescriptors
272+
*/
273+
public function testAddX509KeyDescriptors2Times()
274+
{
275+
$settingsDir = TEST_ROOT .'/settings/';
276+
include $settingsDir.'settings1.php';
277+
278+
$settings = new OneLogin_Saml2_Settings($settingsInfo);
279+
$spData = $settings->getSPData();
280+
281+
$metadata = OneLogin_Saml2_Metadata::builder($spData);
282+
283+
$this->assertNotContains('<md:KeyDescriptor use="signing"', $metadata);
284+
$this->assertNotContains('<md:KeyDescriptor use="encryption"', $metadata);
285+
286+
$certPath = $settings->getCertPath();
287+
$cert = file_get_contents($certPath.'sp.crt');
288+
289+
$metadata = OneLogin_Saml2_Metadata::addX509KeyDescriptors($metadata, $cert, false);
290+
291+
$this->assertEquals(1, substr_count($metadata, "<md:KeyDescriptor"));
292+
293+
$metadata = OneLogin_Saml2_Metadata::addX509KeyDescriptors($metadata, $cert, false);
294+
295+
$this->assertEquals(2, substr_count($metadata, "<md:KeyDescriptor"));
296+
297+
298+
$metadata2 = OneLogin_Saml2_Metadata::builder($spData);
299+
300+
$metadata2 = OneLogin_Saml2_Metadata::addX509KeyDescriptors($metadata2, $cert);
301+
302+
$this->assertEquals(2, substr_count($metadata2, "<md:KeyDescriptor"));
303+
304+
$this->assertEquals(1, substr_count($metadata2, '<md:KeyDescriptor use="signing"'));
305+
306+
$this->assertEquals(1, substr_count($metadata2, '<md:KeyDescriptor use="encryption"'));
307+
308+
$metadata2 = OneLogin_Saml2_Metadata::addX509KeyDescriptors($metadata2, $cert);
309+
310+
$this->assertEquals(4, substr_count($metadata2, "<md:KeyDescriptor"));
311+
312+
$this->assertEquals(2, substr_count($metadata2, '<md:KeyDescriptor use="signing"'));
313+
314+
$this->assertEquals(2, substr_count($metadata2, '<md:KeyDescriptor use="encryption"'));
315+
}
266316
}

0 commit comments

Comments
 (0)