From 72aa4a03ebc874d131ab73eaf52e6c4c848668aa Mon Sep 17 00:00:00 2001 From: Sixto Martin Date: Sun, 29 Sep 2024 22:51:03 +0200 Subject: [PATCH] Merge branch 'feature/support-encrypted-nameid' of github.com:joonlabs/php-saml into joonlabs-feature/support-encrypted-nameid --- src/Saml2/Response.php | 42 ++++++++++--------- ...pted_nameid_encrypted_assertion.xml.base64 | 1 + tests/src/OneLogin/Saml2/ResponseTest.php | 8 ++++ 3 files changed, 32 insertions(+), 19 deletions(-) create mode 100644 tests/data/responses/response_encrypted_nameid_encrypted_assertion.xml.base64 diff --git a/src/Saml2/Response.php b/src/Saml2/Response.php index 08d8e8d5..47ae0a87 100644 --- a/src/Saml2/Response.php +++ b/src/Saml2/Response.php @@ -61,6 +61,13 @@ class Response */ public $encrypted = false; + /** + * The response contains an encrypted nameId in the assertion. + * + * @var bool + */ + public $encryptedNameId = false; + /** * After validation, if it fail this var has the cause of the problem * @@ -227,14 +234,12 @@ public function isValid($requestId = null) ); } - if ($security['wantNameIdEncrypted']) { - $encryptedIdNodes = $this->_queryAssertion('/saml:Subject/saml:EncryptedID/xenc:EncryptedData'); - if ($encryptedIdNodes->length != 1) { - throw new ValidationError( - "The NameID of the Response is not encrypted and the SP requires it", - ValidationError::NO_ENCRYPTED_NAMEID - ); - } + $this->encryptedNameId = $this->encryptedNameId || $this->_queryAssertion('/saml:Subject/saml:EncryptedID/xenc:EncryptedData')->length > 0; + if (!$this->encryptedNameId && $security['wantNameIdEncrypted']) { + throw new ValidationError( + "The NameID of the Response is not encrypted and the SP requires it", + ValidationError::NO_ENCRYPTED_NAMEID + ); } // Validate Conditions element exists @@ -392,17 +397,6 @@ public function isValid($requestId = null) } } - // Detect case not supported - if ($this->encrypted) { - $encryptedIDNodes = Utils::query($this->decryptedDocument, '/samlp:Response/saml:Assertion/saml:Subject/saml:EncryptedID'); - if ($encryptedIDNodes->length > 0) { - throw new ValidationError( - 'SAML Response that contains an encrypted Assertion with encrypted nameId is not supported.', - ValidationError::NOT_SUPPORTED - ); - } - } - if (empty($signedElements) || (!$hasSignedResponse && !$hasSignedAssertion)) { throw new ValidationError( 'No Signature found. SAML Response rejected', @@ -1163,6 +1157,16 @@ protected function decryptAssertion(\DomNode $dom) if ($check === false) { throw new Exception('Error: string from decrypted assertion could not be loaded into a XML document'); } + + // check if the decrypted assertion contains an encryptedID + $encryptedID = $decrypted->getElementsByTagName('EncryptedID')->item(0); + + if ($encryptedID) { + // decrypt the encryptedID + $this->encryptedNameId = true; + $this->decryptAssertion($encryptedID); + } + if ($encData->parentNode instanceof DOMDocument) { return $decrypted; } else { diff --git a/tests/data/responses/response_encrypted_nameid_encrypted_assertion.xml.base64 b/tests/data/responses/response_encrypted_nameid_encrypted_assertion.xml.base64 new file mode 100644 index 00000000..135c5c49 --- /dev/null +++ b/tests/data/responses/response_encrypted_nameid_encrypted_assertion.xml.base64 @@ -0,0 +1 @@ +PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzYW1scDpSZXNwb25zZSB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBJRD0iX3Jlc3BvbnNlX2lkIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAyNC0wOS0wOVQxMjowMDowMFoiIERlc3RpbmF0aW9uPSJodHRwczovL3NwLmV4YW1wbGUuY29tL2FjcyIgSW5SZXNwb25zZVRvPSJfcmVxdWVzdF9pZCI+CiAgICA8c2FtbDpJc3N1ZXI+aHR0cHM6Ly9pZHAuZXhhbXBsZS5jb20vPC9zYW1sOklzc3Vlcj4KICAgIDxzYW1scDpTdGF0dXM+CiAgICAgICAgPHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPgogICAgPC9zYW1scDpTdGF0dXM+CiAgICA8c2FtbDpFbmNyeXB0ZWRBc3NlcnRpb24geG1sbnM6eGVuYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjIj48eGVuYzpFbmNyeXB0ZWREYXRhIHhtbG5zOnhlbmM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMDQveG1sZW5jIyIgVHlwZT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjRWxlbWVudCI+PHhlbmM6RW5jcnlwdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDkveG1sZW5jMTEjYWVzMjU2LWdjbSIvPgogICA8eGVuYzpDaXBoZXJEYXRhPgogICAgICA8eGVuYzpDaXBoZXJWYWx1ZT5oVi9lSmhMWVJrd0FTckhWbUNkNElaRm40bHIyd3FuM1pidDk4bldBVFFqb2FaNWxHSEQ4aFkvaXNQVjkxSU5pU0lYai9yZnZMNzVrL29Bb3NZY2xqUzJPaFVZUk1iMFN4OHU1Ui9Cc2Z6cFdTNm9senRnWnVVNWZ5TSsrTnVYWVVGTlJJM2RocTRFZnI5ejNHU2V4bnFlbGtOUVdHN3dCNzRjM2dlZHYwTWpxcjIxZXVkN3Y3MHdkVFZxTHpDMlJkd3paaS85czZyVytPY2l0NWVJS0YrRW1kWHpMVzFIZmZIMHBxVUw4bUJVdVpzQ05wMFhuMVdaOEp0WEJqWVFhdkJuMzhtSXZ0WXYzS0N0aytzSkVOWTZob3ZRZG83YUVHZG9SUlg0OGVSYWlxazVUUkdSenBsc0t2VHQyR01MUVBvKzYxWWI5RVk4L3puWE5RQldwVHJUZlpIcDF3djZmNExJbHlPRm05VnNxZWlpS2pUeFFycjdQYktRNHh6YWozZDViZjBtbFlEcEJ4VFRNUzBUY0FPbmNrby9qUmpiU0g5ZmYrUVR4dHRaSldFdS9ndXc0K3JDd29UZ2lldVhuM2tRMVFZSFRaSnhqWVJhSDd0YlRiRnFnaDJEMVZsWnNweFU0cHE4ckorOFp2bklVUUh2TEtNanhuK2k3KzY1TitEN1F0bXVFRUw3d0M3YmFiKzlxdnFRQnNGNHIrQmlDOEhNbGJRQmUzR3lCSFJ1YTJYZ3FOVW43WURVNEFTZlBVRmpmMkE1Z2k3d1c2c1hpWlhGeUpaQmpIOVpUOXNuRmdOUXVueHg5d0VYeXNkdGtVUGlvOHB5WHloc1lmRG5RM2k4Y1c1aFV4OXE1K05TMVNzeCtxTFZEaEFxcDJhZHJGUDFCZW02aUtaWFNkbnpWYkt0K242dVM0aHVOTm42RE02dkhnaEpyWW43dUZrWWJnR1VUN1Z5K1lGaDVLWit2N3hzbTRhNEVYN014Q0tzd0U1empjT1VGMDN2cDJkTlB4RGdmRnluR20xT1N3V2N2L3kxMytNOUsvZnhiNFlZajFWekR5Y212OVdyN1JzVUhnZ3BQcS9VR09PUHJ0cnNlMlZHM0llbHB3ZEFOTWlzNldhTjFYWWU0K3JHRXp3WHl5VnV5TWNza0V5RlNTb1VHNFcwMWNGbHA3blhIUGtZbUJKSHA5MTBWTW1PWEdyTC9TYW9ubUVkOU93YUIyUW1td0dmd1o5dU5pK3F0Wlg2b00rQ2tWSFhDdUFTdXVzalRtTVF0REtta0NUVUhtcDFveXBBcnU5OUFXMUlWRDRHV01ZVlJMNGFrd3dReldMU3pCQWJDbk1hUGp6MTJIaWNTd3RjMTJnNjFtZ2JlYVM0SlF3c2xxbmwza0FKcHVkTUszZGY2bUFLeW5nUFdNeFNBWU9PVkRPVkxCZTJMRnpieHZ2NzluNUwrWmFlcmRUVHpoTnVxT0ptZzJZOStKQWpsd0UvWHNjVmRXQVlITHdOUjhDYXc1eW1hOUFQdFpjclB0VjZuMTYycTdnWGV5QTkvTTAxaXdSSkY5dzE2UklZMWNJNWhsQys0YXRxci9MK29ZTnROWmtlUEdzZ2pNbjY2bUFYMHgwRlBEb2xLRHpVOXgyV2xOL0JQeWR0a0hpV3BsKzNKWGlpR1dWRzdnUkZKYnRqWmR4UGUvam1uRlIwK015RUl4MklpLyt5emVWYlppanIxNmp2c2pKS2hia2x1cXF6YURybEhDaFFicEVWNlBWSTlnS2dCcnloS3l4ZGJMYWthQ0xPcW5POHFEMVNJajA3NmpqeVJlZzE4MU0va2xTU3gvK1hFdzRpTjhxUkl1aEpGMDFLM0VsZ043SkJWZzAva2p2SHdiNyt5d3IrK1FUVnEzUmpEMkV0eVczTG85dTIzOG9KTGhwOE9PVWNnd3FPOWdncWsrNGI1TS9BV0dEcHZlZStqOUpseDRWMUZuTncxZUQvQkdFd2p6a3lRckRIVDRDT01hOWZLVkhhL0pPN1o3emI5azdiWTlvbWhwOHBmU1V0bTJjNmVwY0h4R0s4SFI4cG9mTVJwaUw4WXNmRUg0dEV2OC9PaVozS3AyZ0JEVmthU2dQTXhwdzRIVjJmL1BmbWJMQmlubHZzbmNyNjhFNDF3LzJGN2hHWVU3OGM3Y2UxYVBIU2haU1k3MmFPOHBEOUxrMTRqV1lMY2w4QThINHZ1STVjYjY3am9qbHlHQjNwYTRxakh6RnRRZjdxdlFsdjZvYTJWRzlDSEJIUFdOazNxRm4xbGwwS3RjaDVtd2VBTldBMlZWSVVSa0o2QXROY3p2TXhIMUlZeXJQNXplaE0vdTI2ekJHQzNZa3F1M1ZZZXhRZ3Q3djRUZDFPWXFxVU9HSXhCaGtYcnMzdHNIZz09PC94ZW5jOkNpcGhlclZhbHVlPgogICA8L3hlbmM6Q2lwaGVyRGF0YT4KPC94ZW5jOkVuY3J5cHRlZERhdGE+PC9zYW1sOkVuY3J5cHRlZEFzc2VydGlvbj4KPGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+CiAgPGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNyc2Etc2hhMjU2Ii8+CiAgPGRzOlJlZmVyZW5jZSBVUkk9IiNfcmVzcG9uc2VfaWQiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjc2hhMjU2Ii8+PGRzOkRpZ2VzdFZhbHVlPi9iVnZlU1JLL1pVdUlza1pFeTdZZ3Nub1lQMjJ1U0FJQWtGRnlpQTk5Y2c9PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8+PGRzOlNpZ25hdHVyZVZhbHVlPmFXSUllOFF1cTI4NHRrOUpBN3d1dXZNaHdnckJNczVhUm1xUUxXRnlzdFV6dWlpcFZUYUE2TElyZ05Gd21ocm1EZmZ3ZVZRWGREK245OGxSNXhSTitsTEs4Vy9IREVGL0NYNmlEekg3UmxQSEhsQ0k0alB2TGtyeTkwZ1J3VHM4ZXZ4Qy9kTlYwYW84WEFJMm5XMkZuYWJjRUhuNXE3ZkNkaXUxWlZkMzQycz08L2RzOlNpZ25hdHVyZVZhbHVlPgo8L2RzOlNpZ25hdHVyZT48L3NhbWxwOlJlc3BvbnNlPgo= diff --git a/tests/src/OneLogin/Saml2/ResponseTest.php b/tests/src/OneLogin/Saml2/ResponseTest.php index 8d532fa8..f1368aef 100644 --- a/tests/src/OneLogin/Saml2/ResponseTest.php +++ b/tests/src/OneLogin/Saml2/ResponseTest.php @@ -1814,4 +1814,12 @@ public function testIsValidSignUsingX509certMulti() $response = new Response($settings, $xml); $this->assertTrue($response->isValid()); } + + public function testCanGetEncryptedNameIdInEncryptedAssertion() + { + $xml = file_get_contents(TEST_ROOT . '/data/responses/response_encrypted_nameid_encrypted_assertion.xml.base64'); + $response = new Response($this->_settings, $xml); + $this->assertTrue($response->isValid()); + $this->assertSame('user@example.com', $response->getNameId()); + } }