selective-php / xmldsig

Sign XML Documents with Digital Signatures
MIT License
73 stars 34 forks source link

Add support for X509 certificates #22

Closed LordOfTheRats closed 1 year ago

LordOfTheRats commented 1 year ago

Add support for embedding one or more X509 certificates into the signature.

When validating a signature with embedded X509 certificates, the embedded chain can be verified against one or more CA certificates and used for signature validation.

My goal is to validate a signed XML document using the top level CA. The chain of intermediates has be attached to the signature.

I am not particularly familiar with XMLDSig and thus not certain if my implementation is fully compliant with the specification. I'd be happy to change/update my code as needed or desired.

Sign XML Document and attach certificate (chain)

$xmlSigner = new XmlSigner();
$xmlSigner->loadPrivateKeyFile('/tmp/xmldsig/cert.key', '1234');
$xmlSigner->loadCertificatesFile('/tmp/xmldsig/cert.crt');
$xmlSigner->loadCertificatesFile('/tmp/xmldsig/intermediate.cert.pem');
#$xmlSigner->loadCertificatesFile('/tmp/xmldsig/ca-bundle.pem');

$sxml = $xmlSigner->signXml(file_get_contents('/tmp/xmldsig/test.xml'), DigestAlgorithmType::SHA256);
var_dump($sxml);

The resulting XML looks like this (formatted for better readability)

