メモ代わり。てきとーに。 いや、ですからてきとーですって。 2年前ぐらいにPythonあたりでメールくれた方、ごめんなさい。メール紛失してしまい無視した形になってしまいました。。。

2009年8月2日日曜日

[Apache Shindig][お勉強][OpenSocial] メモ118 gadgets.io.makeRequest 認証認可タイプSIGNEDをやってみる(RSA-SHA1で署名)

makeRequestでsignatureをリクエストにつけて通信することができるとのこと。
これもOAuthの一種っぽい。

ということでMy Shindigでもやってみる。
参考にしたサイトはhttp://wiki.opensocial.org/index.php?title=Validating_Signed_Requests

Shindigでは、RSA-SHA1かHMAC-SHA1が使えるらしい。

まず、shindigでoauthを使えるようにセットアップする。


config/oauth.json

に設定できる。

oauth.jsonの中身は、デフォルトでは

{
"http://localhost:8080/gadgets/files/samplecontainer/examples/oauth.xml" : {
"" : {
"consumer_key" : "gadgetConsumer",
"consumer_secret" : "gadgetSecret",
"key_type" : "HMAC_SYMMETRIC"
}
},
"http://localhost:8080/gadgets/files/samplecontainer/examples/shindigoauth.xml" : {
"shindig" : {
"consumer_key" : "http://localhost:8080/gadgets/files/samplecontainer/examples/shindigoauth.xml",
"consumer_secret" : "secret",
"key_type" : "HMAC_SYMMETRIC"
}
}
}

なんてなふうになっているんで、自分のガジェット用定義にする。

で、下記自分用。

{
"http://localhost/opensocial/oauth.xml" : {
"" : {
"consumer_key" : "gadgetConsumer",
"consumer_secret" : "gadgetSecret",
"key_type" : "RSA_PRIVATE"
}
}
}

最初のURLの"http://localhost:8080/opensocial/oauth.xml"は、
ガジェットのURL。shindigはmakeRequestをSIGNEDで受け取ると、
現在実行中のガジェットのURLをキーに、oauth.jsonの中身を探しにいく。

consumer_keyというのは、どこのコンテナから送られてきたリクエストかを指し示す。
consumer_secretというのは、key_typeにHMAC_SYMMETRICを指定した場合の、先方と共有すべき
パスワード、みたいなものらしい。
RSA_PRIVATEの場合、consumer_secretには、BasicOAuthStore.java(デフォルト)を使うのであれば、opensslで作成した証明書の秘密鍵を指定する。


次は、証明書と秘密鍵の作成。
特に設定しないで起動すると、ログに

Couldn't load OAuth signing key. To create a key, run:
openssl req -newkey rsa:1024 -days 365 -nodes -x509 -keyout testkey.pem \
-out testkey.pem -subj '/CN=mytestkey'
openssl pkcs8 -in testkey.pem -out oauthkey.pem -topk8 -nocrypt -outform PEM

Then edit gadgets.properties and add these lines:
shindig.signing.key-file=
shindig.signing.key-name=mykey


などと出力されているんで、そのとおりにopnessl、設定を実行。

$ openssl req -newkey rsa:1024 -days 365 -nodes -x509 -keyout testkey.pem -out testkey.pem -subj '/CN=mytestkey'
$ openssl pkcs8 -in testkey.pem -out oauthkey.pem -topk8 -nocrypt -outform PEM

で、できあがったファイルをWEB-INFの直下に配置。(getResourceで見える場所ならどこでも良い)

$ cp ./oauthkey.pem /tmp/

で、shindig.propertiesを修正。

shindig.signing.key-file=/tmp/oauthkey.pem
shindig.signing.key-name=mytestkey

と。

さらに、oauthkey.pemをoauth.jsonのconsumer_secretにセットする。

"http://localhost/opensocial/hello.xml" : {
"" : {
"consumer_key" : "gadgetConsumer",
"consumer_secret": "-----BEGIN PRIVATE KEY-----\nMIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAMMTit67BWHr6lY/\nsIGrNljDff5ZG6M5RCSDvIwHKwmhMFIRE1rhfdzmxst1wpih6X4D/r9W6rMVqMEz\nD8UVz+D7guS6VAJLzOVMi08bo3aOvUuTbnqt6vsVTO41wwPkoHzBWkQsIk5BLBia\neDr/nTR0Hih8cHCDo8KOjyl6lUJBAgMBAAECgYAx/Fr0MaXybnoQeadnwpBddFJf\nj0O2smDUnnD8zIVppUT1cmysN+WBahz8W4NuiuaCpNFtA/+FvgIM52GeFbW7NInS\nkCKQ/d0IxFII9qiE/VHGiSIx2tM978LtEZ90oUDcBSfOfkmuAiZmNNa7MPvxzXBH\n6ykLtK6I0Jdsc7vutQJBAPX6waYp8oyjR/ju6dN/BpYknzeTcH4xV6sX1gtAD1SR\noFpui5VgkPrTAHsHNHNlEakH+olzOyW/kEmteg4WiUMCQQDLBe7CrkZQt1UjhIij\nazE2TzpNHkxocsFa27vp3fnfSo3U+RxsX+Veyz09SoSEVfj40Zbaz9u35BUDV5Hk\nlbwrAkA870iHjD6svko3DaBZJ0+PZDUZBGpKqJiKJtZ8ZPsnPQgXPulPMOPZbung\nkFFbAL+WOwbChAwz9recuN/Sm5yXAkAr4LhdcvTXdQf9iSNOzlfweOcMy8OPv11y\nmZLfVI4waBiHMt+lvIQCHMK7gGNrhnksrX+BAvNlpvGzqzVoxcPJAkA4m5u5skPY\nmbpIPAPda/lr8XN88U5QROr/LdWeu46WO/402U86R+cVRwM/m7WVHAC8MCxzQ+8U\nDOWOrYDKefew\n-----END PRIVATE KEY-----",
"key_type" : "RSA_PRIVATE"
}
},

