1
15 package com.amazonaws.auth;
16
17 import static com.amazonaws.util.StringUtils.UTF8;
18
19 import com.amazonaws.AmazonClientException;
20 import com.amazonaws.ReadLimitInfo;
21 import com.amazonaws.SDKGlobalTime;
22 import com.amazonaws.SdkClientException;
23 import com.amazonaws.SignableRequest;
24 import com.amazonaws.internal.SdkDigestInputStream;
25 import com.amazonaws.internal.SdkThreadLocalsRegistry;
26 import com.amazonaws.util.Base64;
27 import com.amazonaws.util.BinaryUtils;
28 import com.amazonaws.util.SdkHttpUtils;
29 import com.amazonaws.util.StringUtils;
30 import java.io.ByteArrayInputStream;
31 import java.io.ByteArrayOutputStream;
32 import java.io.InputStream;
33 import java.net.URI;
34 import java.security.DigestInputStream;
35 import java.security.MessageDigest;
36 import java.security.NoSuchAlgorithmException;
37 import java.util.ArrayList;
38 import java.util.Collections;
39 import java.util.Date;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.SortedMap;
43 import java.util.TreeMap;
44 import javax.crypto.Mac;
45 import javax.crypto.spec.SecretKeySpec;
46
47
54 public abstract class AbstractAWSSigner implements Signer {
55
56 public static final String EMPTY_STRING_SHA256_HEX;
57 private static final ThreadLocal<MessageDigest> SHA256_MESSAGE_DIGEST;
58
59 static {
60 SHA256_MESSAGE_DIGEST = SdkThreadLocalsRegistry.register(
61 new ThreadLocal<MessageDigest>() {
62 @Override
63 protected MessageDigest initialValue() {
64 try {
65 return MessageDigest.getInstance("SHA-256");
66 } catch (NoSuchAlgorithmException e) {
67 throw new SdkClientException(
68 "Unable to get SHA256 Function"
69 + e.getMessage(), e);
70 }
71 }
72 });
73 EMPTY_STRING_SHA256_HEX = BinaryUtils.toHex(doHash(""));
74 }
75
76
80 protected String signAndBase64Encode(String data, String key,
81 SigningAlgorithm algorithm) throws SdkClientException {
82 return signAndBase64Encode(data.getBytes(UTF8), key, algorithm);
83 }
84
85
89 protected String signAndBase64Encode(byte[] data, String key,
90 SigningAlgorithm algorithm) throws SdkClientException {
91 try {
92 byte[] signature = sign(data, key.getBytes(UTF8), algorithm);
93 return Base64.encodeAsString(signature);
94 } catch (Exception e) {
95 throw new SdkClientException(
96 "Unable to calculate a request signature: "
97 + e.getMessage(), e);
98 }
99 }
100
101 public byte[] sign(String stringData, byte[] key,
102 SigningAlgorithm algorithm) throws SdkClientException {
103 try {
104 byte[] data = stringData.getBytes(UTF8);
105 return sign(data, key, algorithm);
106 } catch (Exception e) {
107 throw new SdkClientException(
108 "Unable to calculate a request signature: "
109 + e.getMessage(), e);
110 }
111 }
112
113 public byte[] signWithMac(String stringData, Mac mac) {
114 try {
115 return mac.doFinal(stringData.getBytes(UTF8));
116 } catch (Exception e) {
117 throw new SdkClientException(
118 "Unable to calculate a request signature: "
119 + e.getMessage(), e);
120 }
121 }
122
123 protected byte[] sign(byte[] data, byte[] key,
124 SigningAlgorithm algorithm) throws SdkClientException {
125 try {
126 Mac mac = algorithm.getMac();
127 mac.init(new SecretKeySpec(key, algorithm.toString()));
128 return mac.doFinal(data);
129 } catch (Exception e) {
130 throw new SdkClientException(
131 "Unable to calculate a request signature: "
132 + e.getMessage(), e);
133 }
134 }
135
136
148 public byte[] hash(String text) throws SdkClientException {
149 return AbstractAWSSigner.doHash(text);
150 }
151
152 private static byte[] doHash(String text) throws SdkClientException {
153 try {
154 MessageDigest md = getMessageDigestInstance();
155 md.update(text.getBytes(UTF8));
156 return md.digest();
157 } catch (Exception e) {
158 throw new SdkClientException(
159 "Unable to compute hash while signing request: "
160 + e.getMessage(), e);
161 }
162 }
163
164 protected byte[] hash(InputStream input) throws SdkClientException {
165 try {
166 MessageDigest md = getMessageDigestInstance();
167 @SuppressWarnings("resource")
168 DigestInputStream digestInputStream = new SdkDigestInputStream(
169 input, md);
170 byte[] buffer = new byte[1024];
171 while (digestInputStream.read(buffer) > -1)
172 ;
173 return digestInputStream.getMessageDigest().digest();
174 } catch (Exception e) {
175 throw new SdkClientException(
176 "Unable to compute hash while signing request: "
177 + e.getMessage(), e);
178 }
179 }
180
181
192 public byte[] hash(byte[] data) throws SdkClientException {
193 try {
194 MessageDigest md = getMessageDigestInstance();
195 md.update(data);
196 return md.digest();
197 } catch (Exception e) {
198 throw new SdkClientException(
199 "Unable to compute hash while signing request: "
200 + e.getMessage(), e);
201 }
202 }
203
216 protected String getCanonicalizedQueryString(Map<String, List<String>> parameters) {
217
218 final SortedMap<String, List<String>> sorted = new TreeMap<String, List<String>>();
219
220
224 for (Map.Entry<String, List<String>> entry : parameters.entrySet()) {
225 final String encodedParamName = SdkHttpUtils.urlEncode(
226 entry.getKey(), false);
227 final List<String> paramValues = entry.getValue();
228 final List<String> encodedValues = new ArrayList<String>(
229 paramValues.size());
230 for (String value : paramValues) {
231 encodedValues.add(SdkHttpUtils.urlEncode(value, false));
232 }
233 Collections.sort(encodedValues);
234 sorted.put(encodedParamName, encodedValues);
235
236 }
237
238 final StringBuilder result = new StringBuilder();
239 for(Map.Entry<String, List<String>> entry : sorted.entrySet()) {
240 for(String value : entry.getValue()) {
241 if (result.length() > 0) {
242 result.append("&");
243 }
244 result.append(entry.getKey())
245 .append("=")
246 .append(value);
247 }
248 }
249
250 return result.toString();
251 }
252
253 protected String getCanonicalizedQueryString(SignableRequest<?> request) {
254
259 if (SdkHttpUtils.usePayloadForQueryParameters(request))
260 return "";
261 return this.getCanonicalizedQueryString(request.getParameters());
262 }
263
264
271 protected byte[] getBinaryRequestPayload(SignableRequest<?> request) {
272 if (SdkHttpUtils.usePayloadForQueryParameters(request)) {
273 String encodedParameters = SdkHttpUtils.encodeParameters(request);
274 if (encodedParameters == null)
275 return new byte[0];
276
277 return encodedParameters.getBytes(UTF8);
278 }
279
280 return getBinaryRequestPayloadWithoutQueryParams(request);
281 }
282
283
290 protected String getRequestPayload(SignableRequest<?> request) {
291 return newString(getBinaryRequestPayload(request));
292 }
293
294
303 protected String getRequestPayloadWithoutQueryParams(SignableRequest<?> request) {
304 return newString(getBinaryRequestPayloadWithoutQueryParams(request));
305 }
306
307
316 protected byte[] getBinaryRequestPayloadWithoutQueryParams(SignableRequest<?> request) {
317 InputStream content = getBinaryRequestPayloadStreamWithoutQueryParams(request);
318
319 try {
320 ReadLimitInfo info = request.getReadLimitInfo();
321 content.mark(info == null ? -1 : info.getReadLimit());
322 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
323 byte[] buffer = new byte[1024 * 5];
324 while (true) {
325 int bytesRead = content.read(buffer);
326 if (bytesRead == -1) break;
327
328 byteArrayOutputStream.write(buffer, 0, bytesRead);
329 }
330
331 byteArrayOutputStream.close();
332 content.reset();
333
334 return byteArrayOutputStream.toByteArray();
335 } catch (Exception e) {
336 throw new SdkClientException("Unable to read request payload to sign request: " + e.getMessage(), e);
337 }
338 }
339
340 protected InputStream getBinaryRequestPayloadStream(SignableRequest<?> request) {
341 if (SdkHttpUtils.usePayloadForQueryParameters(request)) {
342 String encodedParameters = SdkHttpUtils.encodeParameters(request);
343 if (encodedParameters == null)
344 return new ByteArrayInputStream(new byte[0]);
345
346 return new ByteArrayInputStream(
347 encodedParameters.getBytes(UTF8));
348 }
349
350 return getBinaryRequestPayloadStreamWithoutQueryParams(request);
351 }
352
353 protected InputStream getBinaryRequestPayloadStreamWithoutQueryParams(SignableRequest<?> request) {
354 try {
355 InputStream is = request.getContentUnwrapped();
356 if (is == null)
357 return new ByteArrayInputStream(new byte[0]);
358 if (!is.markSupported())
359 throw new SdkClientException("Unable to read request payload to sign request.");
360 return is;
361 } catch (AmazonClientException e) {
362 throw e;
363 } catch (Exception e) {
364 throw new SdkClientException("Unable to read request payload to sign request: " + e.getMessage(), e);
365 }
366 }
367
368 protected String getCanonicalizedResourcePath(String resourcePath) {
369 return getCanonicalizedResourcePath(resourcePath, true);
370 }
371
372 protected String getCanonicalizedResourcePath(String resourcePath, boolean urlEncode) {
373 if (resourcePath == null || resourcePath.isEmpty()) {
374 return "/";
375 } else {
376 String value = urlEncode ? SdkHttpUtils.urlEncode(resourcePath, true) : resourcePath;
377 if (value.startsWith("/")) {
378 return value;
379 } else {
380 return "/".concat(value);
381 }
382 }
383 }
384
385 protected String getCanonicalizedEndpoint(URI endpoint) {
386 String endpointForStringToSign = StringUtils.lowerCase(endpoint.getHost());
387
394 if (SdkHttpUtils.isUsingNonDefaultPort(endpoint)) {
395 endpointForStringToSign += ":" + endpoint.getPort();
396 }
397
398 return endpointForStringToSign;
399 }
400
401
413 protected AWSCredentials sanitizeCredentials(AWSCredentials credentials) {
414 String accessKeyId = null;
415 String secretKey = null;
416 String token = null;
417 synchronized (credentials) {
418 accessKeyId = credentials.getAWSAccessKeyId();
419 secretKey = credentials.getAWSSecretKey();
420 if ( credentials instanceof AWSSessionCredentials ) {
421 token = ((AWSSessionCredentials) credentials).getSessionToken();
422 }
423 }
424 if (secretKey != null) secretKey = secretKey.trim();
425 if (accessKeyId != null) accessKeyId = accessKeyId.trim();
426 if (token != null) token = token.trim();
427
428 if (credentials instanceof AWSSessionCredentials) {
429 return new BasicSessionCredentials(accessKeyId, secretKey, token);
430 }
431
432 return new BasicAWSCredentials(accessKeyId, secretKey);
433 }
434
435
442 protected String newString(byte[] bytes) {
443 return new String(bytes, UTF8);
444 }
445
446
454 protected Date getSignatureDate(int offsetInSeconds) {
455 return new Date(System.currentTimeMillis() - offsetInSeconds*1000);
456 }
457
458
461 @Deprecated
462 protected int getTimeOffset(SignableRequest<?> request) {
463 final int globleOffset = SDKGlobalTime.getGlobalTimeOffset();
464 return globleOffset == 0 ? request.getTimeOffset() : globleOffset;
465 }
466
467
475 protected abstract void addSessionCredentials(SignableRequest<?> request,
476 AWSSessionCredentials credentials);
477
478
479
483 private static MessageDigest getMessageDigestInstance() {
484 MessageDigest messageDigest = SHA256_MESSAGE_DIGEST.get();
485 messageDigest.reset();
486 return messageDigest;
487 }
488
489 }
490