<?xml version="1.0"?>
<root>
  <creditcard>
    <number>19834209</number>
    <expiry>02/02/2025</expiry>
  </creditcard>
  <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
    <SignedInfo>
      <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
      <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
      <Reference URI="">
        <Transforms>
          <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
        </Transforms>
        <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
        <DigestValue>s351jUlMhC7u81I9XcMPhlkKeXfqoaBlxiOhP2wG0Do=</DigestValue>
      </Reference>
    </SignedInfo>
    <SignatureValue>PIP28XzDsiSYhQERBbwOQH6rnUs8FMJa+i2Gh8Bi26RPSYGbfIbdOEHPUdK0mUna1V6Ghh9ldvwsas1xwioNTNF72fiN3jAx8PwpSRfaGVgwgx5Br58aSqLJpcNUXbRZ2MqntgtAMZFn4+UntHGYMVjv2nu6fKPBLaPqvMTT3CSz9kAdoplu3hXDvJAGGCm9umtZF1EjLTJrK8srJGS/d+hzEx7arBlr5nBg5a9sz3tqgRc7FbTE5ZG4vmv2+NIkn+humX8MnSMTk11E50Bd4vPry84cySEwvDnDg5TohXtZQj6KCEFLxoeaaMeh3bweXifL9c5xtEGWUY/UYQ/WHA==</SignatureValue>
    <KeyInfo>
      <KeyValue>
        <RSAKeyValue>
          <Modulus>y2x0BPmM+WYTZaRD3pGmrrlE7HVOiKtZr6F7ghy4uUjOxuMoPL3LtnwWtAh2vtPBM1eyEKedjDHPoq95uTCJi9SCVxH/2gCJ1odwpMZHUFxUbcIVwWj5vnpKKFDYlISoAq/JnEzbrgdav9uqEb/BZsezlSiGTQUufmvmLH5F3m4q278xynPFZy2CgCyljh0f0vB9ZSWvvb752Kl/CbKwlzADOZbnysEPzC8v8bpaA82Bmagd2NMzw5kOLUnTa4UH6jSPvbKLcLcxJeNNggDvfXjPLLmo2ItWS32UqIqkhnZHn7oKijieWm4eR6Tc1HYV/fahyzKurN+WVAfOkAD5Bw==</Modulus>
          <Exponent>AQAB</Exponent>
        </RSAKeyValue>
      </KeyValue>
      <X509Data>
        <X509Certificate>MIIEEzCCAfsCFBqM0+R+r+MbPTyQs1faKxL9/aCxMA0GCSqGSIb3DQEBCwUAMEoxCzAJBgNVBAYTAkdCMRAwDgYDVQQIDAdFbmdsYW5kMRIwEAYDVQQKDAlBbGljZSBMdGQxFTATBgNVBAMMDEludGVybWVkaWF0ZTAeFw0yMjExMjgxNDEzNThaFw0yMzExMjgxNDEzNThaMEIxCzAJBgNVBAYTAkdCMRAwDgYDVQQIDAdFbmdsYW5kMRIwEAYDVQQKDAlBbGljZSBMdGQxDTALBgNVBAMMBENlcnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLbHQE+Yz5ZhNlpEPekaauuUTsdU6Iq1mvoXuCHLi5SM7G4yg8vcu2fBa0CHa+08EzV7IQp52MMc+ir3m5MImL1IJXEf/aAInWh3CkxkdQXFRtwhXBaPm+ekooUNiUhKgCr8mcTNuuB1q/26oRv8Fmx7OVKIZNBS5+a+YsfkXebirbvzHKc8VnLYKALKWOHR/S8H1lJa+9vvnYqX8JsrCXMAM5lufKwQ/MLy/xuloDzYGZqB3Y0zPDmQ4tSdNrhQfqNI+9sotwtzEl402CAO99eM8suajYi1ZLfZSoiqSGdkefugqKOJ5abh5HpNzUdhX99qHLMq6s35ZUB86QAPkHAgMBAAEwDQYJKoZIhvcNAQELBQADggIBAGXGZr6JervM2CItfVqxIueN8o9hZrYVbfmciXnM5/IDlzbLUXAsdoN6RmmjagdZMlXPQysOvWqrdRUXCWTT4nc8U+9eLZt5sn0QyPskIkrBRv4HLqz1or+CSqudk9f9kUqmVwIXy+5JwFvQjZ/yQ0FKUka7NUTOggIJb2Z6DFB+c847YlKE3HV5BosmwX9IZFb8iD+lyIdbJ7peot8u8I3+4nBVLZwxlYTCNfFqtJdf06rNudCf9DEWsqJcw3yfNoIfqHQOWEIa+sQIe/S6ajfErqocO2rHEZNKcM0mIyT9WxTP57mKgiHTzagwxhS3/Kt37q1QgxeV7Qcoda8zBgzNsMRaD/JMlFB+t2yRS1LWBWz+yo7JL4Wjr7WpN0YQ5KQDWK84b80MuAe81QppD9GHwf+2KGchQYE+3498dmrL7te7HfId3JOyhUTIChoz66Sr2sHRwHojNNxyCWtubiMujQg0mlN7HEHuYk8V7ae/BlOYioATvXzlH+Z3h9fD2MlEsVsVwiZx5/69+g5DchcJQwyqssRvz3ZFcI4Shv7YgeajH9I8AGamM2ptLYyZFKx1Py8x5Lq46GqwmL/Zs7vFl+Y7Lrp+TQ8YoO6IxOfPHX0Jk5G6Nw9mB7cOO84CoJpOQBtmzQdGWOLhtLD76GjZ0avMeOLHCIrCh+5kYxmP</X509Certificate>
        <X509Certificate>MIIFbDCCA1SgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwQDELMAkGA1UEBhMCR0IxEDAOBgNVBAgMB0VuZ2xhbmQxEjAQBgNVBAoMCUFsaWNlIEx0ZDELMAkGA1UEAwwCQ0EwHhcNMjIxMTI4MTQwODQxWhcNMzIxMTI1MTQwODQxWjBKMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDESMBAGA1UECgwJQWxpY2UgTHRkMRUwEwYDVQQDDAxJbnRlcm1lZGlhdGUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDOma2yjhmUCj4EDvi9p/MVT5fMix4qTltYiniJnzSeEaICniY66A0jInjeExruRvW4D5mG3RQteI9ERtHytY8N5IQW9UVqioIdriJ6PcMUp4+tzEcNN0QkfDsrs+r5dCORcd4mIgam7ZkR5+sjcP0MDzSh8DY17rPmzbPQrxu0SDe9srGRzN1IMkgm3rvH7o8vzywK363g4p4ZOKRXdDdMBNPG7AVZqPFE9FfpWg/IhRGBFqpMuMSC+d0+RJ9+/G1QbgvDGPvOWX602maVf/2EE7WapLD+YABioUZmsmZUBMCSQqZkau9gVmF5HpIyDbKtp5dTec1bZkCO/Fe8/2V8oebbsXsRjxzj8MckMDjcXCm6RfQvc0JZ7d4aBrxV5eQBoOsF79lFJR3mkiX9JxQZVaj3Bk8xU2XL85x80I34FHHH6zNmxNc7l4f8awSZOIHTYYnG2p8KKRBGt4a3qZ4xQo3tumvoNTZvUPAIeCdrfUnT4T3Mc5rLQ+le7p+GNnSgG1m48dJlFvCpPobm0EjoD3BXu9cp9j5dodEa/MlkCcf7AkDXCYBGphfYb4CXQO2IY58iDxcth3tt4Ibql/CEWt6hVLAUUQmxUkU3V2/rzV0Yk2xfPEI2BlfGAgoyfJfDzQCpl83R71RmRROxAowATG3aPCf9p5IKbfmClaeFZwIDAQABo2YwZDAdBgNVHQ4EFgQUFdONgiwWVZgt56ioohptQlJrAOMwHwYDVR0jBBgwFoAUvVSZPv+r5Q5kVn+hQ93bTKsuZoowEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBADuyQCOqohW7N3lTxEsIOtQZjWENtr82wLTNJRITyAbzyL237mqmp0tZY79rgb0fGEs8ATpZWHg0vUTMMnXoem/4lKcADqu+7gczyllbvdIg6w8lhmsrGZyUIpbHnuyB/6gUk/m9j/NRZo8GOlr1VqwyH/kl3Ej8D2C1RyEPj5Xn/I5hUpdscvX5/ed9LGU//kQFZUuNlYX2Aw8ak9yNkk8WAedbDQZ4C6qfOFcGlop5wdJS5eQdStIswsQ/0Rfxpy+g+IjLkMpJoqCyDYbFjgeuoU4Q3HGAHb2i+caEQSZvsLW70tQryzW6FWcr6eHxY7U2PMcp4/qBUR8OVGlKYVjbfxO4EayQ08h3i3gVIagJBWCZVF1qGphgIHsg4ofjMiIjkjuvT+AJttnCJEAF2vYEG4n1vCQgs6AdaWe9tTpg1tOvcePfr0yLjKccdAVamCsE7jm6EWf7iGa4XT5p5apb7WxJY9CZzs/UI5FazZ7uh8SHPkLcIsP7w2zCUadyqIk4sgKeg7jjsuQRpVuZPw4K108A8S26laCptp4HleRUQbiK2Fvn1+c4eVfC/dSAjdy9GO+Isg6I9/nQZiI2jW+s6fTSUeeqF0XtMThwQ07um7E+bN4Gqg7Rgq9Oe4J0EdBPPjBqbfpJTA7flyb27Gfw0mUG9WK4TQei/6leQM85</X509Certificate>
      </X509Data>
    </KeyInfo>
  </Signature>