な感じ。(BasicOAuthStoreを使用の場合。デフォルト)
BasicOAuthStoreは実運用には耐えられるものではないとshindig-devのメーリングリストで
見つけた。実運用する際には別途実装せよ、とのこと。

でコンパイル。で、起動。


今度は通信先のサーブレットを実装。
http://wiki.opensocial.org/index.php?title=Validating_Signed_Requests
にあるものそのままだけど、一応コピペ。
そのままじゃ動かないので、一部現状のOAuthライブラリに合わせて修正。
さらに環境に合わせて修正。

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.OAuthValidator;
import net.oauth.SimpleOAuthValidator;
/* ここまで修正 */
import net.oauth.OAuthProblemException;
import net.oauth.OAuthServiceProvider;
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);
/* ここから修正(oauth.jsonに書いたconsumerKeyをセット) */
OAuthConsumer consumer =
new OAuthConsumer(null, "gadgetConsumer", null, provider);
/* 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 + "/SignedFetchVerifyServlet", requestParameters);
/*
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(" ");
out.print("Value-->" + OAuthServlet.htmlEncode(value));
}
out.print("<br>");
out.print(" VALIDATING SIGNATURE ");
out.print("<br>");
/* ここから修正 */
OAuthValidator validator = new SimpleOAuthValidator();
message.validateMessage(accessor, validator);
/* message.validateSignature(accessor); */
/* ここまで修正 */
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;
}


で、CERTIFICATEの値をさっきつくったtestkey.pemの
BEGIN CERTIRICATEの方、証明書をコピペ。

で、コンパイル。OAuthライブラリは、
http://code.google.com/p/oauth/
にある。

クラスパス通してコンパイル。


次にSINGEDでリクエストするガジェットを用意。

<?xml version="1.0" encoding="UTF-8"?>
<Module>
<ModulePrefs title="oAuth Java Example">
<Require feature="opensocial-0.8"></Require>
<Require feature="dynamic-height"></Require>
</ModulePrefs>
<Content type="html">
<![CDATA[
<script type="text/javascript">
var servletUrl="http://localhost/oauth/SignedFetchVerifyServlet";

function response(data) {
document.getElementById('dom_handle').innerHTML=data.text;
};

function request() {
var params={};
params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.TEXT;
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>


な感じ。

実行させると、、、

*** OAuthMessage Params:
URL: http://localhost/oauth/SignedFetchVerifyServlet
Param Name-->opensocial_viewer_id Value-->root
Param Name-->oauth_signature Value-->uR7NP8VHHIvUTVl6wQI6iDUtp25DSrg1zrdfzLByvLR1/4xE2aT3+J+l9aGDBQGO8jFky9qC6CEQE1UFruif4cSDXY7PhQZZnCcaNxPLvPMmknc2lNafYt1h0xlTCZ1YupgwCXegCYTCrEcONTD2X+xPs9amKFIcRTpsHCojNb4=
Param Name-->oauth_body_hash Value-->2jmj7l5rSw0yVb/vlWAYkK/YBwk=
Param Name-->oauth_nonce Value-->1249149514626651000
Param Name-->oauth_version Value-->1.0
Param Name-->oauth_signature_method Value-->RSA-SHA1
Param Name-->oauth_consumer_key Value-->gadgetConsumer
Param Name-->opensocial_owner_id Value-->root
Param Name-->opensocial_app_id Value-->http://localhost/opensocial/hello.xml
Param Name-->opensocial_app_url Value-->http://localhost/opensocial/hello.xml
Param Name-->oauth_timestamp Value-->1249149514
VALIDATING SIGNATURE
REQUEST STATUS::OK


なんてなふうに表示される。

完璧。これで、
RSA-SHA1での署名付きリクエスト、
さらにはRSA-SHA1の署名VERIFYまでできた。


--
というか、config/oauth.jsonとshindig.propertiesの両方に秘密鍵指定しないと
動かないのは、なんか変。何か間違えたかも。
.

0 コメント: