{"id":529,"date":"2011-03-02T22:17:31","date_gmt":"2011-03-02T21:17:31","guid":{"rendered":"https:\/\/d-mueller.de\/blog\/?p=529"},"modified":"2015-06-22T20:27:15","modified_gmt":"2015-06-22T18:27:15","slug":"dealing-with-trusted-timestamps-in-php-rfc-3161","status":"publish","type":"post","link":"https:\/\/d-mueller.de\/blog\/dealing-with-trusted-timestamps-in-php-rfc-3161\/","title":{"rendered":"Dealing with Trusted Timestamps in PHP (RFC 3161)"},"content":{"rendered":"<p>This article won&#8217;t be pariculary interesting for the most readers. My aim is to help the billions of developers that are confused about dealing with <b>trusted timestamping<\/b>. If you don&#8217;t know what Trusted Timestamps are, carry on.<\/p>\n<h2>Explanation of the concept behind Trusted Timestamps<\/h2>\n<p>I can&#8217;t put it better as Wikipedia (<a href=\"http:\/\/en.wikipedia.org\/wiki\/Trusted_timestamping\">Trusted timestamping<\/a>) does:<\/p>\n<blockquote><p>\nTrusted timestamping is the process of securely keeping track of the creation and modification time of a document. Security here means that no one \u2014 not even the owner of the document \u2014 should be able to change it once it has been recorded provided that the timestamper&#8217;s integrity is never compromised.\n<\/p><\/blockquote>\n<p>The workflow is as follows:<\/p>\n<ol>\n<li>You create a hash of a file \/ database-columns \/ whatever in sha1 or md5<\/li>\n<li>You create a timestamp-requestfile from your hash via the <a href=\"http:\/\/www.openssl.org\/docs\/apps\/ts.html\">openssl ts<\/a>-command<\/li>\n<li>You send the requestfile to a Timestamp Authority (TSA). I will use the <a href=\"https:\/\/www.pki.dfn.de\/zeitstempel\/\">DFN timestamp-service<\/a> but you are free to use any TSA that follows the <a href=\"http:\/\/www.ietf.org\/rfc\/rfc3161.txt\">RFC 3161<\/a> in which Trusted Timestamping is defined<\/li>\n<li>The TSA responds with a confusing human-unreadable binary string (Timestamp-Response) that you can store in a file or in a database. This response is signed with a certificate of the TSA so that you can&#8217;t manipulate the string or simply switch the TSA afterwards.<\/li>\n<li>You are now able to check if someone has changed the signed data when you recalculate the hash (step 1) and validate it against the Timestamp-Response.<\/li>\n<\/ol>\n<h2>Why has this to be so complicated?<\/h2>\n<p>Trusted Timestamping is particulary interesting for applications which deal with very critical data. You can&#8217;t simply store a hash in your database when the attacker might have access to all your systems and comes from within your company (e.g. the admin itself). He would be able to simply manipulate the hashed data and overwrite the old hash in your database. Another point is to prove in court that there has no manipulation happened &#8211; definitely necessary when dealing with money.<\/p>\n<h2>Preconditions<\/h2>\n<p>As mentioned above, this requires the openssl ts-command which is availible in openssl versions newer than 0.99 (check with command <i>openssl version<\/i>).<\/p>\n<ul>\n<li><b>Linux:<\/b> The version that ships with Debian is older than that and does not support the openssl ts-command. In this case, you have to use the <a href=\"http:\/\/wiki.debian.org\/DebianExperimental\">Debian Experimentals<\/a>.<\/li>\n<li>When you&#8217;re under <b>Windows<\/b> (e.g. local testing environment), you have to <a href=\"http:\/\/www.slproweb.com\/products\/Win32OpenSSL.html\">look here<\/a> (The <i>light<\/i> package will do. If needed, the <i>Visual C++ 2008 Redistributables<\/i> have to be installed in advance. Also don&#8217;t forget to add <i>X:\\path\\to\\openssl\\bin<\/i> to your PATH).<\/li>\n<\/ul>\n<p>Furthermore CURL is required. Ah, and you have to be able to use the <a href=\"http:\/\/php.net\/manual\/de\/function.exec.php\">exec<\/a>-function of PHP, which might be disabled at some shared hosts.<\/p>\n<h2>Let&#8217;s go!<\/h2>\n<p>We&#8217;ll be creating a Trusted Timestamp-Class that is able to handle anything from creating requestfiles over signing them at the TSA to verifying the integrity. Let&#8217;s start.<\/p>\n<h3>Create the Requestfile<\/h3>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\n\r\npublic static function createRequestfile ($hash)\r\n{\r\n\tif (strlen($hash) !== 40)\r\n\t\tthrow new Exception(&quot;Invalid hash.&quot;);\r\n\t\t\r\n\t$outfilepath = self::createTempFile();\r\n\t$cmd = &quot;openssl ts -query -digest &quot;.escapeshellarg($hash).&quot; -cert -out &quot;.escapeshellarg($outfilepath);\r\n\r\n\t$retarray = array();\r\n\texec($cmd.&quot; 2&gt;&amp;1&quot;, $retarray, $retcode);\r\n\t\r\n\tif ($retcode !== 0)\r\n\t\tthrow new Exception(&quot;OpenSSL does not seem to be installed: &quot;.implode(&quot;, &quot;, $retarray));\r\n\t\r\n\tif (stripos($retarray[0], &quot;openssl:Error&quot;) !== false)\r\n\t\tthrow new Exception(&quot;There was an error with OpenSSL. Is version &gt;= 0.99 installed?: &quot;.implode(&quot;, &quot;, $retarray));\r\n\r\n\treturn $outfilepath;\r\n}\r\n<\/pre>\n<p>You throw a hash in and the method returns the path to the newly created Timestamp Requestfile. If you&#8217;re curious what the <i>2>&#038;1<\/i> appendix to the executed command is for: This redirects the error-stream STDERR to STDOUT, so <a href=\"http:\/\/php.net\/manual\/de\/function.exec.php\">exec<\/a> will be able to also return the error description if something goes wrong.<\/p>\n<h3>Sign Requestfile<\/h3>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\npublic static function signRequestfile ($requestfile_path, $tsa_url)\r\n{\r\n\tif (!file_exists($requestfile_path))\r\n\t\tthrow new Exception(&quot;The Requestfile was not found&quot;);\r\n\r\n\t$ch = curl_init();\r\n\tcurl_setopt($ch, CURLOPT_URL, $tsa_url);\r\n\tcurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\r\n\tcurl_setopt($ch, CURLOPT_TIMEOUT, 10);\r\n\tcurl_setopt($ch, CURLOPT_POST, 1);\r\n\tcurl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);\r\n\tcurl_setopt($ch, CURLOPT_POSTFIELDS, file_get_contents($requestfile_path));\r\n\tcurl_setopt($ch, CURLOPT_HTTPHEADER, array(&#039;Content-Type: application\/timestamp-query&#039;));\r\n\tcurl_setopt($ch, CURLOPT_USERAGENT, &quot;Mozilla\/4.0 (compatible; MSIE 5.01; Windows NT 5.0)&quot;); \r\n\t$binary_response_string = curl_exec($ch);\r\n\t$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\r\n\tcurl_close($ch);\r\n\t\r\n\tif ($status != 200 || !strlen($binary_response_string))\r\n\t\tthrow new Exception(&quot;The request failed&quot;);\r\n\t\r\n\t$base64_response_string = base64_encode($binary_response_string);\r\n\t\r\n\t$response_time = self::getTimestampFromAnswer ($base64_response_string);\r\n\t\r\n\treturn array(&quot;response_string&quot; =&gt; $base64_response_string,\r\n\t\t\t\t &quot;response_time&quot; =&gt; $response_time);\r\n}\r\n<\/pre>\n<p>Now that we&#8217;ve created our Requestfile, we want to send it to the TSA in order to get our Timestamp-Response binary certificate signed string. For that, we use CURL to transmit our Requestfile. After we get our response, we <a href=\"http:\/\/php.net\/manual\/de\/function.base64-encode.php\">base64-encode<\/a> it because handling with binary strings can be a pain in the ass, especially when you want to write it to the database. We return the base64-encoded responsestring and the extracted unix timestamp (will be shown in the next method).<\/p>\n<h3>Get the Unix-Timestamp from the Timestamp Response<\/h3>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\npublic static function getTimestampFromAnswer ($base64_response_string)\r\n{\r\n\t$binary_response_string = base64_decode($base64_response_string);\r\n\r\n\t$responsefile = self::createTempFile($binary_response_string);\r\n\r\n\t$cmd = &quot;openssl ts -reply -in &quot;.escapeshellarg($responsefile).&quot; -text&quot;;\r\n\t\r\n\t$retarray = array();\r\n\texec($cmd.&quot; 2&gt;&amp;1&quot;, $retarray, $retcode);\r\n\t\r\n\tif ($retcode !== 0)\r\n\t\tthrow new Exception(&quot;The reply failed: &quot;.implode(&quot;, &quot;, $retarray));\r\n\t\r\n\t$matches = array();\r\n\t$response_time = 0;\r\n\r\n\t\/*\r\n\t * Format of answer:\r\n\t * \r\n\t * Foobar: some stuff\r\n\t * Time stamp: 21.08.2010 blabla GMT\r\n\t * Somestuff: Yayayayaya\r\n\t *\/\r\n\tforeach ($retarray as $retline)\r\n\t{\r\n\t\tif (preg_match(&quot;~^Time\\sstamp\\:\\s(.*)~&quot;, $retline, $matches))\r\n\t\t{\r\n\t\t\t$response_time = strtotime($matches[1]);\r\n\t\t\tbreak;\t\t\r\n\t\t}\r\n\t}\r\n\r\n\tif (!$response_time)\r\n\t\tthrow new Exception(&quot;The Timestamp was not found&quot;);\t\r\n\t\t\r\n\treturn $response_time;\r\n}\r\n<\/pre>\n<p>We want to be sure that an attacker can&#8217;t simply make a new TSA request to sign his manipulated data and delete the old TSA request with the real data. So we have to pull the exact unix timestamp from the Timestamp Response, when our Request has been signed.<\/p>\n<h3>The Validation<\/h3>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\npublic static function validate ($hash, $base64_response_string, $response_time, $tsa_cert_file)\r\n{\r\n\tif (strlen($hash) !== 40)\r\n\t\tthrow new Exception(&quot;Invalid Hash&quot;);\r\n\t\r\n\t$binary_response_string = base64_decode($base64_response_string);\r\n\t\r\n\tif (!strlen($binary_response_string))\r\n\t\tthrow new Exception(&quot;There was no response-string&quot;);\t\r\n\t\t\r\n\tif (!intval($response_time))\r\n\t\tthrow new Exception(&quot;There is no valid response-time given&quot;);\r\n\t\r\n\tif (!file_exists($tsa_cert_file))\r\n\t\tthrow new Exception(&quot;The TSA-Certificate could not be found&quot;);\r\n\t\r\n\t$responsefile = self::createTempFile($binary_response_string);\r\n\r\n\t$cmd = &quot;openssl ts -verify -digest &quot;.escapeshellarg($hash).&quot; -in &quot;.escapeshellarg($responsefile).&quot; -CAfile &quot;.escapeshellarg($tsa_cert_file);\r\n\t\r\n\t$retarray = array();\r\n\texec($cmd.&quot; 2&gt;&amp;1&quot;, $retarray, $retcode);\r\n\t\r\n\t\/*\r\n\t * just 2 &quot;normal&quot; cases: \r\n\t * \t1) Everything okay -&gt; retcode 0 + retarray[0] == &quot;Verification: OK&quot;\r\n\t *  2) Hash is wrong -&gt; retcode 1 + strpos(retarray[somewhere], &quot;message imprint mismatch&quot;) !== false\r\n\t * \r\n\t * every other case (Certificate not found \/ invalid \/ openssl is not installed \/ ts command not known)\r\n\t * are being handled the same way -&gt; retcode 1 + any retarray NOT containing &quot;message imprint mismatch&quot;\r\n\t *\/\r\n\t\r\n\tif ($retcode === 0 &amp;&amp; strtolower(trim($retarray[0])) == &quot;verification: ok&quot;)\r\n\t{\r\n\t\tif (self::getTimestampFromAnswer ($base64_response_string) != $response_time)\r\n\t\t\tthrow new Exception(&quot;The responsetime of the request was changed&quot;);\r\n\t\t\r\n\t\treturn true;\r\n\t}\r\n\r\n\tforeach ($retarray as $retline)\r\n\t{\r\n\t\tif (stripos($retline, &quot;message imprint mismatch&quot;) !== false)\r\n\t\t\treturn false;\r\n\t}\r\n\r\n\tthrow new Exception(&quot;Systemcommand failed: &quot;.implode(&quot;, &quot;, $retarray));\r\n}\r\n<\/pre>\n<p>Wohow, that&#8217;s a lot of code. But what&#8217;s happening isn&#8217;t very complicated. We do some verification of our parameters and then mess around with return-codes from the openssl ts-command to see if our hash is still valid when checking against our Timestamp Response. We also check, if the unix timestamp of signing is still the same. It&#8217;s important to mention that we need the <b>certificate chain<\/b> (usually pem-format) of the TSA. Our method expects the path to the certificate-file in the last parameter. The <a href=\"https:\/\/pki.pca.dfn.de\/global-services-ca\/pub\/cacert\/chain.txt\">certificate chain for the DFN-Service<\/a> can be found here.<\/p>\n<h3>Our Helper-Function to create a tempfile<\/h3>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\npublic static function createTempFile ($str = &quot;&quot;)\r\n{\r\n\t$tempfilename = tempnam(sys_get_temp_dir(), rand());\r\n\r\n\tif (!file_exists($tempfilename))\r\n\t\tthrow new Exception(&quot;Tempfile could not be created&quot;);\r\n\t\t\r\n\tif (!empty($str) &amp;&amp; !file_put_contents($tempfilename, $str))\r\n\t\tthrow new Exception(&quot;Could not write to tempfile&quot;);\r\n\r\n\treturn $tempfilename;\r\n}\r\n<\/pre>\n<p>Umm&#8230; nothing spectacular.<\/p>\n<h2>How do I use this class?<\/h2>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\nrequire_once &quot;TrustedTimestamps.php&quot;;\r\n\r\n$my_hash = sha1(&quot;Some Data for testing&quot;);\r\n\r\n$requestfile_path = TrustedTimestamps::createRequestfile($my_hash);\r\n\r\n$response = TrustedTimestamps::signRequestfile($requestfile_path, &quot;http:\/\/zeitstempel.dfn.de&quot;);\r\nprint_r($response);\r\n\/*\r\nArray\r\n(\r\n    [response_string] =&gt; Shitload of text (base64-encoded Timestamp-Response of the TSA)\r\n    [response_time] =&gt; 1299098823\r\n)\r\n*\/\r\n\r\necho TrustedTimestamps::getTimestampFromAnswer($response[&#039;response_string&#039;]); \/\/1299098823\r\n\r\n$tsa_cert_chain_file = &quot;chain.txt&quot;; \/\/from https:\/\/pki.pca.dfn.de\/global-services-ca\/pub\/cacert\/chain.txt\r\n\r\n$validate = TrustedTimestamps::validate($my_hash, $response[&#039;response_string&#039;], $response[&#039;response_time&#039;], $tsa_cert_chain_file); \r\nvar_dump($validate); \/\/bool(true)\r\n\r\n\/\/now with an incorrect hash. Same goes for a manipulated response string or response time\r\n$validate = TrustedTimestamps::validate(sha1(&quot;im not the right hash&quot;), $response[&#039;response_string&#039;], $response[&#039;response_time&#039;], $tsa_cert_chain_file);\r\nvar_dump($validate); \/\/bool(false)\r\n<\/pre>\n<p>The code should speak for itself.<\/p>\n<h2>I don&#8217;t want to copy and paste all your little code samples!<\/h2>\n<p>Okay, okay&#8230; There you are, the complete class:<\/p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\n&lt;?php\r\n\/**\r\n * TrustedTimestamps.php - Creates Timestamp Requestfiles, processes the request at a Timestamp Authority (TSA) after RFC 3161\r\n *\r\n * Released under the MIT license (opensource.org\/licenses\/MIT) Copyright (c) 2015 David M\u00fcller\r\n *\r\n * bases on OpenSSL and RFC 3161: http:\/\/www.ietf.org\/rfc\/rfc3161.txt\r\n *\r\n * WARNING: \r\n * \tneeds openssl ts, which is availible in OpenSSL versions &gt;= 0.99\r\n * \tThis is currently (2011-03-02) not the case in Debian\r\n * \t(see http:\/\/stackoverflow.com\/questions\/5043393\/openssl-ts-command-not-working-trusted-timestamps)\r\n * \t-&gt; Possibility: Debian Experimentals -&gt; http:\/\/wiki.debian.org\/DebianExperimental\r\n * \r\n * For OpenSSL on Windows, see\r\n * \thttp:\/\/www.slproweb.com\/products\/Win32OpenSSL.html\r\n * \thttp:\/\/www.switch.ch\/aai\/support\/howto\/openssl-windows.html\r\n * \r\n * @version 0.3\r\n * @author David M\u00fcller\r\n * @package trustedtimestamps\r\n*\/\r\n\r\nclass TrustedTimestamps\r\n{\r\n    \/**\r\n     * Creates a Timestamp Requestfile from a hash\r\n     *\r\n     * @param string $hash: The hashed data (sha1)\r\n     * @return string: path of the created timestamp-requestfile\r\n     *\/\r\n\tpublic static function createRequestfile ($hash)\r\n\t{\r\n\t\tif (strlen($hash) !== 40)\r\n\t\t\tthrow new Exception(&quot;Invalid Hash.&quot;);\r\n\t\t\t\r\n\t\t$outfilepath = self::createTempFile();\r\n\t\t$cmd = &quot;openssl ts -query -digest &quot;.escapeshellarg($hash).&quot; -cert -out &quot;.escapeshellarg($outfilepath);\r\n\r\n\t\t$retarray = array();\r\n\t\texec($cmd.&quot; 2&gt;&amp;1&quot;, $retarray, $retcode);\r\n\t\t\r\n\t\tif ($retcode !== 0)\r\n\t\t\tthrow new Exception(&quot;OpenSSL does not seem to be installed: &quot;.implode(&quot;, &quot;, $retarray));\r\n\t\t\r\n\t\tif (stripos($retarray[0], &quot;openssl:Error&quot;) !== false)\r\n\t\t\tthrow new Exception(&quot;There was an error with OpenSSL. Is version &gt;= 0.99 installed?: &quot;.implode(&quot;, &quot;, $retarray));\r\n\r\n\t\treturn $outfilepath;\r\n\t}\r\n\r\n    \/**\r\n     * Signs a timestamp requestfile at a TSA using CURL\r\n     *\r\n     * @param string $requestfile_path: The path to the Timestamp Requestfile as created by createRequestfile\r\n     * @param string $tsa_url: URL of a TSA such as http:\/\/zeitstempel.dfn.de\r\n     * @return array of response_string with the unix-timetamp of the timestamp response and the base64-encoded response_string\r\n     *\/\r\n\tpublic static function signRequestfile ($requestfile_path, $tsa_url)\r\n\t{\r\n\t\tif (!file_exists($requestfile_path))\r\n\t\t\tthrow new Exception(&quot;The Requestfile was not found&quot;);\r\n\r\n\t\t$ch = curl_init();\r\n\t\tcurl_setopt($ch, CURLOPT_URL, $tsa_url);\r\n\t\tcurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\r\n\t\tcurl_setopt($ch, CURLOPT_TIMEOUT, 10);\r\n\t\tcurl_setopt($ch, CURLOPT_POST, 1);\r\n\t\tcurl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);\r\n\t\tcurl_setopt($ch, CURLOPT_POSTFIELDS, file_get_contents($requestfile_path));\r\n\t\tcurl_setopt($ch, CURLOPT_HTTPHEADER, array(&#039;Content-Type: application\/timestamp-query&#039;));\r\n\t\tcurl_setopt($ch, CURLOPT_USERAGENT, &quot;Mozilla\/4.0 (compatible; MSIE 5.01; Windows NT 5.0)&quot;); \r\n\t\t$binary_response_string = curl_exec($ch);\r\n\t\t$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\r\n\t\tcurl_close($ch);\r\n\t\t\r\n\t\tif ($status != 200 || !strlen($binary_response_string))\r\n\t\t\tthrow new Exception(&quot;The request failed&quot;);\r\n\t\t\r\n\t\t$base64_response_string = base64_encode($binary_response_string);\r\n\t\t\r\n\t\t$response_time = self::getTimestampFromAnswer ($base64_response_string);\r\n\t\t\r\n\t\treturn array(&quot;response_string&quot; =&gt; $base64_response_string,\r\n\t\t\t\t\t &quot;response_time&quot; =&gt; $response_time);\r\n\t}\r\n\r\n    \/**\r\n     * Extracts the unix timestamp from the base64-encoded response string as returned by signRequestfile\r\n     *\r\n     * @param string $base64_response_string: Response string as returned by signRequestfile\r\n     * @return int: unix timestamp\r\n     *\/\r\n\tpublic static function getTimestampFromAnswer ($base64_response_string)\r\n\t{\r\n\t\t$binary_response_string = base64_decode($base64_response_string);\r\n\r\n\t\t$responsefile = self::createTempFile($binary_response_string);\r\n\r\n\t\t$cmd = &quot;openssl ts -reply -in &quot;.escapeshellarg($responsefile).&quot; -text&quot;;\r\n\t\t\r\n\t\t$retarray = array();\r\n\t\texec($cmd.&quot; 2&gt;&amp;1&quot;, $retarray, $retcode);\r\n\t\t\r\n\t\tif ($retcode !== 0)\r\n\t\t\tthrow new Exception(&quot;The reply failed: &quot;.implode(&quot;, &quot;, $retarray));\r\n\t\t\r\n\t\t$matches = array();\r\n\t\t$response_time = 0;\r\n\r\n\t\t\/*\r\n\t\t * Format of answer:\r\n\t\t * \r\n\t\t * Foobar: some stuff\r\n\t\t * Time stamp: 21.08.2010 blabla GMT\r\n\t\t * Somestuff: Yayayayaya\r\n\t\t *\/\r\n\t\tforeach ($retarray as $retline)\r\n\t\t{\r\n\t\t\tif (preg_match(&quot;~^Time\\sstamp\\:\\s(.*)~&quot;, $retline, $matches))\r\n\t\t\t{\r\n\t\t\t\t$response_time = strtotime($matches[1]);\r\n\t\t\t\tbreak;\t\t\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (!$response_time)\r\n\t\t\tthrow new Exception(&quot;The Timestamp was not found&quot;);\t\r\n\t\t\t\r\n\t\treturn $response_time;\r\n\t}\r\n\r\n    \/**\r\n     *\r\n     * @param string $hash: sha1 hash of the data which should be checked\r\n     * @param string $base64_response_string: The response string as returned by signRequestfile\r\n     * @param int $response_time: The response time, which should be checked\r\n     * @param string $tsa_cert_file: The path to the TSAs certificate chain (e.g. https:\/\/pki.pca.dfn.de\/global-services-ca\/pub\/cacert\/chain.txt)\r\n     * @return &lt;type&gt;\r\n     *\/\r\n\tpublic static function validate ($hash, $base64_response_string, $response_time, $tsa_cert_file)\r\n\t{\r\n\t\tif (strlen($hash) !== 40)\r\n\t\t\tthrow new Exception(&quot;Invalid Hash&quot;);\r\n\t\t\r\n\t\t$binary_response_string = base64_decode($base64_response_string);\r\n\t\t\r\n\t\tif (!strlen($binary_response_string))\r\n\t\t\tthrow new Exception(&quot;There was no response-string&quot;);\t\r\n\t\t\t\r\n\t\tif (!intval($response_time))\r\n\t\t\tthrow new Exception(&quot;There is no valid response-time given&quot;);\r\n\t\t\r\n\t\tif (!file_exists($tsa_cert_file))\r\n\t\t\tthrow new Exception(&quot;The TSA-Certificate could not be found&quot;);\r\n\t\t\r\n\t\t$responsefile = self::createTempFile($binary_response_string);\r\n\r\n\t\t$cmd = &quot;openssl ts -verify -digest &quot;.escapeshellarg($hash).&quot; -in &quot;.escapeshellarg($responsefile).&quot; -CAfile &quot;.escapeshellarg($tsa_cert_file);\r\n\t\t\r\n\t\t$retarray = array();\r\n\t\texec($cmd.&quot; 2&gt;&amp;1&quot;, $retarray, $retcode);\r\n\t\t\r\n\t\t\/*\r\n\t\t * just 2 &quot;normal&quot; cases: \r\n\t\t * \t1) Everything okay -&gt; retcode 0 + retarray[0] == &quot;Verification: OK&quot;\r\n\t\t *  2) Hash is wrong -&gt; retcode 1 + strpos(retarray[somewhere], &quot;message imprint mismatch&quot;) !== false\r\n\t\t * \r\n\t\t * every other case (Certificate not found \/ invalid \/ openssl is not installed \/ ts command not known)\r\n\t\t * are being handled the same way -&gt; retcode 1 + any retarray NOT containing &quot;message imprint mismatch&quot;\r\n\t\t *\/\r\n\t\t\r\n\t\tif ($retcode === 0 &amp;&amp; strtolower(trim($retarray[0])) == &quot;verification: ok&quot;)\r\n\t\t{\r\n\t\t\tif (self::getTimestampFromAnswer ($base64_response_string) != $response_time)\r\n\t\t\t\tthrow new Exception(&quot;The responsetime of the request was changed&quot;);\r\n\t\t\t\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\tforeach ($retarray as $retline)\r\n\t\t{\r\n\t\t\tif (stripos($retline, &quot;message imprint mismatch&quot;) !== false)\r\n\t\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tthrow new Exception(&quot;Systemcommand failed: &quot;.implode(&quot;, &quot;, $retarray));\r\n\t}\r\n\r\n    \/**\r\n     * Create a tempfile in the systems temp path\r\n     *\r\n     * @param string $str: Content which should be written to the newly created tempfile\r\n     * @return string: filepath of the created tempfile\r\n     *\/\r\n\tpublic static function createTempFile ($str = &quot;&quot;)\r\n\t{\r\n\t\t$tempfilename = tempnam(sys_get_temp_dir(), rand());\r\n\r\n\t\tif (!file_exists($tempfilename))\r\n\t\t\tthrow new Exception(&quot;Tempfile could not be created&quot;);\r\n\t\t\t\r\n\t\tif (!empty($str) &amp;&amp; !file_put_contents($tempfilename, $str))\r\n\t\t\tthrow new Exception(&quot;Could not write to tempfile&quot;);\r\n\r\n\t\treturn $tempfilename;\r\n\t}\r\n}\r\n<\/pre>\n<p>I hope you will find any use in this, don&#8217;t hesitate to ask if something is not clear to you.<\/p>\n<h2>License<\/h2>\n<p>This class is released under the <a href=\"http:\/\/opensource.org\/licenses\/MIT\">MIT License<\/a>.<\/p>\n<h2>Credits: Thanks to the DFN!<\/h2>\n<p>The guys at the DFN gave me the allowance to use their service as an example for my blogpost. The service is free. You&#8217;ll find further information to the DFN service <a href=\"https:\/\/www.pki.dfn.de\/zeitstempel\/\">at this point<\/a>. Please also respect the <a href=\"http:\/\/www.dfn.de\/fileadmin\/6Organisation\/Geschaeftsstelle\/satzungdfn.pdf\">DFN Charter<\/a> (Google Translator is your friend).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This article won&#8217;t be pariculary interesting for the most readers. My aim is to help the billions of developers that are confused about dealing with trusted timestamping. If you don&#8217;t know what Trusted Timestamps are, carry on. Explanation of the &hellip; <a href=\"https:\/\/d-mueller.de\/blog\/dealing-with-trusted-timestamps-in-php-rfc-3161\/\">Weiterlesen <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,6,3],"tags":[],"class_list":["post-529","post","type-post","status-publish","format-standard","hentry","category-php","category-security","category-webdev"],"_links":{"self":[{"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/posts\/529","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/comments?post=529"}],"version-history":[{"count":0,"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/posts\/529\/revisions"}],"wp:attachment":[{"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/media?parent=529"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/categories?post=529"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/tags?post=529"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}