Up to signing xml

This commit is contained in:
David Bomba 2025-04-25 14:52:07 +10:00
parent 1f4fae314c
commit dd60eb3b58
10 changed files with 703 additions and 23 deletions

View File

@ -2,6 +2,10 @@
namespace App\Services\EDocument\Standards\Verifactu\Models;
use RobRichards\XMLSecLibs\XMLSecurityDSig;
use RobRichards\XMLSecLibs\XMLSecurityKey;
use Illuminate\Support\Facades\Log;
class Invoice extends BaseXmlModel
{
protected string $idVersion;
@ -36,6 +40,9 @@ class Invoice extends BaseXmlModel
protected string $huella;
protected ?string $signature = null;
protected ?FacturaRectificativa $facturaRectificativa = null;
protected ?string $privateKeyPath = null;
protected ?string $publicKeyPath = null;
protected ?string $certificatePath = null;
public function __construct()
{
@ -411,6 +418,117 @@ class Invoice extends BaseXmlModel
$this->facturaRectificativa = $facturaRectificativa;
}
public function setPrivateKeyPath(string $path): self
{
$this->privateKeyPath = $path;
return $this;
}
public function setPublicKeyPath(string $path): self
{
$this->publicKeyPath = $path;
return $this;
}
public function setCertificatePath(string $path): self
{
$this->certificatePath = $path;
return $this;
}
protected function signXml(\DOMDocument $doc): void
{
if (!$this->privateKeyPath || !file_exists($this->privateKeyPath)) {
throw new \RuntimeException('Private key not found or not set');
}
if (!$this->certificatePath || !file_exists($this->certificatePath)) {
throw new \RuntimeException('Certificate not found or not set');
}
Log::info('Starting XML signing process');
Log::debug('XML before signing: ' . $doc->saveXML());
try {
// Create a new Security object
Log::debug('Creating XMLSecurityDSig object');
$objDSig = new XMLSecurityDSig();
// Set canonicalization method
Log::debug('Setting canonicalization method');
$objDSig->setCanonicalMethod(XMLSecurityDSig::EXC_C14N);
// Create a new Security key
Log::debug('Creating XMLSecurityKey object');
$objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA256, ['type' => 'private']);
// Load the private key
Log::debug('Loading private key from: ' . $this->privateKeyPath);
$objKey->loadKey($this->privateKeyPath, true);
// Add reference
Log::debug('Adding reference to document');
$objDSig->addReference(
$doc,
XMLSecurityDSig::SHA256,
['http://www.w3.org/2000/09/xmldsig#enveloped-signature']
);
Log::debug('Added reference to document');
// Add the certificate
Log::debug('Adding certificate');
$objDSig->add509Cert(file_get_contents($this->certificatePath));
// Sign the XML document
Log::debug('Signing document');
$objDSig->sign($objKey);
// Append the signature to the XML
Log::debug('Appending signature');
$objDSig->appendSignature($doc->documentElement);
Log::debug('XML after signing: ' . $doc->saveXML());
} catch (\Exception $e) {
Log::error('Error during XML signing: ' . $e->getMessage());
Log::error('Stack trace: ' . $e->getTraceAsString());
throw $e;
}
}
public function verifySignature(\DOMDocument $doc): bool
{
if (!$this->publicKeyPath || !file_exists($this->publicKeyPath)) {
throw new \RuntimeException('Public key not found or not set');
}
// Get the signature node
$objXMLSecDSig = new XMLSecurityDSig();
// Locate the signature
$objDSig = $objXMLSecDSig->locateSignature($doc);
if (!$objDSig) {
throw new \RuntimeException('Signature not found in document');
}
// Canonicalize the signed info
$objXMLSecDSig->canonicalizeSignedInfo();
// Validate references
$objXMLSecDSig->validateReference();
// Get the key from the certificate
$objKey = $objXMLSecDSig->locateKey();
if (!$objKey) {
throw new \RuntimeException('Key not found in signature');
}
// Load the public key
$objKey->loadKey($this->publicKeyPath, false, true);
// Verify the signature
return $objXMLSecDSig->verify($objKey) === 1;
}
public function toXml(): string
{
// Validate required fields first, outside of try-catch
@ -439,8 +557,8 @@ class Invoice extends BaseXmlModel
$doc->formatOutput = true;
// Create root element with proper namespaces
$root = $doc->createElementNS(self::XML_NAMESPACE, self::XML_NAMESPACE_PREFIX . ':RegistroAlta');
$root->setAttribute('xmlns:ds', self::XML_DS_NAMESPACE);
$root = $doc->createElementNS(parent::XML_NAMESPACE, parent::XML_NAMESPACE_PREFIX . ':RegistroAlta');
$root->setAttribute('xmlns:ds', parent::XML_DS_NAMESPACE);
$doc->appendChild($root);
// Add required elements in exact order according to schema
@ -617,12 +735,19 @@ class Invoice extends BaseXmlModel
$root->appendChild($signatureElement);
}
$xml = $doc->saveXML($root);
// error_log("Generated XML: " . $xml);
return $xml;
// Sign the document if private key is set
if ($this->privateKeyPath !== null) {
try {
$this->signXml($doc);
} catch (\Exception $e) {
throw new \RuntimeException('Failed to sign XML: ' . $e->getMessage());
}
}
return $doc->saveXML();
} catch (\Exception $e) {
error_log("Error in toXml: " . $e->getMessage());
throw $e;
throw new \RuntimeException("Error in toXml: " . $e->getMessage(), 0, $e);
}
}

