Validating Signed Requests

This article describes how to validate a signed request. If you're unsure of what a signed request is or why you would want to use one, please check out the Introduction To Signed Requests page.

Obtaining encryption keys

Signed requests currently include three parameters:

  1. oauth_consumer_key
  2. xoauth_signature_publickey
  3. oauth_signature_method

The auth_consumer_key parameter indicates the container from which the request is being sent. The oauth_signature_method parameter indicates the method used to sign the request. For requests signed with RSA-SHA1, the }xoauth_signature_publickey* contains the name of the public key used to sign the request.

For HMAC-SHA1, you will need to establish a secret key between your application and the container. Each container has a different mechanism for doing so- please consult your target container's documentation for information about establishing a shared secret key.

For RSA-SHA1, you will need to obtain the certificate referenced by xoauth_signature_publickey. For your convenience, there is a list of public keys for each container available at https://opensocialresources.appspot.com/certificates/ along with links to each certificate. This site is for convenience only and not approved by the containers listed! For the best security, be sure to check your containers documentation to verify the location of the appropriate public key!

Certificates should not be fetched each time you want to validate parameters - instead, implement a server side key cache indexed on the value of xoauth_signature_publickey, oauth_consumer_key}e}, and {{oauth_signature_method. If these value change, you will need to pull a new certificate down and store it in your key cache.

OAuth Signing Mechanism

The parameter signing is implemented according to the OAuth parameter signing specification.

Check [ this page|http://oauth.net/core/1.0/#rfc.section.A.5.1] for information about generating the Signature Base String.

PHP

Client side code

Your client code should make a request in the following manner. We will be specifying a gadgets.io.AuthorizationType.SIGNED request, and will expect the response to be returned as JSON.

function makeSignedRequest() {
   var params = {};
   params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.SIGNED;
   params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.JSON;
   var url = "http://graargh.returnstrue.com/buh/fetchme.php";
   gadgets.io.makeRequest(url, response, params);
 };
 
 function response(ret) {
   output(ret.data);
 
   var html = [ ret.data.validated, "<br />",
     "oauth_consumer_key: ", ret.data.query.oauth_consumer_key, "<br />",
     "oauth_nonce: ", ret.data.query.oauth_nonce, "<br />",
     "oauth_signature: ", ret.data.query.oauth_signature, "<br />",
     "oauth_signature_method: ", ret.data.query.oauth_signature_method, "<br />",
     "oauth_timestamp: ", ret.data.query.oauth_timestamp, "<br />",
     "oauth_token: ", ret.data.query.oauth_token, "<br />",
     "opensocial_appid: ", ret.data.query.opensocial_appid, "<br />",
     "opensocial_ownerid: ", ret.data.query.opensocial_ownerid, "<br />",
     "xoauth_signature_publickey: ", ret.data.query.xoauth_signature_publickey ].join("");
 
   output(html);
 
 };
 
makeSignedRequest();

Server side code

The following code shows the server side code for fetchme.php. This code relies on OAuth.php (revision 526) from the open source OAuth library project.

The code implements these steps:

  • Creates an OrkutSignatureMethod class that extends OAuthSignatureMethod_RSA_SHA1
  • Creates an OAuthRequest object from the current request
  • Creates an instance of OrkutSignatureMethod
  • Checks the request using the OrkutSignatureMethod class
  • Outputs a JSON object

This example is not meant to be production quality code - merely a demonstration of the steps you would take to validate a signed request server-side. You should not be inlining orkut's public key certificate in your production code.

<?php
   require_once("OAuth.php");
 
   class OrkutSignatureMethod extends OAuthSignatureMethod_RSA_SHA1 {
     protected function fetch_public_cert(&$request) {
       return <<<EOD
 -----BEGIN CERTIFICATE-----
 MIIDHDCCAoWgAwIBAgIJAMbTCksqLiWeMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNV
 BAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIG
 A1UEChMLR29vZ2xlIEluYy4xDjAMBgNVBAsTBU9ya3V0MQ4wDAYDVQQDEwVscnlh
 bjAeFw0wODAxMDgxOTE1MjdaFw0wOTAxMDcxOTE1MjdaMGgxCzAJBgNVBAYTAlVT
 MQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChML
 R29vZ2xlIEluYy4xDjAMBgNVBAsTBU9ya3V0MQ4wDAYDVQQDEwVscnlhbjCBnzAN
 BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAseBXZ4NDhm24nX3sJRiZJhvy9eDZX12G
 j4HWAMmhAcnm2iBgYpAigwhVHtOs+ZIUIdzQHvHeNd0ydc1Jg8e+C+Mlzo38OvaG
 D3qwvzJ0LNn7L80c0XVrvEALdD9zrO+0XSZpTK9PJrl2W59lZlJFUk3pV+jFR8NY
 eB/fto7AVtECAwEAAaOBzTCByjAdBgNVHQ4EFgQUv7TZGZaI+FifzjpTVjtPHSvb
 XqUwgZoGA1UdIwSBkjCBj4AUv7TZGZaI+FifzjpTVjtPHSvbXqWhbKRqMGgxCzAJ
 BgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEU
 MBIGA1UEChMLR29vZ2xlIEluYy4xDjAMBgNVBAsTBU9ya3V0MQ4wDAYDVQQDEwVs
 cnlhboIJAMbTCksqLiWeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEA
 CETnhlEnCJVDXoEtSSwUBLP/147sqiu9a4TNqchTHJObwTwDPUMaU6XIs2OTMmFu
 GeIYpkHXzTa9Q6IKlc7Bt2xkSeY3siRWCxvZekMxPvv7YTcnaVlZzHrVfAzqNsTG
 P3J//C0j+8JWg6G+zuo5k7pNRKDY76GxxHPYamdLfwk=
 -----END CERTIFICATE-----
 EOD;
     }
   }
 
   //Build a request object from the current request
   $request = OAuthRequest::from_request(null, null, array_merge($_GET, $_POST));
 
   //Initialize the new signature method
   $signature_method = new OrkutSignatureMethod();
 
   //Check the request signature
   @$signature_valid = $signature_method->check_signature($request, null, null, $_GET["oauth_signature"]);
 
   //Build the output object
   $payload = array();
   if ($signature_valid == true) {
     $payload["validated"] = "Success! The data was validated";
   } else {
     $payload["validated"] = "This request was spoofed";
   }
 
   //Add extra parameters to help debugging
   $payload["query"] = array_merge($_GET, $_POST);
   $payload["rawpost"] = file_get_contents("php://input");
 
   //Return the response as JSON
   print(json_encode($payload));

Results

When you run the client side script against the server code, the "response" function will be called with one of the following objects:

If the request was validated:

{ "validated" : "Success! The data was validated",
   "query" : { <all of the parameters passed in the query> }
 }

If the request was not validated:

{ "validated" : "This request was spoofed",
   "query" : { <all of the parameters passed in the query> }
 }

Response prints this out in the following format:

Success! The data was validated
oauth_consumer_key: orkut.com
oauth_nonce: c970afeb19325be3
oauth_signature: #### REALLY LONG STRING ###
oauth_signature_method: RSA-SHA1
oauth_timestamp: 1201225242
oauth_token:
opensocial_appid: ############
opensocial_ownerid: ############
xoauth_signature_publickey: pub.1199819524.-1556113204990931254.cer

Java

Client Side code

<?xml version="1.0" encoding="UTF-8"?>
 <Module>
  <ModulePrefs title="oAuth Java Example">
    <Require feature="opensocial-0.7"></Require>
    <Require feature="dynamic-height"></Require>
  </ModulePrefs>
  <Content type="html">
   <![CDATA[
    <script type="text/javascript">
      var servletUrl="http://oauthtest.s42.eatj.com/oauth/SignedFetchVerifyServlet";
 
      function response(data) {
        document.getElementById('dom_handle').innerHTML=data.text;
      };
 
      function request() {
        var params={};
        params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.SIGNED;
        gadgets.io.makeRequest(servletUrl,response,params);
      };
 
      gadgets.util.registerOnLoadHandler(request); 
    </script>
 
    <div id="dom_handle"></div>
   ]]>
   </Content>
 </Module>

Server side code

The code implements these steps:

  • Create a OAuthServiceProvider
  • Create a OAuthConsumer and populate the RSA Certficate with orkut's Certificate
  • Extract the OAuthMessage from the HttpServletRequest
  • Create a OAuthAccessor using the consumer
  • Checks the OAuthMessage using this accessor

This code relies on Java OAuth from the open source Opensocial Java Client.

This example is not meant to be production quality code - merely a demonstration of the steps you would take to validate a signed request server-side. You should not be inlining orkut's public key certificate in your production code.

package net.oauth.example.provider.servlets;
 
 import net.oauth.OAuth;
 import net.oauth.OAuthAccessor;
 import net.oauth.OAuthConsumer;
 import net.oauth.OAuthMessage;
 import net.oauth.OAuthProblemException;
 import net.oauth.OAuthServiceProvider;
 import net.oauth.OAuthValidator;
 import net.oauth.SimpleOAuthValidator;
 import net.oauth.server.OAuthServlet;
 import net.oauth.signature.RSA_SHA1;
 
 import java.util.ArrayList;
 import java.io.IOException;
 import java.util.Map;
 import java.io.PrintWriter;
 import java.util.List;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 public class SignedFetchVerifyServlet extends HttpServlet {
 
   private final static String CERTIFICATE =
       "-----BEGIN CERTIFICATE-----\n"
           + "MIIDHDCCAoWgAwIBAgIJAMbTCksqLiWeMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNV\n"
           + "BAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIG\n"
           + "A1UEChMLR29vZ2xlIEluYy4xDjAMBgNVBAsTBU9ya3V0MQ4wDAYDVQQDEwVscnlh\n"
           + "bjAeFw0wODAxMDgxOTE1MjdaFw0wOTAxMDcxOTE1MjdaMGgxCzAJBgNVBAYTAlVT\n"
           + "MQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChML\n"
           + "R29vZ2xlIEluYy4xDjAMBgNVBAsTBU9ya3V0MQ4wDAYDVQQDEwVscnlhbjCBnzAN\n"
           + "BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAseBXZ4NDhm24nX3sJRiZJhvy9eDZX12G\n"
           + "j4HWAMmhAcnm2iBgYpAigwhVHtOs+ZIUIdzQHvHeNd0ydc1Jg8e+C+Mlzo38OvaG\n"
           + "D3qwvzJ0LNn7L80c0XVrvEALdD9zrO+0XSZpTK9PJrl2W59lZlJFUk3pV+jFR8NY\n"
           + "eB/fto7AVtECAwEAAaOBzTCByjAdBgNVHQ4EFgQUv7TZGZaI+FifzjpTVjtPHSvb\n"
           + "XqUwgZoGA1UdIwSBkjCBj4AUv7TZGZaI+FifzjpTVjtPHSvbXqWhbKRqMGgxCzAJ\n"
           + "BgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEU\n"
           + "MBIGA1UEChMLR29vZ2xlIEluYy4xDjAMBgNVBAsTBU9ya3V0MQ4wDAYDVQQDEwVs\n"
           + "cnlhboIJAMbTCksqLiWeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEA\n"
           + "CETnhlEnCJVDXoEtSSwUBLP/147sqiu9a4TNqchTHJObwTwDPUMaU6XIs2OTMmFu\n"
           + "GeIYpkHXzTa9Q6IKlc7Bt2xkSeY3siRWCxvZekMxPvv7YTcnaVlZzHrVfAzqNsTG\n"
           + "P3J//C0j+8JWg6G+zuo5k7pNRKDY76GxxHPYamdLfwk=\n"
           + "-----END CERTIFICATE-----";
 
   @Override
   protected void doGet(HttpServletRequest req, HttpServletResponse resp)
       throws ServletException, IOException {
     verifyFetch(req, resp);
   }
 
 
   @Override
   protected void doPost(HttpServletRequest req, HttpServletResponse resp)
       throws ServletException, IOException {
     verifyFetch(req, resp);
   }
 
 
   private void verifyFetch(HttpServletRequest request, HttpServletResponse resp)
       throws IOException, ServletException {
     resp.setContentType("text/html; charset=UTF-8");
     PrintWriter out = resp.getWriter();
 
     try {
       OAuthServiceProvider provider =
           new OAuthServiceProvider(null, null, null);
       OAuthConsumer consumer =
           new OAuthConsumer(null, "orkut.com", null, provider);
       consumer.setProperty(RSA_SHA1.X509_CERTIFICATE, CERTIFICATE);
 
       String method = request.getMethod();
       String requestUrl = getRequestUrl(request);
       List<OAuth.Parameter> requestParameters = getRequestParameters(request);
 
       OAuthMessage message =
           new OAuthMessage(method, requestUrl, requestParameters);
 
       OAuthAccessor accessor = new OAuthAccessor(consumer);
       out.print("*** OAuthMessage Params:");
       out.print("<br>URL: " + OAuthServlet.htmlEncode(message.URL));
       for (java.util.Map.Entry param : message.getParameters()) {
         String key = param.getKey().toString();
         String value = param.getValue().toString();
         out.print("<br>");
         out.print("Param Name-->" + OAuthServlet.htmlEncode(key));
         out.print("&nbsp;");
         out.print("Value-->" + OAuthServlet.htmlEncode(value));
       }
       out.print("<br>");
       out.print(" VALIDATING SIGNATURE ");
       out.print("<br>");
       message.validateMessage( accessor, new SimpleOAuthValidator());
       out.print("REQUEST STATUS::OK");
       out.print("<br>");
     } catch (OAuthProblemException ope) {
       out.print("<br>");
       out.print("OAuthProblemException-->"
           + OAuthServlet.htmlEncode(ope.getProblem()));
      } catch (Exception e) {
     	 out.println(e);
     	 System.out.println(e);
       throw new ServletException(e);
     } finally {
       out.flush();   
     }
   }
 
   /**
     * Constructs and returns the full URL associated with the passed request
     * object.
     * 
     * @param  request Servlet request object with methods for retrieving the
     *         various components of the request URL
     */
   public static String getRequestUrl(HttpServletRequest request) {
     StringBuilder requestUrl = new StringBuilder();
     String scheme = request.getScheme();
     int port = request.getLocalPort();
 
     requestUrl.append(scheme);
     requestUrl.append("://");
     requestUrl.append(request.getServerName());
 
     if ((scheme.equals("http") && port != 80)
             || (scheme.equals("https") && port != 443)) {
       requestUrl.append(":");
       requestUrl.append(port);
     }
 
     requestUrl.append(request.getContextPath());
     requestUrl.append(request.getServletPath());
 
     return requestUrl.toString();
   }
 
   /**
    * Constructs and returns a List of OAuth.Parameter objects, one per
    * parameter in the passed request.
    * 
    * @param  request Servlet request object with methods for retrieving the
    *         full set of parameters passed with the request
    */
   public static List<OAuth.Parameter> getRequestParameters(
       HttpServletRequest request) {
 
     List<OAuth.Parameter> parameters = new ArrayList<OAuth.Parameter>();
 
     for (Object e : request.getParameterMap().entrySet()) {
       Map.Entry<String, String[]> entry = (Map.Entry<String, String[]>) e;
 
       for (String value : entry.getValue()) {
         parameters.add(new OAuth.Parameter(entry.getKey(), value));
       }
     }
 
     return parameters;
   }
 }

Results

When you run the client side script against the server code, and if the request was validated:

*** OAuthMessage Params:
URL: http://oauthtest.s42.eatj.com/oauth/SignedFetchVerifyServlet
Param Name-->oauth_consumer_key Value-->orkut.com
Param Name-->oauth_nonce Value-->7fe2ce6b24e17c86
Param Name-->opensocial_app_id Value-->10449685582340194994
Param Name-->opensocial_viewer_id Value-->10972140642666904206
Param Name-->oauth_timestamp Value-->1211519778
Param Name-->opensocial_owner_id Value-->10972140642666904206
Param Name-->oauth_signature Value-->eYwX2yWLpOZ+gb5oEFeIy+EM3j237nrj/1rj3yp69jgo
                                     /QaPk/OnXbXP9imEcERxtQLj9QqDD8cjbjMJ46VINc7b
                                     Lab8qER1Xhkf4tTIkwfFXJW9tjQMBuGO8OVf9v0UAjAr
                                     uWfSN331LLgGFecKQR5UTD0qAAyzTnFY9aSmqg8=
Param Name-->xoauth_signature_publickey Value-->pub.1199819524.-1556113204990931254.cer
Param Name-->oauth_token Value-->
Param Name-->oauth_signature_method Value-->RSA-SHA1
VALIDATING SIGNATURE
REQUEST STATUS::OK

Python

Client side code

Copied from http://opensocial.dachary.org/signed-python-test.xml

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
  <ModulePrefs title="Python Debug Server makeRequest" height="300" />
  <Content type="html">
     <![CDATA[
          <script language="JavaScript" type="text/javascript" src="/json2.js"></script>
          <script type="text/javascript">
function makeSignedRequest() {
   var params = {};
   params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.SIGNED;
   params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.JSON;
   var url = "http://opensocial.dachary.org/cgi-bin/fetchme.py";
   gadgets.io.makeRequest(url, response, params);
 };
 
 function response(ret) {
   if(window.console && window.console.log)
      window.console.log("ret = " + JSON.stringify(ret));
 
   var html = [ "<font size='-1'><b>", ret.data.validated, "</b><br />\n<ul><li>",
     "<b>opensocial_app_id</b>: ", ret.data.query.opensocial_app_id, "</li>\n<li>",
     "<b>opensocial_owner_id</b>: ", ret.data.query.opensocial_owner_id, "</li>\n<li>",
     "<b>opensocial_viewer_id</b>: ", ret.data.query.opensocial_viewer_id, "</li>\n<li>",
     "<b>oauth_consumer_key</b>: ", ret.data.query.oauth_consumer_key, "</li>\n<li>",
     "<b>oauth_nonce</b>: ", ret.data.query.oauth_nonce, "</li>\n<li>",
     "<b>oauth_signature</b>: ", ret.data.query.oauth_signature, "</li>\n<li>",
     "<b>oauth_signature_method</b>: ", ret.data.query.oauth_signature_method, "</li>\n<li>",
     "<b>oauth_timestamp</b>: ", ret.data.query.oauth_timestamp, "</li>\n<li>",
     "<b>oauth_token</b>: ", ret.data.query.oauth_token, "</li>\n<li>",
     "<b>xoauth_signature_publickey</b>: ", ret.data.query.xoauth_signature_publickey, "</li></ul></font>" ].join("");
 
   document.getElementById('console').innerHTML = html;
 
 };
 
 makeSignedRequest();
          </script>
          <div id="console">...loading...</div>
     ]]>
  </Content>
</Module>

Server side code

This code was copied from http://opensocial.dachary.org/fetchme.py and assumes http://opensocial-python-client.googlecode.com/files/opensocial-python-client-0.3.0.tar.bz or better with http://code.google.com/p/opensocial-python-client/issues/detail?id=28 patch applied (i.e. http://opensocial-python-client.googlecode.com/issues/attachment?aid=7986458758935386828&amp;name=p1)

A python library may be used instead of the suggested openssl command line code suggested in the boddy of the example http://code.google.com/p/kurrik-resources/source/browse/trunk/src/x509/x509.py

#!/usr/bin/python -u
 
import hashlib
import urllib
import cgi
import urlparse
import os
 
from opensocial import oauth
from opensocial import simplejson
from Crypto.PublicKey import RSA
 
# http://shindig.opensocial.dachary.org/public.cer
"""
-----BEGIN CERTIFICATE-----
MIIDmTCCAwKgAwIBAgIJAIpuyG3wc28PMA0GCSqGSIb3DQEBBQUAMIGQMQswCQYD
VQQGEwJGUjEOMAwGA1UECBMFUGFyaXMxDjAMBgNVBAcTBVBhcmlzMRQwEgYDVQQK
Ewtwb2tlcnNvdXJjZTEUMBIGA1UECxMLcG9rZXJzb3VyY2UxFDASBgNVBAMTC3Bv
a2Vyc291cmNlMR8wHQYJKoZIhvcNAQkBFhBsb2ljQGRhY2hhcnkub3JnMB4XDTA5
MDcwNjExMDE0MFoXDTEwMDcwNjExMDE0MFowgZAxCzAJBgNVBAYTAkZSMQ4wDAYD
VQQIEwVQYXJpczEOMAwGA1UEBxMFUGFyaXMxFDASBgNVBAoTC3Bva2Vyc291cmNl
MRQwEgYDVQQLEwtwb2tlcnNvdXJjZTEUMBIGA1UEAxMLcG9rZXJzb3VyY2UxHzAd
BgkqhkiG9w0BCQEWEGxvaWNAZGFjaGFyeS5vcmcwgZ8wDQYJKoZIhvcNAQEBBQAD
gY0AMIGJAoGBAMU5jnDTaadleTmZu46hwWPw1BoMvIPAbeV/Cisy2h5Syct4ulDm
AbB8+ZDM0lO74kXZK88IghGQnWGovs3OH9IGZRzeLx5DViC8qMZ3hq2KgiDbuMm/
Rf31Yz4Bq9dTluyDuQQzCM1XLDcMhIdu/mGPSj6hF2vTVNOop6p7VoLFAgMBAAGj
gfgwgfUwHQYDVR0OBBYEFCGh+YhwndgwJACXXaaa2cNrwzthMIHFBgNVHSMEgb0w
gbqAFCGh+YhwndgwJACXXaaa2cNrwzthoYGWpIGTMIGQMQswCQYDVQQGEwJGUjEO
MAwGA1UECBMFUGFyaXMxDjAMBgNVBAcTBVBhcmlzMRQwEgYDVQQKEwtwb2tlcnNv
dXJjZTEUMBIGA1UECxMLcG9rZXJzb3VyY2UxFDASBgNVBAMTC3Bva2Vyc291cmNl
MR8wHQYJKoZIhvcNAQkBFhBsb2ljQGRhY2hhcnkub3JnggkAim7IbfBzbw8wDAYD
VR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQBa8M5aVuUg5strfuVznZxm7rrX
acX25tvSYQ8PMrux8c8K1OPEzgRXSWTAt3mfmDVpcRPXIEQWpj+84qPpRuzc5K06
X8pEgt267U78TN8nHoPg/4qlD4dSbhReEafHtk8YSFgdjsUyhV9Qd34sVP2i24xl
5b6vXllgNnF+LedcMw==
-----END CERTIFICATE-----
"""
# 
# More information about x509 conversion to hex
# in http://www.guyrutenberg.com/2009/01/01/extract-public-key-from-x509-certificate-as-hex/
#
# curl http://shindig.opensocial.dachary.org/public.cer | openssl x509 -modulus -noout | sed s/Modulus=/0x/
#
dachary_hex_cert = "0xC5398E70D369A765793999BB8EA1C163F0D41A0CBC83C06DE57F0A2B32DA1E52C9CB78BA50E601B07CF990CCD253BBE245D92BCF088211909D61A8BECDCE1FD206651CDE2F1E435620BCA8C67786AD8A8220DBB8C9BF45FDF5633E01ABD75396EC83B9043308CD572C370C84876EFE618F4A3EA1176BD354D3A8A7AA7B5682C5"
 
# http://modules.partuza.nl/public.cer
"""
-----BEGIN CERTIFICATE-----
MIIDvzCCAyigAwIBAgIJAIqG9IruKSM7MA0GCSqGSIb3DQEBBAUAMIGcMQswCQYD
VQQGEwJOTDEVMBMGA1UECBMMWnVpZCBIb2xsYW5kMRIwEAYDVQQHEwlSb3R0ZXJk
YW0xEDAOBgNVBAoTB1BhcnR1emExEDAOBgNVBAsTB1BhcnR1emExHDAaBgNVBAMT
E3BhcnR1emEuY2hhYm90Yy5jb20xIDAeBgkqhkiG9w0BCQEWEWNoYWJvdGNAeHM0
YWxsLm5sMB4XDTA4MDUxNTE1NTcyMVoXDTA5MDUxNTE1NTcyMVowgZwxCzAJBgNV
BAYTAk5MMRUwEwYDVQQIEwxadWlkIEhvbGxhbmQxEjAQBgNVBAcTCVJvdHRlcmRh
bTEQMA4GA1UEChMHUGFydHV6YTEQMA4GA1UECxMHUGFydHV6YTEcMBoGA1UEAxMT
cGFydHV6YS5jaGFib3RjLmNvbTEgMB4GCSqGSIb3DQEJARYRY2hhYm90Y0B4czRh
bGwubmwwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALCupxFumTIbkEZUm/cb
j1XtE0+mhqwy+i4inOtPFFvsloA9GJf0CpaFOhLgt6XDRymGqkFHS+Ul1r4Kyzdv
jdNXlkC7DhcWNkO9gtOxTN89h1UXUTLYO+Ps7QLQks4BC1nGyw6RICYT+ommI77x
SiMmNBH4AZTe3fhrxX2Es+6VAgMBAAGjggEFMIIBATAdBgNVHQ4EFgQUUQvfhRl0
bqQGMPSYWLsk6NzTM9swgdEGA1UdIwSByTCBxoAUUQvfhRl0bqQGMPSYWLsk6NzT
M9uhgaKkgZ8wgZwxCzAJBgNVBAYTAk5MMRUwEwYDVQQIEwxadWlkIEhvbGxhbmQx
EjAQBgNVBAcTCVJvdHRlcmRhbTEQMA4GA1UEChMHUGFydHV6YTEQMA4GA1UECxMH
UGFydHV6YTEcMBoGA1UEAxMTcGFydHV6YS5jaGFib3RjLmNvbTEgMB4GCSqGSIb3
DQEJARYRY2hhYm90Y0B4czRhbGwubmyCCQCKhvSK7ikjOzAMBgNVHRMEBTADAQH/
MA0GCSqGSIb3DQEBBAUAA4GBAH7YvtrOU8WlD1DDW/zcI6usZgYt/VR9wXOCYpbG
4KWHXH242EWnRxO73aB5FfILTE1TBP5AFdpous47Fg41SSnjy5x0MtHCp61/w9gH
22k1Qm2uZEGzSxZLun2dyGBcFNKu7nC675P1FDjO/ITAFs/r8SOnmUa22Wa/vrUf
zvN/
-----END CERTIFICATE-----
"""
#
# curl http://modules.partuza.nl/public.cer | openssl x509 -modulus -noout | sed s/Modulus=/0x/
#
partuza_hex_cert = "0xB0AEA7116E99321B9046549BF71B8F55ED134FA686AC32FA2E229CEB4F145BEC96803D1897F40A96853A12E0B7A5C3472986AA41474BE525D6BE0ACB376F8DD3579640BB0E17163643BD82D3B14CDF3D8755175132D83BE3ECED02D092CE010B59C6CB0E91202613FA89A623BEF14A23263411F80194DEDDF86BC57D84B3EE95"
 
url2cert = {
    'http://modules.partuza.nl/public.cer': partuza_hex_cert,
    'http://shindig.opensocial.dachary.org/public.cer': dachary_hex_cert
}
 
print "Content-Type: text/plain\n\n"
 
url = 'http://' + (os.environ.get('HTTP_X_FORWARDED_SERVER') or os.environ.get('HTTP_HOST', 'localhost')) + os.environ.get('REQUEST_URI', '/')
oauth_request = oauth.OAuthRequest.from_request(os.environ.get('REQUEST_METHOD', 'GET'), url)
signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
key, raw = signature_method.build_signature_base_string(oauth_request, oauth.OAuthConsumer('key', 'secret'), None)
 
local_hash = hashlib.sha1(raw).digest()
 
qs = cgi.parse_qs(urlparse.urlparse(url)[4])
oauth_request.parameters['oauth_signature'] = urllib.unquote(qs["oauth_signature"][0])
signature = oauth_request.parameters['oauth_signature'].decode('base64')
 
cert_url = urllib.unquote(qs.get('xoauth_signature_publickey', ['xoauth_signature_publickey missing from QUERY_STRING'])[0])
 
info = {}
info['query'] = oauth_request.parameters
info['url'] = url
 
if cert_url in url2cert:
    hex_cert = url2cert[cert_url]
 
    exponent = 65537
    public_key_long = long(hex_cert, 16)
    public_key = RSA.construct((public_key_long, exponent))
 
    remote_hash = public_key.encrypt(signature, '')[0][-20:]
 
    if local_hash == remote_hash:
        info['validated'] = 'verified '
    else:
        info['validated'] = 'not verified '
else:
    info['validated'] = cert_url + ' is not among the known cert urls (' + str(url2cert.keys()) + ') '
info['validated'] +=  raw + ' from URL ' + url
 
print simplejson.dumps(info)
 
# will run and *not* validate
# REQUEST_URI='/foo?oauth_signature=YWJjCg==&xoauth_signature_publickey=http://shindig.opensocial.dachary.org/public.cer' fetchme.py

C#

Server side code

This code relies on the csharp OAuth library from the open source OAuth library project.

This example is not meant to be production quality code - merely a demonstration of the steps you would take to validate a signed request server-side. You should not be inlining orkut's public key certificate in your production code.

// -----------------------------------------------------------------------------------
//
//  OBasePage.cs to validate oAuth signature - .NET 
//  by XtremeHeights
//  Last Modification: 4th Oct 2008
//
//  For more information, visit:
//  http:/www.xtremeheights.com/
//  email: contact@xtremeheights.com
//
//	
//  Thanks: 
//  Khushal Patel for creating plug & plug code for implementing oAuth in .NET
//  Nemesh Singh for fixing the code which started spoofing every request after 25th sep 2008
//
// -----------------------------------------------------------------------------------
 
using System;
using System.Collections;
using System.Configuration;
using System.Web;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Collections.Specialized;
using OAuth;
 
public class oBasePage : System.Web.UI.Page
{
  HttpContext htp;
  public oBasePage(HttpContext _htp)
  {
    htp = _htp;
  }
 
  public bool isvalidrequest()
  {
    //If you want to import certificate file directly then uncomment the below line and comment the certificate
    //X509Certificate Cert = X509Certificate.CreateFromCertFile(htp.Request.PhysicalApplicationPath + "/bin/pub.1199819524.-1556113204990931254.cer");
 
    X509Certificate2 cert = new X509Certificate2();
    cert.Import(Encoding.ASCII.GetBytes(
        @"-----BEGIN CERTIFICATE-----
        MIIDHDCCAoWgAwIBAgIJAMbTCksqLiWeMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNV
        BAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIG
        A1UEChMLR29vZ2xlIEluYy4xDjAMBgNVBAsTBU9ya3V0MQ4wDAYDVQQDEwVscnlh
        bjAeFw0wODAxMDgxOTE1MjdaFw0wOTAxMDcxOTE1MjdaMGgxCzAJBgNVBAYTAlVT
        MQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChML
        R29vZ2xlIEluYy4xDjAMBgNVBAsTBU9ya3V0MQ4wDAYDVQQDEwVscnlhbjCBnzAN
        BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAseBXZ4NDhm24nX3sJRiZJhvy9eDZX12G
        j4HWAMmhAcnm2iBgYpAigwhVHtOs+ZIUIdzQHvHeNd0ydc1Jg8e+C+Mlzo38OvaG
        D3qwvzJ0LNn7L80c0XVrvEALdD9zrO+0XSZpTK9PJrl2W59lZlJFUk3pV+jFR8NY
        eB/fto7AVtECAwEAAaOBzTCByjAdBgNVHQ4EFgQUv7TZGZaI+FifzjpTVjtPHSvb
        XqUwgZoGA1UdIwSBkjCBj4AUv7TZGZaI+FifzjpTVjtPHSvbXqWhbKRqMGgxCzAJ
        BgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEU
        MBIGA1UEChMLR29vZ2xlIEluYy4xDjAMBgNVBAsTBU9ya3V0MQ4wDAYDVQQDEwVs
        cnlhboIJAMbTCksqLiWeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEA
        CETnhlEnCJVDXoEtSSwUBLP/147sqiu9a4TNqchTHJObwTwDPUMaU6XIs2OTMmFu
        GeIYpkHXzTa9Q6IKlc7Bt2xkSeY3siRWCxvZekMxPvv7YTcnaVlZzHrVfAzqNsTG
        P3J//C0j+8JWg6G+zuo5k7pNRKDY76GxxHPYamdLfwk=
        -----END CERTIFICATE-----"));
 
    //Getting Post variables
    string http_params;
    NameValueCollection _p = htp.Request.Form;
    Hashtable _hp = new Hashtable();
 
    foreach (string k in _p.Keys)
    {
      if (k != null)
      {
        _hp[k] = _p[k];
      }
    }
 
    SortedList _ps = new SortedList((IDictionary)_hp);
 
    string[] pairs1 = new string[_ps.Count];
    int l = 0;
 
    foreach (string name in _ps.Keys)
    {
      pairs1[l++] = name + "=" + Uri.EscapeDataString(_ps[name].ToString());
    }
    http_params = String.Join("&", pairs1);
 
    if (_ps.Count != 0)
    {
      http_params = "&" + http_params;
    }
 
    /* RSACryptoServiceProvider Provider = CertUtil.GetCertPublicKey(Cert); // if importing the file directly... */
    RSACryptoServiceProvider Provider = (RSACryptoServiceProvider)cert.PublicKey.Key;
    OAuth.OAuthBase ba = new OAuthBase();
 
    string signature = htp.Request.QueryString["oauth_signature"];
 
    string baseString = ba.GenerateSignatureBase(htp.Request.Url,
                                                 htp.Request.QueryString["oauth_consumer_key"],
                                                 "",
                                                 htp.Request.QueryString["oauth_token"],
                                                 "",
                                                 htp.Request.HttpMethod,
                                                 htp.Request.QueryString["oauth_timestamp"],
                                                 htp.Request.QueryString["oauth_nonce"],
                                                 "RSA-SHA1",
                                                 http_params);
 
    byte[] sign = Convert.FromBase64String(signature);
    byte[] bstring = Encoding.UTF8.GetBytes(baseString);
    htp.Response.Write(htp.Request.QueryString["oauth_token"]);
 
    return Provider.VerifyData(bstring, "SHA1", sign);
  }
}

If you have any comments or questions about this page, please discuss them in the OpenSocial developer forum.