openssl_sign - Verificar firma digital en respuesta SAML contra certificado en PHP
sign php (2)
Le sugiero que utilice https://github.com/lightSAML/lightSAML . Está utilizando xmlseclibs e implementa el perfil completo de SAML SSO SP. La recepción simple de la respuesta SAML del HTTP POST y la verificación de sus muestras de firma se encuentran en el libro de recetas LightSAML http://www.lightsaml.com/LightSAML-Core/Cookbook/How-to-receive-SAML-message/ y http://www.lightsaml.com/LightSAML-Core/Cookbook/How-to-verify-signature-of-SAML-message/ y el código completo se vería así
$request = /Symfony/Component/HttpFoundation/Request::createFromGlobals();
$bindingFactory = new /LightSaml/Binding/BindingFactory();
$binding = $bindingFactory->getBindingByRequest($request);
$messageContext = new /LightSaml/Context/Profile/MessageContext();
/** @var /LightSaml/Model/Protocol/Response $response */
$response = $binding->receive($request, $messageContext);
$key = /LightSaml/Credential/KeyHelper::createPublicKey(
/LightSaml/Credential/X509Certificate::fromFile(__DIR__.''/../web/sp/saml.crt'')
);
/** @var /LightSaml/Model/XmlDSig/SignatureXmlReader $signatureReader */
$signatureReader = $authnRequest->getSignature();
try {
$ok = $signatureReader->validate($key);
if ($ok) {
print "Signaure OK/n";
} else {
print "Signature not validated";
}
} catch (/Exception $ex) {
print "Signature validation failed/n";
}
El manejo de la Respuesta por la acción específica del perfil de SSO de SAML es un poco más que eso; para esos detalles, puede consultar la muestra en https://github.com/lightSAML/lightSAML/blob/master/web/sp/acs.php o si estás usando Symfony https://github.com/lightSAML/SpBundle
Soy un novato para el certificado SSL. Así que un poco cauteloso sobre lo que he hecho hasta ahora. Estoy creando una aplicación que usa SSO para autenticar a los usuarios que usan PHP 5.4. Lo que tengo: un certificado (.pfx) proporcionado por la parte. SAML encriptado en la variable POST.
El xml descifrado es casi similar a SAML: ¿Por qué el certificado está dentro de la firma?
Necesito verificar que la respuesta sea del proveedor verificado. Llegué a saber mientras buscaba en Google que necesito .pem en lugar de .pfx, así que he convertido el archivo .pfx a .pem usando los comandos ssl. He usado el código de http://www.php.net/manual/es/function.openssl-verify.php#62526 . Aquí está mi código.
$encxml=$_POST[''SAMLResponse''];
$xml = new SimpleXMLElement(base64_decode($encxml));
$signature = ((string)$xml->Signature->SignatureValue);
var_dump($signature);
//do I need to do something with this X509Certificate value embedded in xml??
$cert = ((string)$xml->Signature->KeyInfo->X509Data->X509Certificate);
var_dump($cert);
//Or I need
$fp = fopen("xyz.pem", "r");
$priv_key = fread($fp, 8192);
fclose($fp);
print_r($priv_key);
$ok = openssl_verify($xml, $signature, $priv_key);
Entonces, ¿debería ignorar el certificado X509 incrustado en xml o debo verificarlo también ... será suficiente openssl_verify? y estoy en el camino correcto? Por favor, cualquier orientación será apreciada.
Un XML firmado con la sintaxis xmldsig tiene 3 partes importantes:
-
Signature -> KeyInfo
contiene información sobre la clave pública derivada de la clave privada utilizada para firmar los datos -
Signature -> SignedInfo
contiene los datos que se firmarán con la clave privada mencionada anteriormente; los datos contienen información sobre cómo se debe calcular la verificación, como:CanonicalizationMethod
,CanonicalizationMethod
SignatureMethod
,Reference
-
Signature -> SignatureValue
contiene el valor de la firma generada al firmarSignature -> SignedInfo
con la clave privada
Teóricamente, así es como el código debe buscar un algoritmo rsa-sha1 (especificado por Signature -> SignedInfo -> SignatureMethod
), con el siguiente método de canonicalización: XML Canonicalization 1.0 exclusivo (omite los comentarios) y el certificado x509 proporcionado:
$xmlDoc = new DOMDocument();
$xmlDoc->loadXML($xmlString);
$xpath = new DOMXPath($xmlDoc);
$xpath->registerNamespace(''secdsig'', ''http://www.w3.org/2000/09/xmldsig#'');
// fetch Signature node from XML
$query = ".//secdsig:Signature";
$nodeset = $xpath->query($query, $xmlDoc);
$signatureNode = $nodeset->item(0);
// fetch SignedInfo node from XML
$query = "./secdsig:SignedInfo";
$nodeset = $xpath->query($query, $signatureNode);
$signedInfoNode = $nodeset->item(0);
// canonicalize SignedInfo using the method descried in
// ./secdsig:SignedInfo/secdsig:CanonicalizationMethod/@Algorithm
$signedInfoNodeCanonicalized = $signedInfoNode->C14N(true, false);
// fetch the x509 certificate from XML
$query = ''string(./secdsig:KeyInfo/secdsig:X509Data/secdsig:X509Certificate)'';
$x509cert = $xpath->evaluate($query, $signatureNode);
// we have to re-wrap the certificate from XML to respect the PEM standard
$x509cert = "-----BEGIN CERTIFICATE-----/n"
. $x509cert . "/n"
. "-----END CERTIFICATE-----";
// fetch public key from x509 certificate
$publicKey = openssl_get_publickey($x509cert);
// fetch the signature from XML
$query = ''string(./secdsig:SignatureValue)'';
$signature = base64_decode($xpath->evaluate($query, $signatureNode));
// verify the signature
$ok = openssl_verify($signedInfoNodeCanonicalized, $signature, $publicKey);
Esta biblioteca hace un buen trabajo implementando xmldsig en php: xmlseclibs ; un ejemplo de cómo verificar un xmldsig se puede encontrar aquí: https://github.com/robrichards/xmlseclibs/blob/master/tests/xmlsec-verify.phpt . Esta biblioteca también valida el valor de resumen de Signature -> SignedInfo -> Reference
, un paso que omití anteriormente.