View File

@ -94,6 +94,7 @@
"pusher/pusher-php-server": "^7.2",
"quickbooks/v3-php-sdk": "^6.2",
"razorpay/razorpay": "2.*",
"robrichards/xmlseclibs": "^3.1",
"sentry/sentry-laravel": "^4",
"setasign/fpdf": "^1.8",
"setasign/fpdi": "^2.3",

54
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "3469affb27a6e61c80c53267c6ca2889",
"content-hash": "df5805c56ea07b9fe22be83885c67387",
"packages": [
{
"name": "adrienrn/php-mimetyper",
@ -5169,16 +5169,16 @@
},
{
"name": "laravel/framework",
"version": "v11.44.2",
"version": "v11.44.5",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "f85216c82cbd38b66d67ebd20ea762cb3751a4b4"
"reference": "819555ccc7e88ea92945ea785ef9f0fa9b6d4931"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/f85216c82cbd38b66d67ebd20ea762cb3751a4b4",
"reference": "f85216c82cbd38b66d67ebd20ea762cb3751a4b4",
"url": "https://api.github.com/repos/laravel/framework/zipball/819555ccc7e88ea92945ea785ef9f0fa9b6d4931",
"reference": "819555ccc7e88ea92945ea785ef9f0fa9b6d4931",
"shasum": ""
},
"require": {
@ -5380,7 +5380,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2025-03-12T14:34:30+00:00"
"time": "2025-04-24T13:59:11+00:00"
},
{
"name": "laravel/octane",
@ -11027,6 +11027,48 @@
},
"time": "2025-01-21T10:13:31+00:00"
},
{
"name": "robrichards/xmlseclibs",
"version": "3.1.3",
"source": {
"type": "git",
"url": "https://github.com/robrichards/xmlseclibs.git",
"reference": "2bdfd742624d739dfadbd415f00181b4a77aaf07"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/robrichards/xmlseclibs/zipball/2bdfd742624d739dfadbd415f00181b4a77aaf07",
"reference": "2bdfd742624d739dfadbd415f00181b4a77aaf07",
"shasum": ""
},
"require": {
"ext-openssl": "*",
"php": ">= 5.4"
},
"type": "library",
"autoload": {
"psr-4": {
"RobRichards\\XMLSecLibs\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"description": "A PHP library for XML Security",
"homepage": "https://github.com/robrichards/xmlseclibs",
"keywords": [
"security",
"signature",
"xml",
"xmldsig"
],
"support": {
"issues": "https://github.com/robrichards/xmlseclibs/issues",
"source": "https://github.com/robrichards/xmlseclibs/tree/3.1.3"
},
"time": "2024-11-20T21:13:56+00:00"
},
{
"name": "sabberworm/php-css-parser",
"version": "v8.8.0",

View File

@ -2,7 +2,7 @@
namespace Tests\Feature\EInvoice\Verifactu\Models;
use PHPUnit\Framework\TestCase;
use Tests\TestCase;
use App\Services\EDocument\Standards\Verifactu\Models\BaseXmlModel;
abstract class BaseModelTest extends TestCase

View File

@ -789,10 +789,9 @@ class InvoiceTest extends BaseModelTest
->setImporteTotal(100.00)
->setFechaHoraHusoGenRegistro('2023-01-01T12:00:00')
->setTipoHuella('01')
->setHuella('abc123...')
->setSignature('MOCK_SIGNATURE_VALUE');
->setHuella('abc123...');
// Add sistema informatico
// Add sistema informatico with all required fields
$sistema = new SistemaInformatico();
$sistema
->setNombreRazon('Sistema de Facturación')
@ -800,15 +799,74 @@ class InvoiceTest extends BaseModelTest
->setNombreSistemaInformatico('SistemaFacturacion')
->setIdSistemaInformatico('01')
->setVersion('1.0')
->setNumeroInstalacion('INST-001');
->setNumeroInstalacion('INST-001')
->setTipoUsoPosibleSoloVerifactu('S')
->setTipoUsoPosibleMultiOT('S')
->setIndicadorMultiplesOT('S');
$invoice->setSistemaInformatico($sistema);
$xml = $invoice->toXml();
$this->assertValidatesAgainstXsd($xml, $this->getTestXsdPath());
// Add desglose with proper structure
$desglose = new Desglose();
$desglose->setDesgloseIVA([
'Impuesto' => '01',
'ClaveRegimen' => '02',
'CalificacionOperacion' => 'S2',
'BaseImponible' => 100.00,
'TipoImpositivo' => 21,
'Cuota' => 21.00
]);
$invoice->setDesglose($desglose);
// Test deserialization
$deserialized = Invoice::fromXml($xml);
$this->assertEquals('MOCK_SIGNATURE_VALUE', $deserialized->getSignature());
// Add encadenamiento (required)
$encadenamiento = new Encadenamiento();
$encadenamiento->setPrimerRegistro('S');
$invoice->setEncadenamiento($encadenamiento);
// Set up paths for certificates
$certsPath = dirname(__DIR__) . '/certs/';
$privateKeyPath = $certsPath . 'private.pem';
$publicKeyPath = $certsPath . 'public.pem';
$certificatePath = $certsPath . 'certificate.pem';
// Check if certificate files exist and are readable
foreach (['private.pem', 'public.pem', 'certificate.pem'] as $file) {
$path = $certsPath . $file;
echo "\n$file: ";
if (!file_exists($path)) {
echo "MISSING";
throw new \RuntimeException("Certificate file $file does not exist at $path");
}
if (!is_readable($path)) {
echo "NOT READABLE";
throw new \RuntimeException("Certificate file $file is not readable at $path");
}
echo "OK (size: " . filesize($path) . " bytes)";
}
// Set the keys
$invoice->setPrivateKeyPath($privateKeyPath)
->setPublicKeyPath($publicKeyPath)
->setCertificatePath($certificatePath);
// Generate signed XML
$xml = $invoice->toXml();
// Debug output
echo "\nGenerated XML with signature:\n";
echo $xml;
echo "\n\n";
// Load the XML into a DOMDocument for verification
$doc = new \DOMDocument();
$doc->loadXML($xml);
// Verify the signature
$this->assertTrue($invoice->verifySignature($doc));
// Skip schema validation for signed XML since the signature schema is not part of the core invoice schema
// Instead, validate the XML before signing
$unsignedXml = $invoice->toXml(false);
$this->assertValidatesAgainstXsd($unsignedXml, $this->getTestXsdPath());
}
public function testCreateAndSerializeInvoiceWithAgreementData(): void
@ -1140,4 +1198,78 @@ class InvoiceTest extends BaseModelTest
throw new \DOMException('XML does not validate against schema');
}
}
public function testSignatureGeneration(): void
{
$invoice = new Invoice();
$invoice->setIdVersion('1.0')
->setIdFactura('TEST123')
->setNombreRazonEmisor('Test Company')
->setTipoFactura('F1')
->setDescripcionOperacion('Test Operation')
->setCuotaTotal(100.00)
->setImporteTotal(121.00)
->setFechaHoraHusoGenRegistro(date('Y-m-d\TH:i:s'))
->setTipoHuella('SHA-256')
->setHuella(hash('sha256', 'test'));
// Set up the desglose
$desglose = new Desglose();
$desglose->setDesgloseIVA([
'Impuesto' => 'IVA',
'ClaveRegimen' => '01',
'BaseImponible' => 100.00,
'TipoImpositivo' => 21.00,
'Cuota' => 21.00
]);
$invoice->setDesglose($desglose);
// Set up encadenamiento
$encadenamiento = new Encadenamiento();
$encadenamiento->setPrimerRegistro('1');
$invoice->setEncadenamiento($encadenamiento);
// Set up sistema informatico
$sistemaInformatico = new SistemaInformatico();
$sistemaInformatico->setNombreRazon('Test System')
->setNif('12345678Z')
->setNombreSistemaInformatico('Test Software')
->setIdSistemaInformatico('TEST001')
->setVersion('1.0')
->setNumeroInstalacion('001')
->setTipoUsoPosibleSoloVerifactu('S')
->setTipoUsoPosibleMultiOT('S')
->setIndicadorMultiplesOT('S');
$invoice->setSistemaInformatico($sistemaInformatico);
// Set up signature keys
$privateKeyPath = dirname(__DIR__) . '/certs/private.pem';
$publicKeyPath = dirname(__DIR__) . '/certs/public.pem';
$certificatePath = dirname(__DIR__) . '/certs/certificate.pem';
// Set the keys
$invoice->setPrivateKeyPath($privateKeyPath)
->setPublicKeyPath($publicKeyPath)
->setCertificatePath($certificatePath);
// Generate signed XML
$xml = $invoice->toXml();
// Debug output
echo "\nGenerated XML with signature:\n";
echo $xml;
echo "\n\n";
// Load the XML into a DOMDocument for verification
$doc = new \DOMDocument();
$doc->loadXML($xml);
// Verify the signature
$this->assertTrue($invoice->verifySignature($doc));
// Validate against schema
$this->assertValidatesAgainstXsd($xml, $this->getTestXsdPath());
// Clean up test keys
}
}

