66
77use DOMDocument ;
88use Exception ;
9- use RobRichards \ XMLSecLibs \ XMLSecurityKey ;
9+ use OpenSSLAsymmetricKey ;
1010use SimpleSAML \Configuration ;
1111use SimpleSAML \SAML2 \Compat \ContainerSingleton ;
1212use SimpleSAML \SAML2 \XML \samlp \AbstractMessage ;
2424
2525use function chunk_split ;
2626use function file_exists ;
27+ use function is_object ;
28+ use function is_string ;
29+ use function method_exists ;
2730use function openssl_pkey_get_details ;
2831use function openssl_pkey_get_public ;
32+ use function property_exists ;
2933use function sha1 ;
3034use function sprintf ;
3135use function stream_context_create ;
3236use function stream_context_get_options ;
37+ use function trim ;
3338
3439/**
3540 * Implementation of the SAML 2.0 SOAP binding.
@@ -251,7 +256,7 @@ private static function addSSLValidator(AbstractMessage $msg, $context): void
251256 return ;
252257 }
253258
254- if (!isset ($ keyInfo ['key ' ])) {
259+ if (!isset ($ keyInfo ['key ' ]) || ! is_string ( $ keyInfo [ ' key ' ]) ) {
255260 $ container ->getLogger ()->warning ('Missing key in public key details. ' );
256261 return ;
257262 }
@@ -263,29 +268,88 @@ private static function addSSLValidator(AbstractMessage $msg, $context): void
263268 /**
264269 * Validate a SOAP message against the certificate on the SSL connection.
265270 *
266- * @param string $data The public key that was used on the connection.
267- * @param \RobRichards\XMLSecLibs\XMLSecurityKey $key The key we should validate the certificate against.
271+ * @param string $data The public key (PEM) that was used on the connection.
272+ * @param mixed $key The key we should validate the certificate against.
268273 * @throws \Exception
269274 */
270- public static function validateSSL (string $ data , XMLSecurityKey $ key ): void
275+ public static function validateSSL (string $ data , mixed $ key ): void
271276 {
272277 $ container = ContainerSingleton::getInstance ();
273278
274- $ keyInfo = openssl_pkey_get_details ( $ key -> key );
279+ $ pem = self :: extractPublicKeyPem ( $ key );
275280
276- if ($ keyInfo === false ) {
277- throw new Exception ('Unable to get key details from XMLSecurityKey . ' );
281+ if (trim ( $ pem ) !== trim ( $ data ) ) {
282+ throw new Exception ('Key on SSL connection did not match key we validated against . ' );
278283 }
279284
280- if (!isset ($ keyInfo ['key ' ])) {
281- throw new Exception ('Missing key in public key details. ' );
285+ $ container ->getLogger ()->debug ('Message validated based on SSL certificate. ' );
286+ }
287+
288+
289+ /**
290+ * Extract a PEM-encoded public key from different key representations.
291+ *
292+ * This avoids coupling to a specific XML security backend.
293+ *
294+ * @param mixed $key
295+ * @throws \Exception
296+ */
297+ private static function extractPublicKeyPem (mixed $ key ): string
298+ {
299+ // If the validating key is already PEM, normalize it by re-loading through OpenSSL.
300+ if (is_string ($ key )) {
301+ $ opensslKey = openssl_pkey_get_public ($ key );
302+ if ($ opensslKey === false ) {
303+ throw new Exception ('Unable to load validating public key from PEM string. ' );
304+ }
305+
306+ $ keyInfo = openssl_pkey_get_details ($ opensslKey );
307+ if ($ keyInfo === false || !isset ($ keyInfo ['key ' ]) || !is_string ($ keyInfo ['key ' ])) {
308+ throw new Exception ('Unable to get key details from validating PEM key. ' );
309+ }
310+
311+ return $ keyInfo ['key ' ];
282312 }
283313
284- if (trim ($ keyInfo ['key ' ]) !== trim ($ data )) {
285- throw new Exception ('Key on SSL connection did not match key we validated against. ' );
314+ // Some key implementations may expose PEM via a method.
315+ if (is_object ($ key )) {
316+ foreach (['getPublicKeyPem ' , 'getPem ' , 'toPEM ' , 'toPem ' ] as $ method ) {
317+ if (method_exists ($ key , $ method )) {
318+ /** @var mixed $pem */
319+ $ pem = $ key ->{$ method }();
320+ if (is_string ($ pem ) && $ pem !== '' ) {
321+ return self ::extractPublicKeyPem ($ pem );
322+ }
323+ }
324+ }
325+
326+ // Common compatibility case: an object wraps an OpenSSL key or PEM in a public "key" property.
327+ if (property_exists ($ key , 'key ' )) {
328+ /** @var mixed $inner */
329+ $ inner = $ key ->key ;
330+
331+ if (is_string ($ inner )) {
332+ return self ::extractPublicKeyPem ($ inner );
333+ }
334+
335+ if ($ inner instanceof OpenSSLAsymmetricKey) {
336+ $ keyInfo = openssl_pkey_get_details ($ inner );
337+ if ($ keyInfo !== false && isset ($ keyInfo ['key ' ]) && is_string ($ keyInfo ['key ' ])) {
338+ return $ keyInfo ['key ' ];
339+ }
340+ }
341+ }
286342 }
287343
288- $ container ->getLogger ()->debug ('Message validated based on SSL certificate. ' );
344+ // Last attempt: OpenSSL might accept the value directly (OpenSSLAsymmetricKey).
345+ if ($ key instanceof OpenSSLAsymmetricKey) {
346+ $ keyInfo = openssl_pkey_get_details ($ key );
347+ if ($ keyInfo !== false && isset ($ keyInfo ['key ' ]) && is_string ($ keyInfo ['key ' ])) {
348+ return $ keyInfo ['key ' ];
349+ }
350+ }
351+
352+ throw new Exception ('Unable to extract public key PEM from validating key. ' );
289353 }
290354
291355
0 commit comments