Dealing with OpenId(3)JOpenId
1. Some Test in TestMain.java
package com.sillycat.easyopenidgoogle.jopenid;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.expressme.openid.Association;
import org.expressme.openid.Authentication;
import org.expressme.openid.Endpoint;
import org.expressme.openid.OpenIdManager;
public class TestMain {
public static void main(String[] args) throws Exception {
// set proxy if needed:
// java.util.Properties props = System.getProperties();
// props.put("proxySet", "true");
// props.put("proxyHost", "host");
// props.put("proxyPort", "port");
OpenIdManager manager = new OpenIdManager();
manager.setReturnTo("http://localhost/openId");
manager.setRealm("http://localhost");
manager.setTimeOut(10000);
Endpoint endpoint = manager.lookupEndpoint("Google");
System.out.println(endpoint);
Association association = manager.lookupAssociation(endpoint);
System.out.println(association);
String url = manager.getAuthenticationUrl(endpoint, association);
System.out.println("Copy the authentication URL in browser:\n" + url);
System.out
.println("After successfully sign on in browser, enter the URL of address bar in browser:");
String ret = readLine();
HttpServletRequest request = createRequest(ret);
Authentication authentication = manager.getAuthentication(request,
association.getRawMacKey(), endpoint.getAlias());
System.out.println(authentication);
}
static String readLine() throws IOException {
BufferedReader r = new BufferedReader(new InputStreamReader(System.in));
return r.readLine();
}
static HttpServletRequest createRequest(String url)
throws UnsupportedEncodingException {
int pos = url.indexOf('?');
if (pos == (-1))
throw new IllegalArgumentException("Bad url.");
String query = url.substring(pos + 1);
String[] params = query.split("[\\&]+");
final Map<String, String> map = new HashMap<String, String>();
for (String param : params) {
pos = param.indexOf('=');
if (pos == (-1))
throw new IllegalArgumentException("Bad url.");
String key = param.substring(0, pos);
String value = param.substring(pos + 1);
map.put(key, URLDecoder.decode(value, "UTF-8"));
}
return (HttpServletRequest) Proxy.newProxyInstance(
TestMain.class.getClassLoader(),
new Class[] { HttpServletRequest.class },
new InvocationHandler() {
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
if (method.getName().equals("getParameter"))
return map.get((String) args[0]);
throw new UnsupportedOperationException(method
.getName());
}
});
}
}
The pom.xml is as follow:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.expressme</groupId>
<artifactId>JOpenId</artifactId>
<version>1.08</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
</dependencies>
2. How to manage the Servlet
package com.sillycat.easyopenidgoogle.jopenid;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.expressme.openid.Association;
import org.expressme.openid.Authentication;
import org.expressme.openid.Endpoint;
import org.expressme.openid.OpenIdException;
import org.expressme.openid.OpenIdManager;
public class TestMainServlet extends HttpServlet {
private static final long serialVersionUID = 7513752565065680936L;
static final long ONE_HOUR = 3600000L;
static final long TWO_HOUR = ONE_HOUR * 2L;
static final String ATTR_MAC = "openid_mac";
static final String ATTR_ALIAS = "openid_alias";
OpenIdManager manager;
public void init(ServletConfig config) throws ServletException {
super.init(config);
manager = new OpenIdManager();
manager.setRealm("http://localhost"); // change to your domain
manager.setReturnTo("http://localhost/servlet-mapping");
// change to your SERVLET URL
}
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
String op = request.getParameter("op");
if (op == null) {
// check nonce:
checkNonce(request.getParameter("openid.response_nonce"));
// get authentication:
byte[] mac_key = (byte[]) request.getSession().getAttribute(
ATTR_MAC);
String alias = (String) request.getSession().getAttribute(
ATTR_ALIAS);
Authentication authentication = manager.getAuthentication(request,
mac_key, alias);
String identity = authentication.getIdentity();
String email = authentication.getEmail();
// TODO: create user if not exist in database:
showAuthentication(response.getWriter(), identity, email);
} else if ("Google".equals(op)) {
// redirect to Google sign on page:
Endpoint endpoint = manager.lookupEndpoint("Google");
Association association = manager.lookupAssociation(endpoint);
request.getSession().setAttribute(ATTR_MAC,
association.getRawMacKey());
request.getSession().setAttribute(ATTR_ALIAS, endpoint.getAlias());
String url = manager.getAuthenticationUrl(endpoint, association);
response.sendRedirect(url);
} else {
throw new ServletException("Bad parameter op=" + op);
}
}
void showAuthentication(PrintWriter pw, String identity, String email) {
pw.print("<html><body><h1>Identity</h1><p>");
pw.print(identity);
pw.print("</p><h1>Email</h1><p>");
pw.print(email == null ? "(null)" : email);
pw.print("</p></body></html>");
pw.flush();
}
void checkNonce(String nonce) {
// check response_nonce to prevent replay-attack:
if (nonce == null || nonce.length() < 20)
throw new OpenIdException("Verify failed.");
long nonceTime = getNonceTime(nonce);
long diff = System.currentTimeMillis() - nonceTime;
if (diff < 0)
diff = (-diff);
if (diff > ONE_HOUR)
throw new OpenIdException("Bad nonce time.");
if (isNonceExist(nonce))
throw new OpenIdException("Verify nonce failed.");
storeNonce(nonce, nonceTime + TWO_HOUR);
}
boolean isNonceExist(String nonce) {
// TODO: check if nonce is exist in database:
return false;
}
void storeNonce(String nonce, long expires) {
// TODO: store nonce in database:
}
long getNonceTime(String nonce) {
try {
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse(
nonce.substring(0, 19) + "+0000").getTime();
} catch (ParseException e) {
throw new OpenIdException("Bad nonce time.");
}
}
}
configuration for servlet
<p>Please sign on from:<p>
<p><a href="/openid?op=Google">Google Account</a></p>
<p><a href="/openid?op=Yahoo">Yahoo Account</a></p>
https://www.google.com/accounts/o8/id
3. Read some document about reply attack
http://en.wikipedia.org/wiki/Replay_attack
http://en.wikipedia.org/wiki/One-time_password
This is the reponse from google
http://localhost/openId?openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.mode=id_res&openid.op_endpoint=https%3A%2F%2Fwww.google.com%2Faccounts%2Fo8%2Fud&openid.response_nonce=2012-05-25T06%3A50%3A29ZHbJr3Mc8ITewRQ&openid.return_to=http%3A%2F%2Flocalhost%2FopenId&openid.assoc_handle=AMlYA9Uc9CjMYSuX-BRqwwYd0twJXgngATm6daS2QUopgMCMJIZnE4Ur&openid.signed=op_endpoint%2Cclaimed_id%2Cidentity%2Creturn_to%2Cresponse_nonce%2Cassoc_handle%2Cns.ext1%2Cext1.mode%2Cext1.type.firstname%2Cext1.value.firstname%2Cext1.type.email%2Cext1.value.email%2Cext1.type.language%2Cext1.value.language%2Cext1.type.lastname%2Cext1.value.lastname&openid.sig=E15gmFvLmu2CgRD%2Fx%2F134N%2FlL3w%3D&openid.identity=https%3A%2F%2Fwww.google.com%2Faccounts%2Fo8%2Fid%3Fid%3DAItOawl9BR07ZOQPW82cBnEo8hPjWZfX7d6xHwU&openid.claimed_id=https%3A%2F%2Fwww.google.com%2Faccounts%2Fo8%2Fid%3Fid%3DAItOawl9BR07ZOQPW82cBnEo8hPjWZfX7d6xHwU&openid.ns.ext1=http%3A%2F%2Fopenid.net%2Fsrv%2Fax%2F1.0&openid.ext1.mode=fetch_response&openid.ext1.type.firstname=http%3A%2F%2Faxschema.org%2FnamePerson%2Ffirst&openid.ext1.value.firstname=carl&openid.ext1.type.email=http%3A%2F%2Faxschema.org%2Fcontact%2Femail&openid.ext1.value.email=4myhappylife%40gmail.com&openid.ext1.type.language=http%3A%2F%2Faxschema.org%2Fpref%2Flanguage&openid.ext1.value.language=zh-CN&openid.ext1.type.lastname=http%3A%2F%2Faxschema.org%2FnamePerson%2Flast&openid.ext1.value.lastname=luo
We will check the nonce to prevent from replay attack.
references:
http://hi.baidu.com/luohuazju/blog/item/fd6e0a503f109f748435245b.html
http://hi.baidu.com/luohuazju/blog/item/ced42801a543bc15728da525.html
http://code.google.com/p/jopenid/
http://code.google.com/p/jopenid/wiki/Download
http://code.google.com/p/openid4java/wiki/SRegHowTo
http://code.google.com/p/jopenid/wiki/QuickStart
http://code.google.com/p/jopenid/wiki/DevGuide
1. Some Test in TestMain.java
package com.sillycat.easyopenidgoogle.jopenid;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.expressme.openid.Association;
import org.expressme.openid.Authentication;
import org.expressme.openid.Endpoint;
import org.expressme.openid.OpenIdManager;
public class TestMain {
public static void main(String[] args) throws Exception {
// set proxy if needed:
// java.util.Properties props = System.getProperties();
// props.put("proxySet", "true");
// props.put("proxyHost", "host");
// props.put("proxyPort", "port");
OpenIdManager manager = new OpenIdManager();
manager.setReturnTo("http://localhost/openId");
manager.setRealm("http://localhost");
manager.setTimeOut(10000);
Endpoint endpoint = manager.lookupEndpoint("Google");
System.out.println(endpoint);
Association association = manager.lookupAssociation(endpoint);
System.out.println(association);
String url = manager.getAuthenticationUrl(endpoint, association);
System.out.println("Copy the authentication URL in browser:\n" + url);
System.out
.println("After successfully sign on in browser, enter the URL of address bar in browser:");
String ret = readLine();
HttpServletRequest request = createRequest(ret);
Authentication authentication = manager.getAuthentication(request,
association.getRawMacKey(), endpoint.getAlias());
System.out.println(authentication);
}
static String readLine() throws IOException {
BufferedReader r = new BufferedReader(new InputStreamReader(System.in));
return r.readLine();
}
static HttpServletRequest createRequest(String url)
throws UnsupportedEncodingException {
int pos = url.indexOf('?');
if (pos == (-1))
throw new IllegalArgumentException("Bad url.");
String query = url.substring(pos + 1);
String[] params = query.split("[\\&]+");
final Map<String, String> map = new HashMap<String, String>();
for (String param : params) {
pos = param.indexOf('=');
if (pos == (-1))
throw new IllegalArgumentException("Bad url.");
String key = param.substring(0, pos);
String value = param.substring(pos + 1);
map.put(key, URLDecoder.decode(value, "UTF-8"));
}
return (HttpServletRequest) Proxy.newProxyInstance(
TestMain.class.getClassLoader(),
new Class[] { HttpServletRequest.class },
new InvocationHandler() {
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
if (method.getName().equals("getParameter"))
return map.get((String) args[0]);
throw new UnsupportedOperationException(method
.getName());
}
});
}
}
The pom.xml is as follow:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.expressme</groupId>
<artifactId>JOpenId</artifactId>
<version>1.08</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
</dependencies>
2. How to manage the Servlet
package com.sillycat.easyopenidgoogle.jopenid;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.expressme.openid.Association;
import org.expressme.openid.Authentication;
import org.expressme.openid.Endpoint;
import org.expressme.openid.OpenIdException;
import org.expressme.openid.OpenIdManager;
public class TestMainServlet extends HttpServlet {
private static final long serialVersionUID = 7513752565065680936L;
static final long ONE_HOUR = 3600000L;
static final long TWO_HOUR = ONE_HOUR * 2L;
static final String ATTR_MAC = "openid_mac";
static final String ATTR_ALIAS = "openid_alias";
OpenIdManager manager;
public void init(ServletConfig config) throws ServletException {
super.init(config);
manager = new OpenIdManager();
manager.setRealm("http://localhost"); // change to your domain
manager.setReturnTo("http://localhost/servlet-mapping");
// change to your SERVLET URL
}
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
String op = request.getParameter("op");
if (op == null) {
// check nonce:
checkNonce(request.getParameter("openid.response_nonce"));
// get authentication:
byte[] mac_key = (byte[]) request.getSession().getAttribute(
ATTR_MAC);
String alias = (String) request.getSession().getAttribute(
ATTR_ALIAS);
Authentication authentication = manager.getAuthentication(request,
mac_key, alias);
String identity = authentication.getIdentity();
String email = authentication.getEmail();
// TODO: create user if not exist in database:
showAuthentication(response.getWriter(), identity, email);
} else if ("Google".equals(op)) {
// redirect to Google sign on page:
Endpoint endpoint = manager.lookupEndpoint("Google");
Association association = manager.lookupAssociation(endpoint);
request.getSession().setAttribute(ATTR_MAC,
association.getRawMacKey());
request.getSession().setAttribute(ATTR_ALIAS, endpoint.getAlias());
String url = manager.getAuthenticationUrl(endpoint, association);
response.sendRedirect(url);
} else {
throw new ServletException("Bad parameter op=" + op);
}
}
void showAuthentication(PrintWriter pw, String identity, String email) {
pw.print("<html><body><h1>Identity</h1><p>");
pw.print(identity);
pw.print("</p><h1>Email</h1><p>");
pw.print(email == null ? "(null)" : email);
pw.print("</p></body></html>");
pw.flush();
}
void checkNonce(String nonce) {
// check response_nonce to prevent replay-attack:
if (nonce == null || nonce.length() < 20)
throw new OpenIdException("Verify failed.");
long nonceTime = getNonceTime(nonce);
long diff = System.currentTimeMillis() - nonceTime;
if (diff < 0)
diff = (-diff);
if (diff > ONE_HOUR)
throw new OpenIdException("Bad nonce time.");
if (isNonceExist(nonce))
throw new OpenIdException("Verify nonce failed.");
storeNonce(nonce, nonceTime + TWO_HOUR);
}
boolean isNonceExist(String nonce) {
// TODO: check if nonce is exist in database:
return false;
}
void storeNonce(String nonce, long expires) {
// TODO: store nonce in database:
}
long getNonceTime(String nonce) {
try {
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse(
nonce.substring(0, 19) + "+0000").getTime();
} catch (ParseException e) {
throw new OpenIdException("Bad nonce time.");
}
}
}
configuration for servlet
<p>Please sign on from:<p>
<p><a href="/openid?op=Google">Google Account</a></p>
<p><a href="/openid?op=Yahoo">Yahoo Account</a></p>
https://www.google.com/accounts/o8/id
3. Read some document about reply attack
http://en.wikipedia.org/wiki/Replay_attack
http://en.wikipedia.org/wiki/One-time_password
This is the reponse from google
http://localhost/openId?openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.mode=id_res&openid.op_endpoint=https%3A%2F%2Fwww.google.com%2Faccounts%2Fo8%2Fud&openid.response_nonce=2012-05-25T06%3A50%3A29ZHbJr3Mc8ITewRQ&openid.return_to=http%3A%2F%2Flocalhost%2FopenId&openid.assoc_handle=AMlYA9Uc9CjMYSuX-BRqwwYd0twJXgngATm6daS2QUopgMCMJIZnE4Ur&openid.signed=op_endpoint%2Cclaimed_id%2Cidentity%2Creturn_to%2Cresponse_nonce%2Cassoc_handle%2Cns.ext1%2Cext1.mode%2Cext1.type.firstname%2Cext1.value.firstname%2Cext1.type.email%2Cext1.value.email%2Cext1.type.language%2Cext1.value.language%2Cext1.type.lastname%2Cext1.value.lastname&openid.sig=E15gmFvLmu2CgRD%2Fx%2F134N%2FlL3w%3D&openid.identity=https%3A%2F%2Fwww.google.com%2Faccounts%2Fo8%2Fid%3Fid%3DAItOawl9BR07ZOQPW82cBnEo8hPjWZfX7d6xHwU&openid.claimed_id=https%3A%2F%2Fwww.google.com%2Faccounts%2Fo8%2Fid%3Fid%3DAItOawl9BR07ZOQPW82cBnEo8hPjWZfX7d6xHwU&openid.ns.ext1=http%3A%2F%2Fopenid.net%2Fsrv%2Fax%2F1.0&openid.ext1.mode=fetch_response&openid.ext1.type.firstname=http%3A%2F%2Faxschema.org%2FnamePerson%2Ffirst&openid.ext1.value.firstname=carl&openid.ext1.type.email=http%3A%2F%2Faxschema.org%2Fcontact%2Femail&openid.ext1.value.email=4myhappylife%40gmail.com&openid.ext1.type.language=http%3A%2F%2Faxschema.org%2Fpref%2Flanguage&openid.ext1.value.language=zh-CN&openid.ext1.type.lastname=http%3A%2F%2Faxschema.org%2FnamePerson%2Flast&openid.ext1.value.lastname=luo
We will check the nonce to prevent from replay attack.
references:
http://hi.baidu.com/luohuazju/blog/item/fd6e0a503f109f748435245b.html
http://hi.baidu.com/luohuazju/blog/item/ced42801a543bc15728da525.html
http://code.google.com/p/jopenid/
http://code.google.com/p/jopenid/wiki/Download
http://code.google.com/p/openid4java/wiki/SRegHowTo
http://code.google.com/p/jopenid/wiki/QuickStart
http://code.google.com/p/jopenid/wiki/DevGuide