View File

@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIEITCCAwmgAwIBAgIULRklu/Ae+uWUdejwvfO7wKhs0lIwDQYJKoZIhvcNAQEL
BQAwgZ8xCzAJBgNVBAYTAkVTMQ8wDQYDVQQIDAZNYWRyaWQxDzANBgNVBAcMBk1h
ZHJpZDEaMBgGA1UECgwRVGVzdCBPcmdhbml6YXRpb24xFjAUBgNVBAsMDUlUIERl
cGFydG1lbnQxGTAXBgNVBAMMEHRlc3QuZXhhbXBsZS5jb20xHzAdBgkqhkiG9w0B
CQEWEHRlc3RAZXhhbXBsZS5jb20wHhcNMjUwNDI1MDQxMzA2WhcNMjYwNDI1MDQx
MzA2WjCBnzELMAkGA1UEBhMCRVMxDzANBgNVBAgMBk1hZHJpZDEPMA0GA1UEBwwG
TWFkcmlkMRowGAYDVQQKDBFUZXN0IE9yZ2FuaXphdGlvbjEWMBQGA1UECwwNSVQg
RGVwYXJ0bWVudDEZMBcGA1UEAwwQdGVzdC5leGFtcGxlLmNvbTEfMB0GCSqGSIb3
DQEJARYQdGVzdEBleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBALZExtAsqspmBMot0/D1Ska36SaEEIZurdgh5odE2jcKWubJQegu4tAu
Eq8LbL6bw8qmJZ6txp3fknIK3RjSyT39Rk0KtpGvrVjFPLAD42UNr8fVspPDHUR9
yUvSpXoDxtcL6y6pn08jNnBFHkSnQK9br5wiNXWhSDkRTKfJ6Jvc269dA1qcJ2jU
S1k5TXULhZwwvjeTFKb9+xwRjdnLlyh4+WSUvwUMDFgXeKn1iQppZ0Oc+G4Xyq8W
oViOwruNMT3iUN83I3wKk3NOkdbx/IxD87UB19mKdmV4X1Nlj+m+/gidDf2DmriM
FtnPp3kPaqwWBB+rV4AggwnKAOwfmUECAwEAAaNTMFEwHQYDVR0OBBYEFLR1jHET
y8qVyFzsD9Ui0466XsLqMB8GA1UdIwQYMBaAFLR1jHETy8qVyFzsD9Ui0466XsLq
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAIllEMRCi/n6bqfv
3eMuzHBxvUNNjmTIbEfnPyjf81FB78e1V3mXE5cYcVbDOwbvzZKa6qcioG88iW8Y
5b9RnLQjy9os7xhne1BJq3J2B6XKlHv9O1f5x1fb6nZ/8m7XyGmXctIGs3K29/sF
zFqx7UgSujZCtKannnjS7RCMTbCixhxVbgwBRSkTLBxNWssHaJ7q4ktrfKdXSHAm
BhfgrvbILYYYN38i7Hfwru7m6Uoo7I+sy8yfHZzm3DwvswVRpRFIWLtkc6PBbyE4
xXWjoJrbFGyzSdLg+DX5SpJ4569Jueuj2LiDCsJ3r1whlUO/gDfB3jdrrUrkxrBq
mmW22K8=
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC2RMbQLKrKZgTK
LdPw9UpGt+kmhBCGbq3YIeaHRNo3ClrmyUHoLuLQLhKvC2y+m8PKpiWercad35Jy
Ct0Y0sk9/UZNCraRr61YxTywA+NlDa/H1bKTwx1EfclL0qV6A8bXC+suqZ9PIzZw
RR5Ep0CvW6+cIjV1oUg5EUynyeib3NuvXQNanCdo1EtZOU11C4WcML43kxSm/fsc
EY3Zy5coePlklL8FDAxYF3ip9YkKaWdDnPhuF8qvFqFYjsK7jTE94lDfNyN8CpNz
TpHW8fyMQ/O1AdfZinZleF9TZY/pvv4InQ39g5q4jBbZz6d5D2qsFgQfq1eAIIMJ
ygDsH5lBAgMBAAECggEAC732iOa3wf48hMHbC4Th2hhy/rY3UlHDBU95yHEZFb6n
CIMiqdCLcBnnvjsgME9cl6uIdOaBCx6iEpK8l6LMsB+m7cOo603D/xoFxNkbRyFQ
l7EepgJF2mm4FBhn7KpdnvD3n7PxvWlpUmZBgu67bhCcCZTymLdhLbv9kjmhsJi3
qqlygaWUaj91yQFLJ4PeTnTMRd4yVFvP9IDGxgTQzDzlMQz1uAu52dunB+TkLkJI
/MgbhC5/bAWmgqgmbNjhFkQgwdG787jstXtMvLBGnYNhBVzuqr4XHh31pKFhl+z3
hG1Hi3yg+XzKVSS7wb+qMv38dGVZYSTSa06vs03mWQKBgQD8cbcGu52iFi1h/9xY
ge1EmIiT9SrYIaYhzrPFdzeY7jpaXfXYZ+nPyrTs6nRxW/RsHP1YxM5WUTE23zhL
g9W1NWATHKh1n+QAwHQdYZWJN1LyqCW5q3fl0koAQGuXIMqKpv3PR+4pyhxdGNC9
MnPTLQmWkIMA7RBAizNN3aXaYwKBgQC41gRDf1Zv/Wg2kc8u3hOX0tbuT9bxium3
kfXmDEJUQ9fww8rA9ZKo3weKMvUklmrXEbLjMN2Md8hmdBVHQXXhtXyZzoiCHVct
DNW2TvqBxpJCnLA3bHr8fgPSAHs7SGAM8gF5mY9ZhPI4+Drjk6YlOHVcEr82C1nG
jJbCdVYdCwKBgFdO1OglNy150hRUs1aBCRhyULorTrgVWynSHWasBrSDn/blDEPe
HIVbLBvMMp2KGgzSMeTjnCFKT2UU6pljbSQQ47t4a+LSe76u0PngaCFe2vdFpFaE
sSDxC9rubMeF9cbiXmG0FPCrEAg6rubgbiKZLvm93TESzE4mVoYVpGjVAoGBAKrG
0tmqZarDb+47ejnLZj74xy0ZB3fU6Wx6p8ANw5sns/T4cfUm8IKmzsiZnHZZpA1i
hO300D5gzgAbcS2NBeWtUZqqgOX3RfyRx0PSZRJS7gAt1YLf8CIqLE1ztGhpzpUn
ZMV9ZD1J4KNSdtaeLPxm1chkadb9Vc1lSEYTM6VRAoGAIcA1aEllKEOWXeZAUfny
l67WA0ua6ss2dPQWJumflcf73jajE+jZQagsnRIJv34jJaP/O5A5OKU1WnemKlVU
fyBd4R4nr47yCLxk2HNwUQBluG6EIeUbdEM/2P6qAamIG4FzurvTKIZ7h2dgvtME
RaRkhB6Q8tXUwjQ+HfCpw9Q=
-----END PRIVATE KEY-----

