title: Verify Coinbase IPNs using HTTP_X_SIGNATURE
body: <p>I have integrated Coinbase for bitcoin transactions and, in addition to other security features already implemented, would like to verify that IPNs actually came from them using their signature.</p>
  
  <p>Their support indicated to "Verify that the correct signature is included in the x_signature header. The signature included will be done with the following public key: <a href="https://www.coinbase.com/coinbase.pub" rel="nofollow">https://www.coinbase.com/coinbase.pub</a>."</p>
  
  <p>Using the <a href="http://php.net/manual/en/function.openssl-verify.php" rel="nofollow">openssl_verify documentation</a>, I put together the following:</p>
  
  <pre><code>function verifyX_SIGNATURE()
  {
      // $data and $signature are assumed to contain the data and the signature
  
      // fetch public key from certificate and ready it
      $fp = fopen("https://www.coinbase.com/coinbase.pub", "r");
      $cert = fread($fp, 8192);
      fclose($fp);
  
      $pubkeyid = openssl_pkey_get_public($cert);
  
      // from $_SERVER['HTTP_X_SIGNATURE'] header (data masked)
      $signature = 'G8hpTMmWpwoW+6y6lNTbM5hCpHc9kJJcZ7Oij6I94UiwZeeS8zymuqhv+YE2tmXHCR6MqO+KU1HUrbo5simYfSaGjsRWtT0oQYhLI0qKqko3kxiVZ37B6gmflTfcJiiS0vWuUgOeEMIFL8CNPVFI+snr50c7CZdDl36Tg0Neu0XbxpVqfDBI5WVzQT3ujo6aR/CYnIbcWJ06Klfpd+EFTtzfKU0viMlqh7dWdZEnXg+u9Z3QMyfPGDd/G+QJzNTej7/L18OYo7TzfRHG+HAxxrURS8/616LNF6qvx3mMWtrHE77Hvw+DNhtjy0bzm58pencX5iuGDKo7hbP6fbqFqYgX3ZYokn/EhuqtUto8kI8WMqCV+wdLcVa5fasswAr58l9LuxnTrkCEB/uWAukDbL+qkkBLZabUTUrG2qJUk0ZHJura3XcfwTtCkanJ49ZaKsk0WaosAeWnrDxTBSwXhFGeFMGvK1u+5ffQlyG2Ndd7gtHPVQGpbMJr2FKyGYIuXDckOlQh6vG85PrE8OSMSY+/LozYyKXYiPaA75ZCpCCXuHgQ+pdxoB2QwM6zx+v8n8doW5OLudlP654GmDLG+wVwbdIQNQlU00ZD/ndAUTwa9pMJjRyL3uHIgAom7vXFfQPYlIvZ/unT5l1uHmmKbkLGkMh/IP5ZJauyfuBIUcg=';
  
      // IPN sent in JSON (data masked)
      $data = '{"order":{"id":"BWU44QUL","created_at":"2015-02-11T14:49:41-08:00","status":"completed","event":{"type":"completed"},"total_btc":{"cents":345660.0,"currency_iso":"BTC"},"total_native":{"cents":30.0,"currency_iso":"USD"},"total_payout":{"cents":0.0,"currency_iso":"USD"},"custom":"1089215012665154939","receive_address":"QRwGZSkh3eoj4XTJFk1rZsy74zTouHY5HA","button":{"type":"buy_now","subscription":false,"repeat":null,"name":"30 karma","description":"30 karma @ $0.01 USD each","id":"9bd1d424582687qac22c3037u5axacf1"},"refund_address":"1Fn2ou3rZRStYgtq8v6Taz47drueyQzrF2","transaction":{"id":"54b8441c7dsfbb9b38105022","hash":"ad9d5d6671b6764fc122923fff90a5b7cdb5ec531eb42cd92bda937465b98d76","confirmations":0}}}';
  
      // state whether signature is okay or not
      $ok = openssl_verify($data, $signature, $pubkeyid);
  
      if ($ok == 1) {
          echo "good";
      } elseif ($ok == 0) {
          echo "bad";
      } else {
          echo "ugly, error checking signature";
      }
      // free the key from memory
      openssl_free_key($pubkeyid);
  }
  </code></pre>
  
  <p>This is not verifying (i.e., printing 'bad') and their API library doesn't have a function. I've masked the data but would appreciate any suggestions of what I'm doing wrong around the process. Thank you in advance.</p>