</root>

Verify XML Document with attached certificates

$signatureValidator = new XmlSignatureValidator();
$signatureValidator->loadCaCertificatesFile('/tmp/xmldsig/ca.cert.pem');

$isValid = $signatureValidator->verifyXml($sxml);
var_dump($isValid);
odan commented 1 year ago

Thanks for the PR. The changes are massive, and the responsibilities of each class may become too large (clean code). I think I need to refactor the whole package before we can add more features like these.

LordOfTheRats commented 1 year ago

Hi odan,

I agree, in the long term some refactoring might be in order to keep the code clean, though I think the changes are right at the limit of what I'd call still reasonable.

Do you have a rough idea how you would refactor the project? I could adapt my changes to at least point in the general direction to make it easier to eventually port the feature into the new project structure.

odan commented 1 year ago

I have refactored the package by separating the different concerns such as encryption and XML signing / verification in to different classes.

There are two new interfaces for the cryptographic part:

The current crypto implementation using OpenSSL has been moved into these classes:

Example usage

$cryptoEncoder = new OpenSslCryptoEncoder('sha512');
$cryptoEncoder->loadPrivateKey('private key content', 'password');

$xmlSigner = new XmlSigner($cryptoEncoder);

$signedXml = $xmlSigner->signXml('<?xml ...');

Read more: https://github.com/selective-php/xmldsig/blob/master/README.md#usage

When you have a very specific requirement, you can now write your own cryptographic implementation, such as loading x509 certificates, loading a certificate chain or an Elliptic Curve Digital Signature Algorithm:

Note, this is just the first draft.

odan commented 1 year ago

I have merged most of your changes into the latest main branch. I just skipped the intermediate certificate validation, because this specific task is not the responsibility of this package. The users itself has to ensure that the certificate chain is always valid.

Please check out the latest dev-master branch and test it.