bseddon / xml-signer

Provides signing and verification of XML documents including support for XAdES
BSD 3-Clause "New" or "Revised" License
19 stars 8 forks source link

Get the TSA timestamp value #6

Closed sangar82 closed 3 years ago

sangar82 commented 3 years ago

In our process to certificates evidences, we create a pdf as a proof of the process. In this PDF, we included when the file was signed by the TA.

I've tried to get it from the xml:


        // extract the timestamp time to save later
        $xml = simplexml_load_string(file_get_contents($path_xml));
        $xml->registerXPathNamespace( 'ds', XMLSecurityDSig::XMLDSIGNS );
        $xml->registerXPathNamespace( 'xa', XADES::NamespaceUrl2016 );
        $entries = $xml->xpath("//xa:SigningTime");

        return date("Y-m-d H:i:s", strtotime((string) $entries[0]));

But it deffears from the information obtained from the webapp validation (+1sec). I think that is the sign date, not the timestamp TA date.

Is there a way to obtain de timestamp TA date? We need it!

p.d. we have methods to extract from the .tsr, but isn't available using your package.

bseddon commented 3 years ago

Here is how to access the date/time value using code from the signer project. If you are using your own code to extract the date time then you need to know it is within the base 64 encoded DER timestamp token. The timestamp token is the second element of the sequence returned by the TSA as the timestamp response. The first element, the response status, is not retained because it must have been successful or the TSR would have been rejected.

Note also that its necessary to select an element within the XAdES namespace not the XMLDSig namespace.

$doc = new DOMDocument();
$doc->load( '...your file with timestamp...' );
// Get the <SignedInfo> node
$xpath = new \DOMXPath( $doc );
$xpath->registerNamespace( 'xa', XAdES::NamespaceUrl2016 );
$nodes = $xpath->query('//xa:EncapsulatedTimeStamp');

// The timestamp token in the signature is base64 encoded
$timestampDERBase64 = $nodes[0];

// This is the timestamp token within the original TSR.  The other part,
// the response code, is not retained because it must have been success.
$timestampDER = base64_decode( $timestampDERBase64->textContent );

// Inflate the DER
$decode = new \lyquidity\Asn1\Der\Decoder();
$timestampToken = $decode->decodeElement( $timestampDER );
// This line is a kludge.  PHP does not resolve functions automatically but it will be available after the call to decodeElement()
$timestampToken = \lyquidity\Asn1\asSequence( $timestampToken ); 
if ( ! $timestampToken )
    throw new \Exception("Oops! Not valid timestamp token");

// Get the signed data
$signedData = lyquidity\Asn1\asSequence( $timestampToken->getNthChildOfType( 1, 0, \lyquidity\Asn1\Element::CLASS_CONTEXTSPECIFIC, \lyquidity\Asn1\Tag::ENVIRONMENT_EXPLICIT ) );
if ( ! $signedData )
    throw new \Exception("Oops! Not valid signed data");

// The raw timestamped input is held DER encoded in an octet string
$tst = \lyquidity\Asn1\asSequence( $signedData->getFirstChildOfType( \lyquidity\Asn1\UniversalTagID::SEQUENCE ) );

// Inflate the string to retrieve the decoded content
$tstInfoRaw =  \lyquidity\Asn1\asOctetString( $tst->getFirstChildOfType( 0,  \lyquidity\Asn1\Element::CLASS_CONTEXTSPECIFIC,  \lyquidity\Asn1\Tag::ENVIRONMENT_EXPLICIT ) );
if ( ! $tstInfoRaw )
{
    throw new Exception('Expect TST info octet string');
}
$tstInfo = \lyquidity\Asn1\asSequence( (new Decoder())->decodeElement( $tstInfoRaw->getValue() ) );
if ( ! $tstInfo )
    throw new \Exception("Unable to access the TST info from the timestamp token");

$time = \lyquidity\Asn1\asGeneralizedTime( $tstInfo->getFirstChildOfType( \lyquidity\Asn1\UniversalTagID::GENERALIZEDTIME ) );
if ( ! $time )
    throw new \Exception("Unable to access the signing date time from the TST");

echo date( 'Y-m-d H:i:s', $time->getValue()->getTimestamp() );
bseddon commented 3 years ago

The essence of this code has been added as a static function to the \lyquidity\TSA\TSA class. This means the above code can be reduced to:

$doc = new DOMDocument();
$doc->load( '...your file with timestamp...' );
// Get the <SignedInfo> node
$xpath = new \DOMXPath( $doc );
$xpath->registerNamespace( 'xa', XAdES::NamespaceUrl2016 );
$nodes = $xpath->query('//xa:EncapsulatedTimeStamp');

$time =\lyquidity\TSA\TSA::getDateFromTSTDERBase64( $nodes[0]->textContent );
echo date( 'Y-m-d H:i:s', $time->getValue()->getTimestamp() );
sangar82 commented 3 years ago

Thanks!! @bseddon the first method is working well! I think you've forgotten to push the commit with the new static function added to\lyquidity\TSA\TSA :)

bseddon commented 3 years ago

I pushed the commit at 14:48 BST. However, I did not update the release so its not available via the requester project's composer release yet.

Anyway, good to know its working for you.