View Javadoc
1   package org.oxerr.peatio.rest.service;
2   
3   import java.lang.annotation.Annotation;
4   import java.lang.reflect.Field;
5   import java.nio.charset.StandardCharsets;
6   import java.util.Map;
7   import java.util.SortedMap;
8   import java.util.TreeMap;
9   
10  import javax.crypto.Mac;
11  import javax.ws.rs.FormParam;
12  import javax.ws.rs.QueryParam;
13  import javax.ws.rs.core.UriBuilder;
14  
15  import org.slf4j.Logger;
16  import org.slf4j.LoggerFactory;
17  
18  import si.mazi.rescu.Params;
19  import si.mazi.rescu.ParamsDigest;
20  import si.mazi.rescu.RestInvocation;
21  
22  import com.xeiam.xchange.service.BaseParamsDigest;
23  import com.xeiam.xchange.utils.DigestUtils;
24  
25  /**
26   * {@link ParamsDigest} implementation for Peatio.
27   */
28  public class PeatioDigest extends BaseParamsDigest {
29  
30  	private final Logger log = LoggerFactory.getLogger(PeatioDigest.class);
31  	private final Field invocationUrlField;
32  
33  	public PeatioDigest(String secretKey)
34  			throws IllegalArgumentException {
35  		super(secretKey, HMAC_SHA_256);
36  
37  		try {
38  			invocationUrlField = RestInvocation.class.getDeclaredField("invocationUrl");
39  			invocationUrlField.setAccessible(true);
40  		} catch (NoSuchFieldException | SecurityException e) {
41  			throw new RuntimeException(e);
42  		}
43  	}
44  
45  	/**
46  	 * {@inheritDoc}
47  	 */
48  	@Override
49  	public String digestParams(RestInvocation restInvocation) {
50  		final String verb = restInvocation.getHttpMethod();
51  		final String uri = restInvocation.getPath();
52  		final String query = getSortedParams(restInvocation);
53  
54  		final String payload = String.join("|", verb, uri, query);
55  		log.debug("payload: {}", payload);
56  
57  		String signature = sign(payload);
58  
59  		// Seems rescu does not support ParamsDigest in QueryParam.
60  		// hack to replace the signature in the invocation URL.
61  		String invocationUrl = restInvocation.getInvocationUrl();
62  		log.debug("old invocationUrl: {}", invocationUrl);
63  		String newInvocationUrl = UriBuilder.fromUri(invocationUrl).replaceQueryParam("signature", signature).build().toString();
64  		try {
65  			invocationUrlField.set(restInvocation, newInvocationUrl);
66  		} catch (IllegalArgumentException | IllegalAccessException e) {
67  			throw new RuntimeException(e);
68  		}
69  		log.debug("new invocationUrl: {}", restInvocation.getInvocationUrl());
70  
71  		return signature;
72  	}
73  
74  	public String sign(String payload) {
75  		Mac mac = getMac();
76  		byte[] hash = mac.doFinal(payload.getBytes(StandardCharsets.UTF_8));
77  		String signature = DigestUtils.bytesToHex(hash).toLowerCase();
78  		return signature;
79  	}
80  
81  	private String getSortedParams(RestInvocation restInvocation) {
82  		final Map<Class<? extends Annotation>, Params> paramsMap = restInvocation.getParamsMap();
83  
84  		final Params queryParams = paramsMap.get(QueryParam.class);
85  		final Params formParams = paramsMap.get(FormParam.class);
86  
87  		final SortedMap<String, String> sortedQueryParams = new TreeMap<>();
88  		sortedQueryParams.putAll(queryParams.asHttpHeaders());
89  		sortedQueryParams.putAll(formParams.asHttpHeaders());
90  
91  		final Params sortedParams = Params.of();
92  		for (Map.Entry<String, String> param : sortedQueryParams.entrySet()) {
93  			if (!param.getKey().equals("signature")) {
94  				sortedParams.add(param.getKey(), param.getValue());
95  			}
96  		}
97  
98  		return sortedParams.asQueryString();
99  	}
100 
101 }