robrichards / xmlseclibs

A PHP library for XML Security
BSD 3-Clause "New" or "Revised" License
387 stars 181 forks source link

Sign with pfx doesn't work #203

Open tarlanpayment opened 4 years ago

tarlanpayment commented 4 years ago

Hi. Thanks for good extension. My code generates invalid signature, I tested when I check with https://tools.chilkat.io/xmlDsigVerify.cshtml, also I have right xml which must be generated, but they are not equal.

    $request = '<?xml version="1.0" encoding="UTF-8"?><CompletePaymentRequest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ReqId>ReqId</ReqId><Login>login</Login><Password>Password</Password><SessionCode>SessionCode</SessionCode><SystemCode>SystemCode</SystemCode></CompletePaymentRequest>';
    $options['force_uri'] = true;
    $doc = new \DOMDocument();
    $doc->formatOutput = FALSE;
    $doc->preserveWhiteSpace = TRUE;
    $certificate = resource_path('TestCert.pfx');
    $doc->loadXML($request);
    // Create a new Security object
    $objDSig = new XMLSecurityDSig('');
    // Use the c14n exclusive canonicalization
    $objDSig->setCanonicalMethod(XMLSecurityDSig::C14N);
    $objDSig->addReference(
        $doc,
        XMLSecurityDSig::SHA1,
        array('http://www.w3.org/2000/09/xmldsig#enveloped-signature'),
        $options
    );

    // Create a new (private) Security key
    $objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type'=>'private'));
    $pfx = file_get_contents($certificate);
    openssl_pkcs12_read($pfx, $key, "123456");
    $objKey->loadKey($key["pkey"]);
    $objDSig->sign($objKey);
    $objDSig->add509Cert($key['cert']);
    $objDSig->appendSignature($doc->documentElement);
    dd($doc->saveXML());
cgodo commented 4 years ago

I spent much time with the same issue and finally found what was wrong, in case it helps anyone.

The problem is that canonicalization must happen AFTER the signature node is added to the document. Otherwise, document namespaces won't be added to the SignedInfo while canonicalization occurs and the SingatureValue will be wrong.

In your code, instead of signing first and then appending the signature:

$objDSig->sign($objKey);
$objDSig->add509Cert($key['cert']);
$objDSig->appendSignature($doc->documentElement);
dd($doc->saveXML());

you can append and sing all in one method, using the second parameter:

$objDSig->sign($objKey, $doc->documentElement);
$objDSig->add509Cert($key['cert']);
dd($doc->saveXML());

This worked for me.

dev-guidolin commented 1 year ago

I spent much time with the same issue and finally found what was wrong, in case it helps anyone.

The problem is that canonicalization must happen AFTER the signature node is added to the document. Otherwise, document namespaces won't be added to the SignedInfo while canonicalization occurs and the SingatureValue will be wrong.

In your code, instead of signing first and then appending the signature:

$objDSig->sign($objKey);
$objDSig->add509Cert($key['cert']);
$objDSig->appendSignature($doc->documentElement);
dd($doc->saveXML());

you can append and sing all in one method, using the second parameter:

$objDSig->sign($objKey, $doc->documentElement);
$objDSig->add509Cert($key['cert']);
dd($doc->saveXML());

This worked for me.

For me to