View File

@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtkTG0CyqymYEyi3T8PVK
RrfpJoQQhm6t2CHmh0TaNwpa5slB6C7i0C4SrwtsvpvDyqYlnq3Gnd+ScgrdGNLJ
Pf1GTQq2ka+tWMU8sAPjZQ2vx9Wyk8MdRH3JS9KlegPG1wvrLqmfTyM2cEUeRKdA
r1uvnCI1daFIORFMp8nom9zbr10DWpwnaNRLWTlNdQuFnDC+N5MUpv37HBGN2cuX
KHj5ZJS/BQwMWBd4qfWJCmlnQ5z4bhfKrxahWI7Cu40xPeJQ3zcjfAqTc06R1vH8
jEPztQHX2Yp2ZXhfU2WP6b7+CJ0N/YOauIwW2c+neQ9qrBYEH6tXgCCDCcoA7B+Z
QQIDAQAB
-----END PUBLIC KEY-----

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:sf="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd" targetNamespace="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd" elementFormDefault="qualified">
<import namespace="http://www.w3.org/2000/09/xmldsig#" schemaLocation="http://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd"/>
<import namespace="http://www.w3.org/2000/09/xmldsig#" schemaLocation="xmldsig/xmldsig-core-schema.xsd"/>
<complexType name="CabeceraType">
<annotation>
<documentation xml:lang="es"> Datos de cabecera </documentation>

