Skip to content

Package: OAuthSignatureCalculator

OAuthSignatureCalculator

nameinstructionbranchcomplexitylinemethod
OAuthSignatureCalculator(ConsumerKey, RequestToken)
M: 16 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 5 C: 0
0%
M: 1 C: 0
0%
baseUrl(Uri)
M: 57 C: 0
0%
M: 12 C: 0
0%
M: 7 C: 0
0%
M: 15 C: 0
0%
M: 1 C: 0
0%
calculateAndAddSignature(Request, RequestBuilderBase)
M: 31 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 6 C: 0
0%
M: 1 C: 0
0%
calculateSignature(String, Uri, long, String, List, List)
M: 21 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
constructAuthHeader(String, String, long)
M: 93 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 14 C: 0
0%
M: 1 C: 0
0%
encodedParams(long, String, List, List)
M: 116 C: 0
0%
M: 16 C: 0
0%
M: 9 C: 0
0%
M: 21 C: 0
0%
M: 1 C: 0
0%
generateNonce()
M: 10 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
generateTimestamp()
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
signatureBaseString(String, Uri, long, String, List, List)
M: 35 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 9 C: 0
0%
M: 1 C: 0
0%
static {...}
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%

Coverage

1: /*
2: * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
3: * Copyright 2010 Ning, Inc.
4: *
5: * Ning licenses this file to you under the Apache License, version 2.0
6: * (the "License"); you may not use this file except in compliance with the
7: * License. You may obtain a copy of the License at:
8: *
9: * http://www.apache.org/licenses/LICENSE-2.0
10: *
11: * Unless required by applicable law or agreed to in writing, software
12: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14: * License for the specific language governing permissions and limitations
15: * under the License.
16: */
17:
18: package com.ning.http.client.oauth;
19:
20: import static com.ning.http.util.MiscUtils.isNonEmpty;
21: import static java.nio.charset.StandardCharsets.UTF_8;
22:
23: import com.ning.http.client.Param;
24: import com.ning.http.client.Request;
25: import com.ning.http.client.RequestBuilderBase;
26: import com.ning.http.client.SignatureCalculator;
27: import com.ning.http.client.uri.Uri;
28: import com.ning.http.util.Base64;
29: import com.ning.http.util.StringUtils;
30: import com.ning.http.util.UTF8UrlEncoder;
31:
32: import java.nio.ByteBuffer;
33: import java.util.ArrayList;
34: import java.util.Arrays;
35: import java.util.List;
36: import java.util.concurrent.ThreadLocalRandom;
37:
38: /**
39: * Simple OAuth signature calculator that can used for constructing client signatures
40: * for accessing services that use OAuth for authorization.
41: * <p/>
42: * Supports most common signature inclusion and calculation methods: HMAC-SHA1 for
43: * calculation, and Header inclusion as inclusion method. Nonce generation uses
44: * simple random numbers with base64 encoding.
45: *
46: * @author tatu (tatu.saloranta@iki.fi)
47: */
48: public class OAuthSignatureCalculator implements SignatureCalculator {
49: public final static String HEADER_AUTHORIZATION = "Authorization";
50:
51: private static final String KEY_OAUTH_CONSUMER_KEY = "oauth_consumer_key";
52: private static final String KEY_OAUTH_NONCE = "oauth_nonce";
53: private static final String KEY_OAUTH_SIGNATURE = "oauth_signature";
54: private static final String KEY_OAUTH_SIGNATURE_METHOD = "oauth_signature_method";
55: private static final String KEY_OAUTH_TIMESTAMP = "oauth_timestamp";
56: private static final String KEY_OAUTH_TOKEN = "oauth_token";
57: private static final String KEY_OAUTH_VERSION = "oauth_version";
58:
59: private static final String OAUTH_VERSION_1_0 = "1.0";
60: private static final String OAUTH_SIGNATURE_METHOD = "HMAC-SHA1";
61:
62: protected static final ThreadLocal<byte[]> NONCE_BUFFER = new ThreadLocal<byte[]>() {
63: protected byte[] initialValue() {
64: return new byte[16];
65: }
66: };
67:
68: protected final ThreadSafeHMAC mac;
69:
70: protected final ConsumerKey consumerAuth;
71:
72: protected final RequestToken userAuth;
73:
74: /**
75: * @param consumerAuth Consumer key to use for signature calculation
76: * @param userAuth Request/access token to use for signature calculation
77: */
78: public OAuthSignatureCalculator(ConsumerKey consumerAuth, RequestToken userAuth) {
79: mac = new ThreadSafeHMAC(consumerAuth, userAuth);
80: this.consumerAuth = consumerAuth;
81: this.userAuth = userAuth;
82: }
83:
84: @Override
85: public void calculateAndAddSignature(Request request, RequestBuilderBase<?> requestBuilder) {
86: String nonce = generateNonce();
87: long timestamp = generateTimestamp();
88: String signature = calculateSignature(request.getMethod(), request.getUri(), timestamp, nonce, request.getFormParams(), request.getQueryParams());
89: String headerValue = constructAuthHeader(signature, nonce, timestamp);
90: requestBuilder.setHeader(HEADER_AUTHORIZATION, headerValue);
91: }
92:
93: private String baseUrl(Uri uri) {
94: /* 07-Oct-2010, tatu: URL may contain default port number; if so, need to extract
95: * from base URL.
96: */
97: String scheme = uri.getScheme();
98:
99: StringBuilder sb = StringUtils.stringBuilder();
100: sb.append(scheme).append("://").append(uri.getHost());
101:
102: int port = uri.getPort();
103:• if (scheme.equals("http")) {
104:• if (port == 80)
105: port = -1;
106:• } else if (scheme.equals("https")) {
107:• if (port == 443)
108: port = -1;
109: }
110:
111:• if (port != -1)
112: sb.append(':').append(port);
113:
114:• if (isNonEmpty(uri.getPath()))
115: sb.append(uri.getPath());
116:
117: return sb.toString();
118: }
119:
120: private String encodedParams(long oauthTimestamp, String nonce, List<Param> formParams, List<Param> queryParams) {
121: /**
122: * List of all query and form parameters added to this request; needed
123: * for calculating request signature
124: */
125: int allParametersSize = 5
126:• + (userAuth.getKey() != null ? 1 : 0)
127:• + (formParams != null ? formParams.size() : 0)
128: + (queryParams != null ? queryParams.size() : 0);
129: OAuthParameterSet allParameters = new OAuthParameterSet(allParametersSize);
130:
131: // start with standard OAuth parameters we need
132: allParameters.add(KEY_OAUTH_CONSUMER_KEY, UTF8UrlEncoder.encodeQueryElement(consumerAuth.getKey()));
133: allParameters.add(KEY_OAUTH_NONCE, UTF8UrlEncoder.encodeQueryElement(nonce));
134: allParameters.add(KEY_OAUTH_SIGNATURE_METHOD, OAUTH_SIGNATURE_METHOD);
135: allParameters.add(KEY_OAUTH_TIMESTAMP, String.valueOf(oauthTimestamp));
136:• if (userAuth.getKey() != null) {
137: allParameters.add(KEY_OAUTH_TOKEN, UTF8UrlEncoder.encodeQueryElement(userAuth.getKey()));
138: }
139: allParameters.add(KEY_OAUTH_VERSION, OAUTH_VERSION_1_0);
140:
141:• if (formParams != null) {
142:• for (Param param : formParams) {
143: // formParams are not already encoded
144: allParameters.add(UTF8UrlEncoder.encodeQueryElement(param.getName()), UTF8UrlEncoder.encodeQueryElement(param.getValue()));
145: }
146: }
147:• if (queryParams != null) {
148:• for (Param param : queryParams) {
149: // queryParams are already encoded
150: allParameters.add(param.getName(), param.getValue());
151: }
152: }
153: return allParameters.sortAndConcat();
154: }
155:
156: StringBuilder signatureBaseString(String method, Uri uri, long oauthTimestamp, String nonce,
157: List<Param> formParams, List<Param> queryParams) {
158:
159: // beware: must generate first as we're using pooled StringBuilder
160: String baseUrl = baseUrl(uri);
161: String encodedParams = encodedParams(oauthTimestamp, nonce, formParams, queryParams);
162:
163: StringBuilder sb = StringUtils.stringBuilder();
164: sb.append(method); // POST / GET etc (nothing to URL encode)
165: sb.append('&');
166: UTF8UrlEncoder.encodeAndAppendQueryElement(sb, baseUrl);
167:
168:
169: // and all that needs to be URL encoded (... again!)
170: sb.append('&');
171: UTF8UrlEncoder.encodeAndAppendQueryElement(sb, encodedParams);
172: return sb;
173: }
174:
175: /**
176: * Method for calculating OAuth signature using HMAC/SHA-1 method.
177: */
178: public String calculateSignature(String method, Uri uri, long oauthTimestamp, String nonce,
179: List<Param> formParams, List<Param> queryParams) {
180:
181: StringBuilder sb = signatureBaseString(method, uri, oauthTimestamp, nonce, formParams, queryParams);
182:
183: ByteBuffer rawBase = StringUtils.charSequence2ByteBuffer(sb, UTF_8);
184: byte[] rawSignature = mac.digest(rawBase);
185: // and finally, base64 encoded... phew!
186: return Base64.encode(rawSignature);
187: }
188:
189: /**
190: * Method used for constructing
191: */
192: private String constructAuthHeader(String signature, String nonce, long oauthTimestamp) {
193: StringBuilder sb = StringUtils.stringBuilder();
194: sb.append("OAuth ");
195: sb.append(KEY_OAUTH_CONSUMER_KEY).append("=\"").append(consumerAuth.getKey()).append("\", ");
196:• if (userAuth.getKey() != null) {
197: sb.append(KEY_OAUTH_TOKEN).append("=\"").append(userAuth.getKey()).append("\", ");
198: }
199: sb.append(KEY_OAUTH_SIGNATURE_METHOD).append("=\"").append(OAUTH_SIGNATURE_METHOD).append("\", ");
200:
201: // careful: base64 has chars that need URL encoding:
202: sb.append(KEY_OAUTH_SIGNATURE).append("=\"");
203: UTF8UrlEncoder.encodeAndAppendQueryElement(sb, signature).append("\", ");
204: sb.append(KEY_OAUTH_TIMESTAMP).append("=\"").append(oauthTimestamp).append("\", ");
205:
206: // also: nonce may contain things that need URL encoding (esp. when using base64):
207: sb.append(KEY_OAUTH_NONCE).append("=\"");
208: UTF8UrlEncoder.encodeAndAppendQueryElement(sb, nonce);
209: sb.append("\", ");
210:
211: sb.append(KEY_OAUTH_VERSION).append("=\"").append(OAUTH_VERSION_1_0).append("\"");
212: return sb.toString();
213: }
214:
215: protected long generateTimestamp() {
216: return System.currentTimeMillis() / 1000L;
217: }
218:
219: protected String generateNonce() {
220: byte[] nonceBuffer = NONCE_BUFFER.get();
221: ThreadLocalRandom.current().nextBytes(nonceBuffer);
222: // let's use base64 encoding over hex, slightly more compact than hex or decimals
223: return Base64.encode(nonceBuffer);
224: // return String.valueOf(Math.abs(random.nextLong()));
225: }
226:
227: /**
228: * Container for parameters used for calculating OAuth signature.
229: * About the only confusing aspect is that of whether entries are to be sorted
230: * before encoded or vice versa: if my reading is correct, encoding is to occur
231: * first, then sorting; although this should rarely matter (since sorting is primary
232: * by key, which usually has nothing to encode)... of course, rarely means that
233: * when it would occur it'd be harder to track down.
234: */
235: final static class OAuthParameterSet {
236: private final ArrayList<Parameter> allParameters;
237:
238: public OAuthParameterSet(int size) {
239: allParameters = new ArrayList<>(size);
240: }
241:
242: public OAuthParameterSet add(String key, String value) {
243: allParameters.add(new Parameter(key, value));
244: return this;
245: }
246:
247: public String sortAndConcat() {
248: // then sort them (AFTER encoding, important)
249: Parameter[] params = allParameters.toArray(new Parameter[allParameters.size()]);
250: Arrays.sort(params);
251:
252: // and build parameter section using pre-encoded pieces:
253: StringBuilder encodedParams = new StringBuilder(100);
254: for (Parameter param : params) {
255: if (encodedParams.length() > 0) {
256: encodedParams.append('&');
257: }
258: encodedParams.append(param.key()).append('=').append(param.value());
259: }
260: return encodedParams.toString();
261: }
262: }
263:
264: /**
265: * Helper class for sorting query and form parameters that we need
266: */
267: final static class Parameter implements Comparable<Parameter> {
268: private final String key, value;
269:
270: public Parameter(String key, String value) {
271: this.key = key;
272: this.value = value;
273: }
274:
275: public String key() {
276: return key;
277: }
278:
279: public String value() {
280: return value;
281: }
282:
283: @Override
284: public int compareTo(Parameter other) {
285: int diff = key.compareTo(other.key);
286: if (diff == 0) {
287: diff = value.compareTo(other.value);
288: }
289: return diff;
290: }
291:
292: @Override
293: public String toString() {
294: return key + "=" + value;
295: }
296:
297: @Override
298: public boolean equals(Object o) {
299: if (this == o) return true;
300: if (o == null || getClass() != o.getClass()) return false;
301:
302: Parameter parameter = (Parameter) o;
303:
304: if (!key.equals(parameter.key)) return false;
305: if (!value.equals(parameter.value)) return false;
306:
307: return true;
308: }
309:
310: @Override
311: public int hashCode() {
312: int result = key.hashCode();
313: result = 31 * result + value.hashCode();
314: return result;
315: }
316: }
317: }