【问题标题】:Verify digital signature in SAML response against certificate in PHP根据 PHP 中的证书验证 SAML 响应中的数字签名
【发布时间】:2013-11-01 13:04:29
【问题描述】:

我是 SSL 证书的新手。所以对我到目前为止所做的事情有点谨慎。 我正在创建一个使用 SSO 对使用 PHP 5.4 的用户进行身份验证的应用程序。 我拥有的: 一方提供的证书(.pfx)。 POST 变量中的加密 SAML。

解密后的xml和SAML: Why is the certificate within the Signature?差不多

我需要验证响应是否来自经过验证的提供商。我在谷歌搜索时知道我需要 .pem 而不是 .pfx,因此我使用 ssl 命令将 .pfx 文件转换为 .pem。我使用了来自http://www.php.net/manual/es/function.openssl-verify.php#62526 的代码。这是我的代码。

$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);

那么我应该忽略嵌入在 xml 中的 X509Certificate 还是我也需要检查它... openssl_verify 就足够了吗?我在正确的道路上吗?请任何指导将不胜感激。

【问题讨论】:

  • 这个问题很老了......但仍然存在......任何专家的意见将不胜感激。
  • 不要尝试自己实现 SAML 堆栈,而是使用现有的库/工具包/产品

标签: php ssl ssl-certificate saml


【解决方案1】:

使用xmldsig 语法签名的 XML 有 3 个重要部分:

  1. Signature -> KeyInfo 包含有关从用于签署数据的私钥派生的公钥的信息
  2. Signature -> SignedInfo 包含将使用上述私钥签名的数据;数据包含有关如何计算验证的信息,例如:CanonicalizationMethodSignatureMethodReference
  3. Signature -> SignatureValue 包含使用私钥签名Signature -> SignedInfo 生成的签名值

理论上,这就是代码应该如何查找 rsa-sha1 算法(由 Signature -> SignedInfo -> SignatureMethod 指定),具有以下规范化方法:Exclusive XML Canonicalization 1.0(省略 cmets),并提供 x509 证书:

$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);    

这个库在 php 中实现 xmldsig 方面做得很好:xmlseclibs;可以在此处找到如何验证 xmldsig 的示例:https://github.com/robrichards/xmlseclibs/blob/master/tests/xmlsec-verify.phpt。这个库还验证来自Signature -> SignedInfo -> Reference 的摘要值,我在上面省略了一个步骤。

【讨论】:

  • 最后一个答案:) ..但我不再从事这个项目..但它可能对其他人有帮助..我不确定是否有其他人可以将此标记为答案,如果有效。
  • 这段代码对我有用——但您可能需要设置 openssl_verify() 的第四个参数以匹配 SAML 响应文档中 SignatureMethod 算法标签中定义的加密方法。
  • $query = './secdsig:SignatureMethod'; $signatureMethod = $xpath->evaluate($query, $signedInfoNode)->item(0)->getAttribute('Algorithm'); $spos = strpos($signatureMethod, '#'); if (!empty($signatureMethod) && $spos !== false) { $algo = strtoupper( substr($signatureMethod, $spos + 1) ); } 其他 { $algo = 'RSA-SHA1'; // 倒退? } var_dump($algo);
【解决方案2】:

我建议你使用https://github.com/lightSAML/lightSAML。它使用 xmlseclibs 并实现了完整的 SAML SSO SP 配置文件。 LightSAML 食谱http://www.lightsaml.com/LightSAML-Core/Cookbook/How-to-receive-SAML-message/http://www.lightsaml.com/LightSAML-Core/Cookbook/How-to-verify-signature-of-SAML-message/ 中给出了从 HTTP POST 简单接收 SAML 响应并验证其签名示例,整个代码如下所示

$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";
}

通过完整的 SAML SSO 配置文件特定操作处理响应不止于此,有关这些详细信息,您可以查看 https://github.com/lightSAML/lightSAML/blob/master/web/sp/acs.php 中的示例,或者如果您使用的是 Symfony https://github.com/lightSAML/SpBundle

【讨论】:

    猜你喜欢
    • 2016-12-06
    • 1970-01-01
    • 2016-01-14
    • 1970-01-01
    • 2014-03-16
    • 2018-09-13
    • 1970-01-01
    • 1970-01-01
    • 2011-09-22
    相关资源
    最近更新 更多