Subversion Repositories oidplus

Rev

Rev 1130 | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. <?php
  2.  
  3. /*
  4.  * OIDplus 2.0
  5.  * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft
  6.  *
  7.  * Licensed under the Apache License, Version 2.0 (the "License");
  8.  * you may not use this file except in compliance with the License.
  9.  * You may obtain a copy of the License at
  10.  *
  11.  *     http://www.apache.org/licenses/LICENSE-2.0
  12.  *
  13.  * Unless required by applicable law or agreed to in writing, software
  14.  * distributed under the License is distributed on an "AS IS" BASIS,
  15.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16.  * See the License for the specific language governing permissions and
  17.  * limitations under the License.
  18.  */
  19.  
  20. // Works with composer.json
  21. // "robrichards/xmlseclibs": "^3.1"
  22. // or
  23. // "selective/xmldsig": "^2.4"
  24.  
  25. /**
  26.  * @param string $xml_content
  27.  * @param string $pubkey
  28.  * @return void
  29.  * @throws Exception
  30.  */
  31. function oidplus_xml_verify(string $xml_content, string $pubkey) {
  32.         require_once __DIR__.'/vendor/autoload.php';
  33.  
  34.         $sig_ok = false;
  35.         if (class_exists('\RobRichards\XMLSecLibs\XMLSecurityDSig')) {
  36.                 // Template: https://github.com/robrichards/xmlseclibs/blob/master/tests/xmlsec-verify.phpt
  37.  
  38.                 $doc = new DOMDocument();
  39.                 $doc->loadXML($xml_content);
  40.  
  41.                 $objXMLSecDSig = new \RobRichards\XMLSecLibs\XMLSecurityDSig();
  42.  
  43.                 $objDSig = $objXMLSecDSig->locateSignature($doc);
  44.                 if (! $objDSig) {
  45.                         throw new Exception("Cannot locate Signature Node");
  46.                 }
  47.                 $objXMLSecDSig->canonicalizeSignedInfo();
  48.                 $objXMLSecDSig->idKeys = array('wsu:Id');
  49.                 $objXMLSecDSig->idNS = array('wsu'=>'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd');
  50.  
  51.                 $retVal = $objXMLSecDSig->validateReference();
  52.                 if (! $retVal) {
  53.                         throw new Exception("Reference Validation Failed");
  54.                 }
  55.  
  56.                 $objKey = $objXMLSecDSig->locateKey();
  57.                 if (! $objKey ) {
  58.                         throw new Exception("We have no idea about the key");
  59.                 }
  60.                 /*
  61.                 $key = NULL;
  62.  
  63.                 $objKeyInfo = \RobRichards\XMLSecLibs\XMLSecEnc::staticLocateKeyInfo($objKey, $objDSig);
  64.                 if (! $objKeyInfo->key && empty($key)) {
  65.                         $objKey->loadKey($pubkey, false);
  66.                 }
  67.                 */
  68.                 $objKey->loadKey($pubkey, /*isfile*/false);
  69.  
  70.                 $sig_ok = $objXMLSecDSig->verify($objKey) === 1;
  71.         } else if (class_exists("\Selective\XmlDSig\XmlSignatureValidator")) {
  72.                 $signatureValidator = new \Selective\XmlDSig\XmlSignatureValidator();
  73.                 $signatureValidator->loadPublicKey($pubkey);
  74.                 $sig_ok = $signatureValidator->verifyXml($xml_content);
  75.         } else {
  76.                 assert(false);
  77.         }
  78.  
  79.         if (!$sig_ok) {
  80.                 throw new Exception("Signature verification failed!");
  81.         }
  82. }
  83.  
  84. /**
  85.  * @param string $xml_content
  86.  * @param string $privkey
  87.  * @param string $pubkey
  88.  * @return false|string|void
  89.  * @throws Exception
  90.  */
  91. function oidplus_xml_sign(string $xml_content, string $privkey, string $pubkey) {
  92.         require_once __DIR__.'/vendor/autoload.php';
  93.  
  94.         if (class_exists('\RobRichards\XMLSecLibs\XMLSecurityDSig')) {
  95.                 // Template: https://github.com/robrichards/xmlseclibs/blob/master/README.md
  96.  
  97.                 // Load the XML to be signed
  98.                 $doc = new DOMDocument();
  99.                 $doc->loadXML($xml_content);
  100.  
  101.                 // Create a new Security object
  102.                 $objDSig = new \RobRichards\XMLSecLibs\XMLSecurityDSig();
  103.                 // Use the c14n exclusive canonicalization
  104.                 $objDSig->setCanonicalMethod(\RobRichards\XMLSecLibs\XMLSecurityDSig::EXC_C14N);
  105.                 // Sign using SHA-512
  106.                 $objDSig->addReference(
  107.                     $doc,
  108.                     \RobRichards\XMLSecLibs\XMLSecurityDSig::SHA512,
  109.                     array('http://www.w3.org/2000/09/xmldsig#enveloped-signature')
  110.                 );
  111.  
  112.                 // Create a new (private) Security key
  113.                 $objKey = new \RobRichards\XMLSecLibs\XMLSecurityKey(\RobRichards\XMLSecLibs\XMLSecurityKey::RSA_SHA512, array('type'=>'private'));
  114.                 /*
  115.                 If key has a passphrase, set it using
  116.                 $objKey->passphrase = '<passphrase>';
  117.                 */
  118.                 // Load the private key
  119.                 $objKey->loadKey($privkey, /*isfile*/false);
  120.  
  121.                 // Sign the XML file
  122.                 $objDSig->sign($objKey);
  123.  
  124.                 // TODO: Selective\XmlDSig has "KeyInfo" with RSAKeyValue (showing Modulus and Exponent)
  125.                 //       while RobRichards\XMLSecLibs has no "KeyInfo".
  126.                 //       We can't use $objDSig->add509Cert, because we have no X.509 certificate, just pubkey and privkey...
  127.  
  128.                 // Append the signature to the XML
  129.                 $objDSig->appendSignature($doc->documentElement);
  130.  
  131.                 // Save the signed XML
  132.                 $xml_signed = $doc->saveXML();
  133.         } else if (class_exists("\Selective\XmlDSig\XmlSigner")) {
  134.                 $xmlSigner = new \Selective\XmlDSig\XmlSigner();
  135.                 $xmlSigner->loadPrivateKey($privkey, '');
  136.                 $xmlSigner->setReferenceUri(''); // Optional: Set reference URI (TODO?)
  137.  
  138.                 /* @phpstan-ignore-next-line */
  139.                 $xml_signed = $xmlSigner->signXml($xml_content, \Selective\XmlDSig\DigestAlgorithmType::SHA512);
  140.         } else {
  141.                 assert(false);
  142.                 return false;
  143.         }
  144.  
  145.         oidplus_xml_verify($xml_signed, $pubkey);
  146.         return $xml_signed;
  147. }
  148.