FIDO UAF服务器
fidouaf github地址: https://github.com/eBay/UAF/tree/master/fidouaf
FIDO UAF演示服务器通过使用github上面分析的UAF Core的代码实现了可以实际运行和使用的服务器。它是用Java编写的,我使用SUN的Jersey服务器框架创建了一个服务器。依赖关系如下:除了使用GSON之外,演示服务器使用Jersey单独提供的JSON库来执行某些任务。此外,可以确认直接引用和使用UAF Core。
\fidouaf\pom.xml
<dependencies>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-server</artifactId>
<version>1.8</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>1.8</version>
</dependency>
<dependency>
<groupId>org.ebayopensource</groupId>
<artifactId>fido-uaf-core</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.3.1</version>
</dependency>
</dependencies>
包的结构如下:驱动部分位于org.ebayopensource.fidouaf.res中的Hello和FidoUfResource中,Hello是一个简单的测试服务器,FidoUfResource提供实际的RP服务器和FIDO服务器页面。
以下是每个子包的说明。
- “facets”包中包含为存储各种FacetID而创建的实体类。
- “RPserver.msg”包中包含用于编写和接收各种消息的容器(Reg,Dereg,Auth),TokenType和Token是定义的.TokenType是枚举数据类型,如下所示,它包含登录类型。
TokenType.java:
package org.ebayopensource.fidouaf.RPserver.msg.enums;
public enum TokenType
{
HTTP_COOKIE,
OAUTH,
OAUTH2,
SAML1_1,
SAML2,
JWT,
OPENID_CONNECT
}
- “stats” 包中有一个名为“ Dash ”的类,它存储多达100个已由命令(Auth,Reg,Dereg)处理的哈希映射,它是一个日志存储类。
- “res”包可以被认为是真实的表面服务器实现,在“res.util” 包中,有一个Notary和Storage接口的实现类,并且存在一个处理响应消息或处理Dereg请求的类,当您包含AppID和AAID时,FetchRequest类充当中介,以帮助您执行特定请求。
FidoUafResource.java
@Path("/v1")
public class FidoUafResource {
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
@GET
@Path("/stats")
@Produces(MediaType.APPLICATION_JSON)
public String getStats() {
return gson.toJson(Dash.getInstance().stats);
}
@GET
@Path("/history")
@Produces(MediaType.APPLICATION_JSON)
public List<Object> getHistory() {
return Dash.getInstance().history;
}
@GET
@Path("/registrations")
@Produces(MediaType.APPLICATION_JSON)
public Map<String, RegistrationRecord> getDbDump() {
return StorageImpl.getInstance().dbDump();
}
/**
* @param username
* @return RegistrationRequest[]
* @desc 注册请求携带用户名
* @method get
*/
@GET
@Path("/public/regRequest/{username}")
@Produces(MediaType.APPLICATION_JSON)
public RegistrationRequest[] getRegisReqPublic(@PathParam("username") String username) {
RegistrationRequest[] regReq = new RegistrationRequest[1];
regReq[0] = new FetchRequest(getAppId(), getAllowedAaids()).getRegistrationRequest(username);
Dash.getInstance().stats.put(Dash.LAST_REG_REQ, regReq);
Dash.getInstance().history.add(regReq);
return regReq;
}
/**
* @param username,appId
* @return String
* @desc 注册请求携带用户名和appId
* @method get
*/
@GET
@Path("/public/regRequest/{username}/{appId}")
@Produces(MediaType.APPLICATION_JSON)
public String getRegReqForAppId(
@PathParam("username") String username,
@PathParam("appId") String appId) {
RegistrationRequest[] regReq = getRegisReqPublic(username);
setAppId(appId, regReq[0].header);
return gson.toJson(regReq);
}
/**
* @param username
* @return RegistrationRequest[]
* @desc 注册请求
* @method get
*/
@GET
@Path("/public/regRequest")
@Produces(MediaType.APPLICATION_JSON)
public RegistrationRequest[] postRegisReqPublic(String username) {
RegistrationRequest[] regReq = new RegistrationRequest[1];
regReq[0] = new FetchRequest(getAppId(), getAllowedAaids()).getRegistrationRequest(username);
Dash.getInstance().stats.put(Dash.LAST_REG_REQ, regReq);
Dash.getInstance().history.add(regReq);
return regReq;
}
private String[] getAllowedAaids() {
String[] ret = {"EBA0#0001", "0015#0001", "0012#0002", "0010#0001",
"4e4e#0001", "5143#0001", "0011#0701", "0013#0001",
"0014#0000", "0014#0001", "53EC#C002", "DAB8#8001",
"DAB8#0011", "DAB8#8011", "5143#0111", "5143#0120",
"4746#F816", "53EC#3801"};
return ret;
}
@GET
@Path("/public/uaf/facets")
@Produces("application/fido.trusted-apps+json")
public Facets facets() {
String timestamp = new Date().toString();
Dash.getInstance().stats.put(Dash.LAST_REG_REQ, timestamp);
String[] trustedIds = {"https://www.head2toes.org",
"android:apk-key-hash:Df+2X53Z0UscvUu6obxC3rIfFyk",
"android:apk-key-hash:bE0f1WtRJrZv/C0y9CM73bAUqiI",
"https://openidconnect.ebay.com"};
Facets facets = new Facets();
facets.trustedFacets = new TrustedFacets[1];
TrustedFacets trusted = new TrustedFacets();
trusted.version = new Version(1, 0);
trusted.ids = trustedIds;
facets.trustedFacets[0] = trusted;
return facets;
}
/**
* 获取appId
*/
private String getAppId() {
return "https://www.head2toes.org/fidouaf/v1/public/uaf/facets";
}
/**
* @param payload
* @return RegistrationRecord[]
* @desc 注册响应
* @method post
*/
@POST
@Path("/public/regResponse")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public RegistrationRecord[] processRegResponse(String payload) {
RegistrationRecord[] result = null;
Gson gson = new Gson();
RegistrationResponse[] fromJson = gson.fromJson(payload, RegistrationResponse[].class);
Dash.getInstance().stats.put(Dash.LAST_REG_RES, fromJson);
Dash.getInstance().history.add(fromJson);
RegistrationResponse registrationResponse = fromJson[0];
result = new ProcessResponse().processRegResponse(registrationResponse);
if (result[0].status.equals("SUCCESS")) {
try {
StorageImpl.getInstance().store(result);
} catch (DuplicateKeyException e) {
result = new RegistrationRecord[1];
result[0] = new RegistrationRecord();
result[0].status = "Error: Duplicate Key";
} catch (SystemErrorException e1) {
result = new RegistrationRecord[1];
result[0] = new RegistrationRecord();
result[0].status = "Error: Data couldn't be stored in DB";
}
}
return result;
}
/**
* @param payload
* @return String
* @desc 注销请求
* @method post
*/
@POST
@Path("/public/deregRequest")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public String deregRequestPublic(String payload) {
return new DeregRequestProcessor().process(payload);
}
/**
* @param
* @return String
* @desc 授权请求
* @method get
*/
@GET
@Path("/public/authRequest")
@Produces(MediaType.APPLICATION_JSON)
public String getAuthReq() {
return gson.toJson(getAuthReqObj());
}
/**
* @param appId
* @return String
* @desc 授权请求携带appId
* @method get
*/
@GET
@Path("/public/authRequest/{appId}")
@Produces(MediaType.APPLICATION_JSON)
public String getAuthForAppIdReq(@PathParam("appId") String appId) {
AuthenticationRequest[] authReqObj = getAuthReqObj();
setAppId(appId, authReqObj[0].header);
return gson.toJson(authReqObj);
}
private void setAppId(String appId, OperationHeader header) {
if (appId == null || appId.isEmpty()) {
return;
}
String decodedAppId = new String(Base64.decodeBase64(appId));
Facets facets = facets();
if (facets == null || facets.trustedFacets == null || facets.trustedFacets.length == 0
|| facets.trustedFacets[0] == null || facets.trustedFacets[0].ids == null) {
return;
}
String[] ids = facets.trustedFacets[0].ids;
for (int i = 0; i < ids.length; i++) {
if (decodedAppId.equals(ids[i])) {
header.appID = decodedAppId;
break;
}
}
}
/**
* @param appId,trxContent
* @return String
* @desc 授权请求携带appId和trxContent
* @method get
*/
@GET
@Path("/public/authRequest/{appId}/{trxContent}")
@Produces(MediaType.APPLICATION_JSON)
public String getAuthTrxReq(@PathParam("appId") String appId, @PathParam("trxContent") String trxContent) {
AuthenticationRequest[] authReqObj = getAuthReqObj();
setAppId(appId, authReqObj[0].header);
setTransaction(trxContent, authReqObj);
return gson.toJson(authReqObj);
}
private void setTransaction(String trxContent, AuthenticationRequest[] authReqObj) {
authReqObj[0].transaction = new Transaction[1];
Transaction t = new Transaction();
t.content = trxContent;
t.contentType = MediaType.TEXT_PLAIN;
authReqObj[0].transaction[0] = t;
}
public AuthenticationRequest[] getAuthReqObj() {
AuthenticationRequest[] ret = new AuthenticationRequest[1];
ret[0] = new FetchRequest(getAppId(), getAllowedAaids()).getAuthenticationRequest();
Dash.getInstance().stats.put(Dash.LAST_AUTH_REQ, ret);
Dash.getInstance().history.add(ret);
return ret;
}
/**
* @param payload
* @return AuthenticatorRecord[]
* @desc 授权响应
* @method post
*/
@POST
@Path("/public/authResponse")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public AuthenticatorRecord[] processAuthResponse(String payload) {
Dash.getInstance().stats.put(Dash.LAST_AUTH_RES, payload);
Gson gson = new Gson();
AuthenticationResponse[] authResp = gson.fromJson(payload, AuthenticationResponse[].class);
Dash.getInstance().stats.put(Dash.LAST_AUTH_RES, authResp);
Dash.getInstance().history.add(authResp);
AuthenticatorRecord[] result = new ProcessResponse().processAuthResponse(authResp[0]);
return result;
}
/**
* @param payload
* @return ReturnUAFRegistrationRequest
* @desc uaf注册请求
* @method post
*/
@POST
@Path("/public/uafRegRequest")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public ReturnUAFRegistrationRequest GetUAFRegistrationRequest(String payload) {
RegistrationRequest[] result = getRegisReqPublic("iafuser01");
ReturnUAFRegistrationRequest uafReq = null;
if (result != null) {
uafReq = new ReturnUAFRegistrationRequest();
uafReq.statusCode = 1200;
uafReq.uafRequest = result;
uafReq.op = Operation.Reg;
uafReq.lifetimeMillis = 5 * 60 * 1000;
}
return uafReq;
}
/**
* @param payload
* @return ReturnUAFAuthenticationRequest
* @desc uaf授权请求
* @method post
*/
@POST
@Path("/public/uafAuthRequest")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public ReturnUAFAuthenticationRequest GetUAFAuthenticationRequest(String payload) {
AuthenticationRequest[] result = getAuthReqObj();
ReturnUAFAuthenticationRequest uafReq = null;
if (result != null) {
uafReq = new ReturnUAFAuthenticationRequest();
uafReq.statusCode = 1200;
uafReq.uafRequest = result;
uafReq.op = Operation.Auth;
uafReq.lifetimeMillis = 5 * 60 * 1000;
}
return uafReq;
}
/**
* @param payload
* @return ReturnUAFDeregistrationRequest
* @desc uaf注销请求
* @method post
*/
@POST
@Path("/public/uafDeregRequest")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public ReturnUAFDeregistrationRequest GetUAFDeregistrationRequest(String payload) {
String result = deregRequestPublic(payload);
ReturnUAFDeregistrationRequest uafReq = new ReturnUAFDeregistrationRequest();
if (result.equalsIgnoreCase("Success")) {
uafReq.statusCode = 1200;
} else if (result.equalsIgnoreCase("Failure: Problem in deleting record from local DB")) {
uafReq.statusCode = 1404;
} else if (result.equalsIgnoreCase("Failure: problem processing deregistration request")) {
uafReq.statusCode = 1491;
} else {
uafReq.statusCode = 1500;
}
uafReq.uafRequest = null;
uafReq.op = Operation.Dereg;
uafReq.lifetimeMillis = 0;
return uafReq;
}
/**
* @param payload
* @return ServerResponse
* @desc uaf授权响应
* @method post
*/
@POST
@Path("/public/uafAuthResponse")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public ServerResponse UAFAuthResponse(String payload) {
String findOp = payload;
findOp = findOp.substring(findOp.indexOf("op") + 6,
findOp.indexOf(",", findOp.indexOf("op")) - 1);
System.out.println("findOp=" + findOp);
AuthenticatorRecord[] result = processAuthResponse(payload);
ServerResponse servResp = new ServerResponse();
if (result[0].status.equals("SUCCESS")) {
servResp.statusCode = 1200;
servResp.Description = "OK. Operation completed";
} else if (result[0].status.equals("FAILED_SIGNATURE_NOT_VALID")
|| result[0].status.equals("FAILED_SIGNATURE_VERIFICATION")
|| result[0].status.equals("FAILED_ASSERTION_VERIFICATION")) {
servResp.statusCode = 1496;
servResp.Description = result[0].status;
} else if (result[0].status.equals("INVALID_SERVER_DATA_EXPIRED")
|| result[0].status.equals("INVALID_SERVER_DATA_SIGNATURE_NO_MATCH")
|| result[0].status.equals("INVALID_SERVER_DATA_CHECK_FAILED")) {
servResp.statusCode = 1491;
servResp.Description = result[0].status;
} else {
servResp.statusCode = 1500;
servResp.Description = result[0].status;
}
return servResp;
}
/**
* @param payload
* @return ServerResponse
* @desc uaf注册响应
* @method post
*/
@POST
@Path("/public/uafRegResponse")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public ServerResponse UAFRegResponse(String payload) {
String findOp = payload;
findOp = findOp.substring(findOp.indexOf("op") + 6,
findOp.indexOf(",", findOp.indexOf("op")) - 1);
System.out.println("findOp=" + findOp);
RegistrationRecord[] result = processRegResponse(payload);
ServerResponse servResp = new ServerResponse();
if (result[0].status.equals("SUCCESS")) {
servResp.statusCode = 1200;
servResp.Description = "OK. Operation completed";
} else if (result[0].status.equals("ASSERTIONS_CHECK_FAILED")) {
servResp.statusCode = 1496;
servResp.Description = result[0].status;
} else if (result[0].status.equals("INVALID_SERVER_DATA_EXPIRED")
|| result[0].status.equals("INVALID_SERVER_DATA_SIGNATURE_NO_MATCH")
|| result[0].status.equals("INVALID_SERVER_DATA_CHECK_FAILED")) {
servResp.statusCode = 1491;
servResp.Description = result[0].status;
} else {
servResp.statusCode = 1500;
servResp.Description = result[0].status;
}
return servResp;
}
/**
* @param payload(有效载荷)
* @return String
* @desc uaf请求
* @method post
*/
@POST
@Path("/public/uafRequest")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public String GetUAFRequest(String payload) {
String uafReq = null;
Gson gson = new Gson();
GetUAFRequest req = gson.fromJson(payload, GetUAFRequest.class);
if (req.op.name().equals("Reg")) {
RegistrationRequest[] result = getRegisReqPublic("iafuser01");
ReturnUAFRegistrationRequest uafRegReq = null;
if (result != null) {
uafRegReq = new ReturnUAFRegistrationRequest();
uafRegReq.statusCode = 1200;
uafRegReq.uafRequest = result;
uafRegReq.op = Operation.Reg;
uafRegReq.lifetimeMillis = 5 * 60 * 1000;
}
uafReq = gson.toJson(uafRegReq);
} else if (req.op.name().equals("Auth")) {
AuthenticationRequest[] result = getAuthReqObj();
ReturnUAFAuthenticationRequest uafAuthReq = null;
if (result != null) {
uafAuthReq = new ReturnUAFAuthenticationRequest();
uafAuthReq.statusCode = 1200;
uafAuthReq.uafRequest = result;
uafAuthReq.op = Operation.Auth;
uafAuthReq.lifetimeMillis = 5 * 60 * 1000;
}
uafReq = gson.toJson(uafAuthReq);
} else if (req.op.name().equals("Dereg")) {
String result = deregRequestPublic(payload);
ReturnUAFDeregistrationRequest uafDeregReq = new ReturnUAFDeregistrationRequest();
if (result.equalsIgnoreCase("Success")) {
uafDeregReq.statusCode = 1200;
} else if (result.equalsIgnoreCase("Failure: Problem in deleting record from local DB")) {
uafDeregReq.statusCode = 1404;
} else if (result.equalsIgnoreCase("Failure: problem processing deregistration request")) {
uafDeregReq.statusCode = 1491;
} else {
uafDeregReq.statusCode = 1500;
}
uafDeregReq.uafRequest = null;
uafDeregReq.op = Operation.Dereg;
uafDeregReq.lifetimeMillis = 0;
uafReq = gson.toJson(uafDeregReq);
}
return uafReq;
}
/**
* @param payload
* @return ServerResponse
* @desc uaf响应
* @method post
*/
@POST
@Path("/public/uafResponse")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public ServerResponse UAFResponse(String payload) {
String findOp = payload;
findOp = findOp.substring(findOp.indexOf("op") + 6,
findOp.indexOf(",", findOp.indexOf("op")) - 1);
System.out.println("findOp=" + findOp);
ServerResponse servResp = new ServerResponse();
if (findOp.equals("Reg")) {
RegistrationRecord[] result = processRegResponse(payload);
if (result[0].status.equals("SUCCESS")) {
servResp.statusCode = 1200;
servResp.Description = "OK. Operation completed";
} else if (result[0].status.equals("ASSERTIONS_CHECK_FAILED")) {
servResp.statusCode = 1496;
servResp.Description = result[0].status;
} else if (result[0].status.equals("INVALID_SERVER_DATA_EXPIRED")
|| result[0].status.equals("INVALID_SERVER_DATA_SIGNATURE_NO_MATCH")
|| result[0].status.equals("INVALID_SERVER_DATA_CHECK_FAILED")) {
servResp.statusCode = 1491;
servResp.Description = result[0].status;
} else {
servResp.statusCode = 1500;
servResp.Description = result[0].status;
}
} else if (findOp.equals("Auth")) {
AuthenticatorRecord[] result = processAuthResponse(payload);
if (result[0].status.equals("SUCCESS")) {
servResp.statusCode = 1200;
servResp.Description = "OK. Operation completed";
} else if (result[0].status.equals("FAILED_SIGNATURE_NOT_VALID")
|| result[0].status.equals("FAILED_SIGNATURE_VERIFICATION")
|| result[0].status.equals("FAILED_ASSERTION_VERIFICATION")) {
servResp.statusCode = 1496;
servResp.Description = result[0].status;
} else if (result[0].status.equals("INVALID_SERVER_DATA_EXPIRED")
|| result[0].status.equals("INVALID_SERVER_DATA_SIGNATURE_NO_MATCH")
|| result[0].status.equals("INVALID_SERVER_DATA_CHECK_FAILED")) {
servResp.statusCode = 1491;
servResp.Description = result[0].status;
} else {
servResp.statusCode = 1500;
servResp.Description = result[0].status;
}
}
return servResp;
}
}
FidoUfResource是一个实际连接FIDO演示服务器表面(例如URL可访问)和内部(UAF基于核心的处理程序)的类,以下是FIDO演示服务器的类图,您可以在其中看到FidoUfResource创建并使用其他类。
结构本身很简单,由于在Jersey中提供网页的方式类似于JSP,因此第一个看到的人很快就能理解它。
FidoUafResource.getDbDump()
@GET
@Path("/registrations")
@Produces(MediaType.APPLICATION_JSON)
public Map<String, RegistrationRecord> getDbDump() {
return StorageImpl.getInstance().dbDump();
}
以上是注册用户的接口
@GET
@Path("/public/regRequest/{username}")
@Produces(MediaType.APPLICATION_JSON)
public RegistrationRequest[] getRegisReqPublic(
@PathParam("username") String username) {
RegistrationRequest[] regReq = new RegistrationRequest[1];
regReq[0] = new FetchRequest(getAppId(),getAllowedAaids()).getRegistrationRequest(username);
Dash.getInstance().stats.put(Dash.LAST_REG_REQ, regReq);
Dash.getInstance().history.add(regReq);
return regReq;
}
以上是使用用户名注册的接口
@POST
@Path("/public/deregRequest")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public String deregRequestPublic(String payload) {
return new DeregRequestProcessor().process(payload);
}
以上是接收用户注销请求的接口
总而言之,FIDO演示服务器以与上述相同的方式提供各种页面和处理请求。下面是已定义函数和变量的列表:
此演示服务器最重要的功能是RP服务器和FIDO服务器是混合的,这是因为它是出于测试目的而制作的,但一般来说,将RP服务器和FIDO服务器分开是一个规则。此外,由于未使用数据库引擎,因此所有appId和aaid都在类中进行了硬编码。可以理解为,仅在服务器运行时才在哈希映射中管理用户信息。
如果要基于此库构建自己的服务器,则需要单独与DB一起使用。