public function testTransforms() { $trans = new Zend_InfoCard_Xml_Security_Transform(); try { $trans->addTransform("foo"); $this->fail("Expected Exception Not Thrown"); } catch(Exception $e) { /* yay */ } $this->assertTrue(is_array($trans->getTransformList())); }
/** * Validates the signature of a provided XML block * * @param string $strXMLInput An XML block containing a Signature * @return bool True if the signature validated, false otherwise * @throws Zend_InfoCard_Xml_Security_Exception */ static public function validateXMLSignature($strXMLInput) { if(!extension_loaded('openssl')) { throw new Zend_InfoCard_Xml_Security_Exception("You must have the openssl extension installed to use this class"); } $sxe = simplexml_load_string($strXMLInput); if(!isset($sxe->Signature)) { throw new Zend_InfoCard_Xml_Security_Exception("Could not identify XML Signature element"); } if(!isset($sxe->Signature->SignedInfo)) { throw new Zend_InfoCard_Xml_Security_Exception("Signature is missing a SignedInfo block"); } if(!isset($sxe->Signature->SignatureValue)) { throw new Zend_InfoCard_Xml_Security_Exception("Signature is missing a SignatureValue block"); } if(!isset($sxe->Signature->KeyInfo)) { throw new Zend_InfoCard_Xml_Security_Exception("Signature is missing a KeyInfo block"); } if(!isset($sxe->Signature->KeyInfo->KeyValue)) { throw new Zend_InfoCard_Xml_Security_Exception("Signature is missing a KeyValue block"); } switch((string)$sxe->Signature->SignedInfo->CanonicalizationMethod['Algorithm']) { case self::CANONICAL_METHOD_C14N_EXC: $cMethod = (string)$sxe->Signature->SignedInfo->CanonicalizationMethod['Algorithm']; break; default: throw new Zend_InfoCard_Xml_Security_Exception("Unknown or unsupported CanonicalizationMethod Requested"); } switch((string)$sxe->Signature->SignedInfo->SignatureMethod['Algorithm']) { case self::SIGNATURE_METHOD_SHA1: $sMethod = (string)$sxe->Signature->SignedInfo->SignatureMethod['Algorithm']; break; default: throw new Zend_InfoCard_Xml_Security_Exception("Unknown or unsupported SignatureMethod Requested"); } switch((string)$sxe->Signature->SignedInfo->Reference->DigestMethod['Algorithm']) { case self::DIGEST_METHOD_SHA1: $dMethod = (string)$sxe->Signature->SignedInfo->Reference->DigestMethod['Algorithm']; break; default: throw new Zend_InfoCard_Xml_Security_Exception("Unknown or unsupported DigestMethod Requested"); } $dValue = base64_decode((string)$sxe->Signature->SignedInfo->Reference->DigestValue, true); $signatureValue = base64_decode((string)$sxe->Signature->SignatureValue, true); $transformer = new Zend_InfoCard_Xml_Security_Transform(); foreach($sxe->Signature->SignedInfo->Reference->Transforms->children() as $transform) { $transformer->addTransform((string)$transform['Algorithm']); } $transformed_xml = $transformer->applyTransforms($strXMLInput); $transformed_xml_binhash = pack("H*", sha1($transformed_xml)); if($transformed_xml_binhash != $dValue) { throw new Zend_InfoCard_Xml_Security_Exception("Locally Transformed XML does not match XML Document. Cannot Verify Signature"); } $public_key = null; switch(true) { case isset($sxe->Signature->KeyInfo->KeyValue->X509Certificate): $certificate = (string)$sxe->Signature->KeyInfo->KeyValue->X509Certificate; $pem = "-----BEGIN CERTIFICATE-----\n" . wordwrap($certificate, 64, "\n", true) . "\n-----END CERTIFICATE-----"; $public_key = openssl_pkey_get_public($pem); if(!$public_key) { throw new Zend_InfoCard_Xml_Security_Exception("Unable to extract and prcoess X509 Certificate from KeyValue"); } break; case isset($sxe->Signature->KeyInfo->KeyValue->RSAKeyValue): if(!isset($sxe->Signature->KeyInfo->KeyValue->RSAKeyValue->Modulus) || !isset($sxe->Signature->KeyInfo->KeyValue->RSAKeyValue->Exponent)) { throw new Zend_InfoCard_Xml_Security_Exception("RSA Key Value not in Modulus/Exponent form"); } $modulus = base64_decode((string)$sxe->Signature->KeyInfo->KeyValue->RSAKeyValue->Modulus); $exponent = base64_decode((string)$sxe->Signature->KeyInfo->KeyValue->RSAKeyValue->Exponent); $pem_public_key = self::_getPublicKeyFromModExp($modulus, $exponent); $public_key = openssl_pkey_get_public ($pem_public_key); break; default: throw new Zend_InfoCard_Xml_Security_Exception("Unable to determine or unsupported representation of the KeyValue block"); } $transformer = new Zend_InfoCard_Xml_Security_Transform(); $transformer->addTransform((string)$sxe->Signature->SignedInfo->CanonicalizationMethod['Algorithm']); // The way we are doing our XML processing requires that we specifically add this // (even though it's in the <Signature> parent-block).. otherwise, our canonical form // fails signature verification $sxe->Signature->SignedInfo->addAttribute('xmlns', 'http://www.w3.org/2000/09/xmldsig#'); $canonical_signedinfo = $transformer->applyTransforms($sxe->Signature->SignedInfo->asXML()); if(@openssl_verify($canonical_signedinfo, $signatureValue, $public_key)) { return (string)$sxe->Signature->SignedInfo->Reference['URI']; } return false; }
/** * Validates the signature of a provided XML block * * @param string $strXMLInput An XML block containing a Signature * @return bool True if the signature validated, false otherwise * @throws Exception */ public static function validateXMLSignature($strXMLInput, $sts_crt = NULL) { if (!extension_loaded('openssl')) { throw new Exception("You must have the openssl extension installed to use this class"); } $sxe = simplexml_load_string($strXMLInput); if ($sts_crt != NULL) { $sxe->registerXPathNamespace('ds', 'http://www.w3.org/2000/09/xmldsig#'); list($keyValue) = $sxe->xpath("//ds:Signature/ds:KeyInfo"); $keyValue->registerXPathNamespace('ds', 'http://www.w3.org/2000/09/xmldsig#'); list($x509cert) = $keyValue->xpath("ds:X509Data/ds:X509Certificate"); list($rsaKeyValue) = $keyValue->xpath("ds:KeyValue/ds:RSAKeyValue"); //Extract the XMLToken issuer public key switch (true) { case isset($x509cert): SimpleSAML\Logger::debug("Public Key: x509cert"); $certificate = (string) $x509cert; $cert_issuer = "-----BEGIN CERTIFICATE-----\n" . wordwrap($certificate, 64, "\n", true) . "\n-----END CERTIFICATE-----"; if (!($t_key = openssl_pkey_get_public($cert_issuer))) { throw new Exception("Wrong token certificate"); } $t_det = openssl_pkey_get_details($t_key); $pem_issuer = $t_det['key']; break; case isset($rsaKeyValue): $rsaKeyValue->registerXPathNamespace('ds', 'http://www.w3.org/2000/09/xmldsig#'); list($modulus) = $rsaKeyValue->xpath("ds:Modulus"); list($exponent) = $rsaKeyValue->xpath("ds:Exponent"); if (!isset($modulus) || !isset($exponent)) { throw new Exception("RSA Key Value not in Modulus/Exponent form"); } $modulus = base64_decode((string) $modulus); $exponent = base64_decode((string) $exponent); $pem_issuer = self::_getPublicKeyFromModExp($modulus, $exponent); break; default: SimpleSAML\Logger::debug("Public Key: Unknown"); throw new Exception("Unable to determine or unsupported representation of the KeyValue block"); } //Check isuer public key against configured one $checkcert = file_get_contents($sts_crt); $check_key = openssl_pkey_get_public($checkcert); $checkData = openssl_pkey_get_details($check_key); $pem_local = $checkData['key']; if (strcmp($pem_issuer, $pem_local) != 0) { SimpleSAML\Logger::debug("Configured STS cert and received STS cert mismatch"); openssl_free_key($check_key); throw new Exception("Configured STS cert and received STS cert mismatch"); } //Validate XML signature $sxe->registerXPathNamespace('ds', 'http://www.w3.org/2000/09/xmldsig#'); list($canonMethod) = $sxe->xpath("//ds:Signature/ds:SignedInfo/ds:CanonicalizationMethod"); switch ((string) $canonMethod['Algorithm']) { case self::CANONICAL_METHOD_C14N_EXC: $cMethod = (string) $canonMethod['Algorithm']; break; default: throw new Exception("Unknown or unsupported CanonicalizationMethod Requested"); } list($signatureMethod) = $sxe->xpath("//ds:Signature/ds:SignedInfo/ds:SignatureMethod"); switch ((string) $signatureMethod['Algorithm']) { case self::SIGNATURE_METHOD_SHA1: $sMethod = (string) $signatureMethod['Algorithm']; break; default: throw new Exception("Unknown or unsupported SignatureMethod Requested"); } list($digestMethod) = $sxe->xpath("//ds:Signature/ds:SignedInfo/ds:Reference/ds:DigestMethod"); switch ((string) $digestMethod['Algorithm']) { case self::DIGEST_METHOD_SHA1: $dMethod = (string) $digestMethod['Algorithm']; break; default: throw new Exception("Unknown or unsupported DigestMethod Requested"); } $base64DecodeSupportsStrictParam = version_compare(PHP_VERSION, '5.2.0', '>='); list($digestValue) = $sxe->xpath("//ds:Signature/ds:SignedInfo/ds:Reference/ds:DigestValue"); if ($base64DecodeSupportsStrictParam) { $dValue = base64_decode((string) $digestValue, true); } else { $dValue = base64_decode((string) $digestValue); } list($signatureValueElem) = $sxe->xpath("//ds:Signature/ds:SignatureValue"); if ($base64DecodeSupportsStrictParam) { $signatureValue = base64_decode((string) $signatureValueElem, true); } else { $signatureValue = base64_decode((string) $signatureValueElem); } $transformer = new Zend_InfoCard_Xml_Security_Transform(); $transforms = $sxe->xpath("//ds:Signature/ds:SignedInfo/ds:Reference/ds:Transforms/ds:Transform"); while (list(, $transform) = each($transforms)) { $transformer->addTransform((string) $transform['Algorithm']); } $transformed_xml = $transformer->applyTransforms($strXMLInput); $transformed_xml_binhash = pack("H*", sha1($transformed_xml)); if ($transformed_xml_binhash != $dValue) { throw new Exception("Locally Transformed XML (" . $transformed_xml_binhash . ") does not match XML Document (" . $dValue . "). Cannot Verify Signature"); } $transformer = new Zend_InfoCard_Xml_Security_Transform(); $transformer->addTransform((string) $canonMethod['Algorithm']); list($signedInfo) = $sxe->xpath("//ds:Signature/ds:SignedInfo"); //SimpleSAML\Logger::debug //print ("signedinfo ".$sxe->saveXML()); $signedInfoXML = self::addNamespace($signedInfo, "http://www.w3.org/2000/09/xmldsig#"); SimpleSAML\Logger::debug("canonicalizo " . $signedInfoXML); $canonical_signedinfo = $transformer->applyTransforms($signedInfoXML); if (openssl_verify($canonical_signedinfo, $signatureValue, $check_key)) { list($reference) = $sxe->xpath("//ds:Signature/ds:SignedInfo/ds:Reference"); openssl_free_key($check_key); return (string) $reference['URI']; } else { openssl_free_key($check_key); throw new Exception("Could not validate the XML signature"); } } else { $sxe->registerXPathNamespace('ds', 'http://www.w3.org/2000/09/xmldsig#'); list($reference) = $sxe->xpath("//ds:Signature/ds:SignedInfo/ds:Reference"); return (string) $reference['URI']; } return false; }