View File

@ -0,0 +1,318 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE schema
PUBLIC "-//W3C//DTD XMLSchema 200102//EN" "http://www.w3.org/2001/XMLSchema.dtd"
[
<!ATTLIST schema
xmlns:ds CDATA #FIXED "http://www.w3.org/2000/09/xmldsig#">
<!ENTITY dsig 'http://www.w3.org/2000/09/xmldsig#'>
<!ENTITY % p ''>
<!ENTITY % s ''>
]>
<!-- Schema for XML Signatures
http://www.w3.org/2000/09/xmldsig#
$Revision: 1.1 $ on $Date: 2002/02/08 20:32:26 $ by $Author: reagle $
Copyright 2001 The Internet Society and W3C (Massachusetts Institute
of Technology, Institut National de Recherche en Informatique et en
Automatique, Keio University). All Rights Reserved.
http://www.w3.org/Consortium/Legal/
This document is governed by the W3C Software License [1] as described
in the FAQ [2].
[1] http://www.w3.org/Consortium/Legal/copyright-software-19980720
[2] http://www.w3.org/Consortium/Legal/IPR-FAQ-20000620.html#DTD
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
targetNamespace="http://www.w3.org/2000/09/xmldsig#"
version="0.1" elementFormDefault="qualified">
<!-- Basic Types Defined for Signatures -->
<simpleType name="CryptoBinary">
<restriction base="base64Binary">
</restriction>
</simpleType>
<!-- Start Signature -->
<element name="Signature" type="ds:SignatureType"/>
<complexType name="SignatureType">
<sequence>
<element ref="ds:SignedInfo"/>
<element ref="ds:SignatureValue"/>
<element ref="ds:KeyInfo" minOccurs="0"/>
<element ref="ds:Object" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="Id" type="ID" use="optional"/>
</complexType>
<element name="SignatureValue" type="ds:SignatureValueType"/>
<complexType name="SignatureValueType">
<simpleContent>
<extension base="base64Binary">
<attribute name="Id" type="ID" use="optional"/>
</extension>
</simpleContent>
</complexType>
<!-- Start SignedInfo -->
<element name="SignedInfo" type="ds:SignedInfoType"/>
<complexType name="SignedInfoType">
<sequence>
<element ref="ds:CanonicalizationMethod"/>
<element ref="ds:SignatureMethod"/>
<element ref="ds:Reference" maxOccurs="unbounded"/>
</sequence>
<attribute name="Id" type="ID" use="optional"/>
</complexType>
<element name="CanonicalizationMethod" type="ds:CanonicalizationMethodType"/>
<complexType name="CanonicalizationMethodType" mixed="true">
<sequence>
<any namespace="##any" minOccurs="0" maxOccurs="unbounded"/>
<!-- (0,unbounded) elements from (1,1) namespace -->
</sequence>
<attribute name="Algorithm" type="anyURI" use="required"/>
</complexType>
<element name="SignatureMethod" type="ds:SignatureMethodType"/>
<complexType name="SignatureMethodType" mixed="true">
<sequence>
<element name="HMACOutputLength" minOccurs="0" type="ds:HMACOutputLengthType"/>
<any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
<!-- (0,unbounded) elements from (1,1) external namespace -->
</sequence>
<attribute name="Algorithm" type="anyURI" use="required"/>
</complexType>
<!-- Start Reference -->
<element name="Reference" type="ds:ReferenceType"/>
<complexType name="ReferenceType">
<sequence>
<element ref="ds:Transforms" minOccurs="0"/>
<element ref="ds:DigestMethod"/>
<element ref="ds:DigestValue"/>
</sequence>
<attribute name="Id" type="ID" use="optional"/>
<attribute name="URI" type="anyURI" use="optional"/>
<attribute name="Type" type="anyURI" use="optional"/>
</complexType>
<element name="Transforms" type="ds:TransformsType"/>
<complexType name="TransformsType">
<sequence>
<element ref="ds:Transform" maxOccurs="unbounded"/>
</sequence>
</complexType>
<element name="Transform" type="ds:TransformType"/>
<complexType name="TransformType" mixed="true">
<choice minOccurs="0" maxOccurs="unbounded">
<any namespace="##other" processContents="lax"/>
<!-- (1,1) elements from (0,unbounded) namespaces -->
<element name="XPath" type="string"/>
</choice>
<attribute name="Algorithm" type="anyURI" use="required"/>
</complexType>
<!-- End Reference -->
<element name="DigestMethod" type="ds:DigestMethodType"/>
<complexType name="DigestMethodType" mixed="true">
<sequence>
<any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="Algorithm" type="anyURI" use="required"/>
</complexType>
<element name="DigestValue" type="ds:DigestValueType"/>
<simpleType name="DigestValueType">
<restriction base="base64Binary"/>
</simpleType>
<!-- End SignedInfo -->
<!-- Start KeyInfo -->
<element name="KeyInfo" type="ds:KeyInfoType"/>
<complexType name="KeyInfoType" mixed="true">
<choice maxOccurs="unbounded">
<element ref="ds:KeyName"/>
<element ref="ds:KeyValue"/>
<element ref="ds:RetrievalMethod"/>
<element ref="ds:X509Data"/>
<element ref="ds:PGPData"/>
<element ref="ds:SPKIData"/>
<element ref="ds:MgmtData"/>
<any processContents="lax" namespace="##other"/>
<!-- (1,1) elements from (0,unbounded) namespaces -->
</choice>
<attribute name="Id" type="ID" use="optional"/>
</complexType>
<element name="KeyName" type="string"/>
<element name="MgmtData" type="string"/>
<element name="KeyValue" type="ds:KeyValueType"/>
<complexType name="KeyValueType" mixed="true">
<choice>
<element ref="ds:DSAKeyValue"/>
<element ref="ds:RSAKeyValue"/>
<any namespace="##other" processContents="lax"/>
</choice>
</complexType>
<element name="RetrievalMethod" type="ds:RetrievalMethodType"/>
<complexType name="RetrievalMethodType">
<sequence>
<element ref="ds:Transforms" minOccurs="0"/>
</sequence>
<attribute name="URI" type="anyURI"/>
<attribute name="Type" type="anyURI" use="optional"/>
</complexType>
<!-- Start X509Data -->
<element name="X509Data" type="ds:X509DataType"/>
<complexType name="X509DataType">
<sequence maxOccurs="unbounded">
<choice>
<element name="X509IssuerSerial" type="ds:X509IssuerSerialType"/>
<element name="X509SKI" type="base64Binary"/>
<element name="X509SubjectName" type="string"/>
<element name="X509Certificate" type="base64Binary"/>
<element name="X509CRL" type="base64Binary"/>
<any namespace="##other" processContents="lax"/>
</choice>
</sequence>
</complexType>
<complexType name="X509IssuerSerialType">
<sequence>
<element name="X509IssuerName" type="string"/>
<element name="X509SerialNumber" type="integer"/>
</sequence>
</complexType>
<!-- End X509Data -->
<!-- Begin PGPData -->
<element name="PGPData" type="ds:PGPDataType"/>
<complexType name="PGPDataType">
<choice>
<sequence>
<element name="PGPKeyID" type="base64Binary"/>
<element name="PGPKeyPacket" type="base64Binary" minOccurs="0"/>
<any namespace="##other" processContents="lax" minOccurs="0"
maxOccurs="unbounded"/>
</sequence>
<sequence>
<element name="PGPKeyPacket" type="base64Binary"/>
<any namespace="##other" processContents="lax" minOccurs="0"
maxOccurs="unbounded"/>
</sequence>
</choice>
</complexType>
<!-- End PGPData -->
<!-- Begin SPKIData -->
<element name="SPKIData" type="ds:SPKIDataType"/>
<complexType name="SPKIDataType">
<sequence maxOccurs="unbounded">
<element name="SPKISexp" type="base64Binary"/>
<any namespace="##other" processContents="lax" minOccurs="0"/>
</sequence>
</complexType>
<!-- End SPKIData -->
<!-- End KeyInfo -->
<!-- Start Object (Manifest, SignatureProperty) -->
<element name="Object" type="ds:ObjectType"/>
<complexType name="ObjectType" mixed="true">
<sequence minOccurs="0" maxOccurs="unbounded">
<any namespace="##any" processContents="lax"/>
</sequence>
<attribute name="Id" type="ID" use="optional"/>
<attribute name="MimeType" type="string" use="optional"/> <!-- add a grep facet -->
<attribute name="Encoding" type="anyURI" use="optional"/>
</complexType>
<element name="Manifest" type="ds:ManifestType"/>
<complexType name="ManifestType">
<sequence>
<element ref="ds:Reference" maxOccurs="unbounded"/>
</sequence>
<attribute name="Id" type="ID" use="optional"/>
</complexType>
<element name="SignatureProperties" type="ds:SignaturePropertiesType"/>
<complexType name="SignaturePropertiesType">
<sequence>
<element ref="ds:SignatureProperty" maxOccurs="unbounded"/>
</sequence>
<attribute name="Id" type="ID" use="optional"/>
</complexType>
<element name="SignatureProperty" type="ds:SignaturePropertyType"/>
<complexType name="SignaturePropertyType" mixed="true">
<choice maxOccurs="unbounded">
<any namespace="##other" processContents="lax"/>
<!-- (1,1) elements from (1,unbounded) namespaces -->
</choice>
<attribute name="Target" type="anyURI" use="required"/>
<attribute name="Id" type="ID" use="optional"/>
</complexType>
<!-- End Object (Manifest, SignatureProperty) -->
<!-- Start Algorithm Parameters -->
<simpleType name="HMACOutputLengthType">
<restriction base="integer"/>
</simpleType>
<!-- Start KeyValue Element-types -->
<element name="DSAKeyValue" type="ds:DSAKeyValueType"/>
<complexType name="DSAKeyValueType">
<sequence>
<sequence minOccurs="0">
<element name="P" type="ds:CryptoBinary"/>
<element name="Q" type="ds:CryptoBinary"/>
</sequence>
<element name="G" type="ds:CryptoBinary" minOccurs="0"/>
<element name="Y" type="ds:CryptoBinary"/>
<element name="J" type="ds:CryptoBinary" minOccurs="0"/>
<sequence minOccurs="0">
<element name="Seed" type="ds:CryptoBinary"/>
<element name="PgenCounter" type="ds:CryptoBinary"/>
</sequence>
</sequence>
</complexType>
<element name="RSAKeyValue" type="ds:RSAKeyValueType"/>
<complexType name="RSAKeyValueType">
<sequence>
<element name="Modulus" type="ds:CryptoBinary"/>
<element name="Exponent" type="ds:CryptoBinary"/>
</sequence>
</complexType>
<!-- End KeyValue Element-types -->
<!-- End Signature -->
</schema>