javaで署名付きリクエストを検証するプログラム

mixiアプリで外部サーバへのアクセスをするときに便利なのが署名付きリクエストです
mixi側のリクエストパラメータに公開鍵暗号を付加し、こちら側でその内容が正しいかどうかを判断することによってmixiアプリからのリクエストであることを保障しています

説明はどうでもいいのでとりあえずソースを
ちなみにこれはOAuthライブラリを利用するのでこちらからダウンロードしてきてください
OAuthのライブラリがおいてあるページに行くとバージョンが明記されてないんですがmixiにおいてあるソースとだいぶ違ったので中身が少しずつ変わっているのでしょうかね・・・?


mixiデベロッパーセンターにあるやつをコピペしても動かなかったのでソースを修正しました

OAuthValidation.java

package net.natsume.lib;

import net.oauth.OAuth;
import net.oauth.OAuthAccessor;
import net.oauth.OAuthConsumer;
import net.oauth.OAuthException;
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.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MixiOAuthValidation{
        //mixiアプリの署名付きパラメータでの公開鍵
	private final static String CERTIFICATE =
	    "-----BEGIN CERTIFICATE-----\n"
	    + "MIICdzCCAeCgAwIBAgIJAOi/chE0MhufMA0GCSqGSIb3DQEBBQUAMDIxCzAJBgNV\n"
	    + "BAYTAkpQMREwDwYDVQQKEwhtaXhpIEluYzEQMA4GA1UEAxMHbWl4aS5qcDAeFw0w\n"
	    + "OTA0MjgwNzAyMTVaFw0xMDA0MjgwNzAyMTVaMDIxCzAJBgNVBAYTAkpQMREwDwYD\n"
	    + "VQQKEwhtaXhpIEluYzEQMA4GA1UEAxMHbWl4aS5qcDCBnzANBgkqhkiG9w0BAQEF\n"
	    + "AAOBjQAwgYkCgYEAwEj53VlQcv1WHvfWlTP+T1lXUg91W+bgJSuHAD89PdVf9Ujn\n"
	    + "i92EkbjqaLDzA43+U5ULlK/05jROnGwFBVdISxULgevSpiTfgbfCcKbRW7hXrTSm\n"
	    + "jFREp7YOvflT3rr7qqNvjm+3XE157zcU33SXMIGvX1uQH/Y4fNpEE1pmX+UCAwEA\n"
	    + "AaOBlDCBkTAdBgNVHQ4EFgQUn2ewbtnBTjv6CpeT37jrBNF/h6gwYgYDVR0jBFsw\n"
	    + "WYAUn2ewbtnBTjv6CpeT37jrBNF/h6ihNqQ0MDIxCzAJBgNVBAYTAkpQMREwDwYD\n"
	    + "VQQKEwhtaXhpIEluYzEQMA4GA1UEAxMHbWl4aS5qcIIJAOi/chE0MhufMAwGA1Ud\n"
	    + "EwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAR7v8eaCaiB5xFVf9k9jOYPjCSQIJ\n"
	    + "58nLY869OeNXWWIQ17Tkprcf8ipxsoHj0Z7hJl/nVkSWgGj/bJLTVT9DrcEd6gLa\n"
	    + "H5TbGftATZCAJ8QJa3X2omCdB29qqyjz4F6QyTi930qekawPBLlWXuiP3oRNbiow\n"
	    + "nOLWEi16qH9WuBs=\n"
	    + "-----END CERTIFICATE-----";

	  public static Map<String, String> verifyFetch(
			  HttpServletRequest request, HttpServletResponse resp,
			  java.util.Map<String, String> confirm)
	          throws IOException, ServletException, OAuthProblemException {
		  Map<String, String> parameter = null;
		    try {
			      OAuthServiceProvider provider = new OAuthServiceProvider(null, null, null);
			      OAuthConsumer consumer = new OAuthConsumer(null, "mixi.jp", null, provider);
			      consumer.setProperty(RSA_SHA1.X509_CERTIFICATE, CERTIFICATE);

			      String method = request.getMethod();
			      String requestUrl = getRequestUrl(request);
			      List requestParameters = getRequestParameters(request);
			      
			      //ここが変わった
			      OAuthMessage message = OAuthServlet.getMessage(request, null);
			      //OAuthMessage message = new OAuthMessage(method, requestUrl+"/test", requestParameters);
			      
			      //ここも変わった
			      OAuthValidator validator = null;
			      validator = new SimpleOAuthValidator();
			      
			      //ここも変わった
			      OAuthAccessor accessor = new OAuthAccessor(consumer);
			      
			      //ここも変わった
			      //リクエスト元URL
			      OAuthServlet.htmlEncode(message.URL);
			      String conf = null;
			      parameter = new HashMap<String, String>();
			      for (java.util.Map.Entry<String,String> param : message.getParameters()) {
				        String key = param.getKey().toString();
				        String value = (String)param.getValue();
				        //パラメータのキー
				        OAuthServlet.htmlEncode(key);
				        //バリュー
				        OAuthServlet.htmlEncode(value);
				        

				        parameter.put(key, value);
				        //比較したいvalueを検証
				        if(confirm.get(key) != null){
				        	conf = confirm.get(key);
					        if(!value.equals(conf)){
					        	OAuthProblemException problem = new OAuthProblemException("Disagreement:" + key +" --> " + value +" == "+ conf);
					        	throw problem;
					        }
				        }
			      }

			      //VALIDATING SIGNATURE
			      validator.validateMessage(message, accessor);
			      /*
			      if (!Boolean.TRUE.equals(accessor.getProperty("authorized"))) {
			          OAuthProblemException problem = new OAuthProblemException("permission_denied");
			         throw problem;
			     }*/


			//      message.validateMessage(accessor, validator);

		    }catch(IOException e){
		    	e.printStackTrace();
		    }catch(URISyntaxException e){
		    	e.printStackTrace();
		    }catch(OAuthException e){
		    	OAuthProblemException ex = new OAuthProblemException("error:invalid access");
		    	throw ex;
		    }
		    return parameter;
	  }

	  /**
	   * 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 getRequestParameters(HttpServletRequest request) {

		  List parameters = new ArrayList();

		    for (Object e : request.getParameterMap().entrySet()) {
		    	Map.Entry entry = (Map.Entry) e;

		//      System.out.println("entry="+entry.getKey().toString()+"\t"+entry.getValue().toString());
		//      for (String value : entry.getValue()) {

			      String key =null;
			      String value = null;
			      String[] values = null;
			      if (entry.getKey() instanceof String) {
			    	  key = (String) entry.getKey();
			      }else{
			    	  key =entry.getKey().toString();
			      }
			      if (entry.getValue() instanceof String[]) {
			    	  values = (String[]) entry.getValue();
			      }

			      for(String v:values){
			          parameters.add(new OAuth.Parameter(key,v));
			      }
		    }

		    return parameters;
	  }
}


とりあえずこんなかんじです
mixiに張ってあった元のソースで間違いがあるところはコメントアウトしています

ちなみにこれはちょっと改造してあって
署名付きリクエストを検証したい場所でこのclassファイルをimportし

try{
  Map<String, String> confirm = new HashMap<String, String>();
  confirm.put("opensocial_app_id", "****");

  MixiOAuthValidation.verifyFetch(req, res, confirm);
}catch(OAuthProblemException e){
  e.printStackTrace();
}

のようにMapにパラメータで渡されるkeyとvalueを入れておくと、指定したvalue以外の値が入っていた場合にOAuthProblemExceptionをthrowするように書き換えてあります
この例の場合はmixiアプリのアプリIDが****番以外からのアクセスがあったときにもエラーが帰ってきます
このソースは自分用にいろいろ書き換えちゃってます。リクエストパラメータを使いたかったのreturnでkeyとvalueを返すようにとか・・・

ちなみにリクエストパラメータのkeyには

oauth_consumer_key
oauth_nonce
opensocial_app_id
opensocial_viewer_id
oauth_timestamp
opensocial_owner_id
oauth_signature
xoauth_signature_publickey
oauth_token
oauth_signature_method

が含まれます