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
が含まれます