SignatureValidator Class
The SignatureValidator class is responsible for validating digital signatures, both for general data and specifically for XML documents that follow the XML-DSIG standard.
Overview
The SignatureValidator implements the SignatureValidatorInterface and provides mechanisms to:
- Validate signatures for any data against a public key.
- Validate signatures in XML documents.
- Extract and parse signature nodes from XML documents.
- Validate specific aspects of XML signatures (digest values and signature values).
This class is a core component of the Derafu Signature library and is typically used through the SignatureService facade.
Basic Usage
Initialization
use Derafu\Signature\Service\SignatureGenerator;
use Derafu\Signature\Service\SignatureValidator;
use Derafu\Xml\Service\XmlDecoder;
use Derafu\Xml\Service\XmlEncoder;
use Derafu\Xml\Service\XmlService;
use Derafu\Xml\Service\XmlValidator;
// Initialize required XML services.
$xmlEncoder = new XmlEncoder();
$xmlDecoder = new XmlDecoder();
$xmlValidator = new XmlValidator();
$xmlService = new XmlService($xmlEncoder, $xmlDecoder, $xmlValidator);
// Create the signature generator (needed by validator).
$generator = new SignatureGenerator($xmlService);
// Create the signature validator.
$validator = new SignatureValidator($generator, $xmlService);
The validator verifies through the OpenSSL service of derafu/certificate. It is the optional third argument of the constructor: without it, the validator uses OpenSsl.
Validating Data Signatures
// Validate a signature for some data.
$data = 'Data that was signed';
$signature = 'base64EncodedSignature';
$publicKey = 'publicKeyPEM';
$isValid = $validator->validate($data, $signature, $publicKey);
// Optional: Specify the signature algorithm.
$isValid = $validator->validate($data, $signature, $publicKey, OPENSSL_ALGO_SHA256);
if ($isValid) {
echo "Signature is valid!";
} else {
echo "Signature is invalid!";
}
Validating XML Signatures
use Derafu\Signature\Exception\SignatureException;
// Validate an XML document with a signature.
$signedXml = file_get_contents('signed_document.xml');
try {
// One result for each signature of the XML.
foreach ($validator->validateXml($signedXml) as $result) {
if ($result->isValid()) {
echo "XML signature is valid!";
} else {
echo "XML signature is not valid: " . $result->getError()?->getMessage();
}
}
} catch (SignatureException $e) {
// The XML is malformed or it has no signatures.
echo "XML signature validation failed: " . $e->getMessage();
}
A signature that is not valid is a result, not an exception: validateXml() throws only if the XML is malformed or has no signatures, or if it can not be canonicalized (see below).
Working with Signature Nodes
// Extract and parse a signature node from XML.
$signatureXml = '<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">...</Signature>';
$signatureNode = $validator->createSignatureNode($signatureXml);
// Access signature components.
$reference = $signatureNode->getReference();
$digestValue = $signatureNode->getDigestValue();
$signatureValue = $signatureNode->getSignatureValue();
$x509Certificate = $signatureNode->getX509Certificate();
Detailed XML Signature Validation
// Create a signature node from a signed XML document.
$signatureNode = $validator->createSignatureNode($signatureXml);
// Validate just the digest value (content integrity).
try {
$validator->validateXmlDigestValue($xmlDoc, $signatureNode);
echo "Content integrity verified!";
} catch (SignatureException $e) {
echo "Content may have been tampered with: " . $e->getMessage();
}
// Validate just the signature value (signer authenticity).
try {
$validator->validateXmlSignatureValue($signatureNode);
echo "Signature authenticity verified!";
} catch (SignatureException $e) {
echo "Signature validation failed: " . $e->getMessage();
}
API Reference
Data Signature Validation
/**
* Validate the digital signature of data.
*
* @param string $data Data to be verified.
* @param string $signature Digital signature of the data in base64.
* @param string $publicKey Public key of the signature of the data.
* @param string|int $signatureAlgorithm Algorithm used to sign (default SHA1).
* @return bool `true` if the signature is valid, `false` if it is invalid.
* @throws SignatureException If there was an error while validating.
*/
public function validate(
string $data,
string $signature,
string $publicKey,
string|int $signatureAlgorithm = OPENSSL_ALGO_SHA1
): bool;
XML Signature Validation
/**
* Validate the validity of an XML signature using RSA and SHA1.
*
* @param XmlDocumentInterface|string $xml XML string to be validated.
* @return array<SignatureValidationResult> One result per signature node.
* @throws SignatureException If the XML is malformed or has no signatures.
*/
public function validateXml(XmlDocumentInterface|string $xml): array;
Signature Node Handling
/**
* Creates the `Signature` instance from a string XML with the signature node.
*
* @param string $xml String with the XML of the `Signature` node.
* @return SignatureInterface
*/
public function createSignatureNode(string $xml): SignatureInterface;
Detailed Validation Methods
/**
* Validate the DigestValue of the signed data.
*
* @param XmlDocumentInterface|string $xml Document to be validated.
* @param SignatureInterface $signatureNode Signature node to be validated.
* @return void
* @throws SignatureException If the DigestValue is invalid.
*/
public function validateXmlDigestValue(
XmlDocumentInterface|string $xml,
SignatureInterface $signatureNode
): void;
/**
* Validate the signature of the `SignedInfo` node of the XML using the X509
* certificate.
*
* @param SignatureInterface $signatureNode Signature node to be validated.
* @throws SignatureException If the XML signature is invalid.
*/
public function validateXmlSignatureValue(
SignatureInterface $signatureNode
): void;
Implementation Details
Signature Validation Process
For general data signatures, the validation process is as follows:
- The public key is normalized (headers and footers are added if missing).
- The base64-encoded signature is decoded to binary.
- The OpenSSL service verifies the signature with the data, the decoded signature and the certificate.
- If the signature is correct, the result is
true; if it is not, it isfalse(a result, not an error); if the verification itself fails (a certificate that can not be read, an algorithm that is not known), aSignatureExceptionis thrown.
An XML that Can Not Be Canonicalized
The XML is canonicalized (C14N) to calculate and to check the digest and the signature. If libxml can not do it (for example, when a namespace of the XML is a relative URI, which the Canonical XML recommendation does not allow), the XmlException of derafu/xml is thrown: The XML could not be canonicalized., with what libxml said in getErrors(). It is an exception and not an invalid signature, because the XML can not be validated.
The errors of OpenSSL of the verification that failed are in getErrors() of that exception, as translatable messages. A signature that is not valid leaves nothing behind: the OpenSSL service empties the errors that OpenSSL leaves for it, so they are not in the errors of any other exception.
XML Signature Validation Process
For XML signatures, the validation process follows the XML-DSIG standard:
- The XML document is loaded and parsed.
- All
Signatureelements in the document are located. - For each signature element:
- A
Signaturenode object is created from the element’s XML. - The digest value is validated to ensure content integrity.
- The signature value is validated to ensure signer authenticity.
- A
Digest Value Validation
The digest value validation ensures that the content being signed hasn’t been modified:
- The XML document is loaded.
- The reference from the signature node is extracted (if any).
- The digest value is calculated for the referenced content or the entire document.
- The calculated digest value is compared to the one in the signature.
- If they don’t match, a
SignatureExceptionis thrown.
Signature Value Validation
The signature value validation ensures that the signature was created with the corresponding private key:
- The
SignedInfoelement is extracted and canonicalized. - The base64-encoded signature value is obtained from the
SignatureValueelement. - The public key is extracted from the
X509Certificateelement. - The signature is validated using the canonicalized
SignedInfo, the signature value, and the public key. - If the validation fails, a
SignatureExceptionis thrown.
Error Handling
The SignatureValidator provides detailed error messages when validation fails:
- For data signatures, it indicates when an error occurred during validation.
- For XML signatures, it indicates whether the problem is with the digest value (content integrity) or the signature value (signer authenticity).
- For missing signatures or malformed XML, it provides clear error messages.
These detailed error messages help diagnose the exact cause of validation failures.
Dependency on SignatureGenerator
The SignatureValidator requires an instance of SignatureGeneratorInterface to calculate digest values for XML documents. This dependency ensures that the same digest calculation algorithm is used for both signing and validating.
XML Handling
The class uses the Derafu XML library for XML operations:
- Loading and parsing XML documents.
- Extracting signature elements.
- Canonicalizing XML for digest and signature validation.
- Converting between XML and PHP arrays.
This integration ensures consistent handling of XML across the library.