1
15 package com.amazonaws.services.s3.internal;
16
17 import com.amazonaws.SdkClientException;
18 import com.amazonaws.ReadLimitInfo;
19 import com.amazonaws.Request;
20 import com.amazonaws.ResetException;
21 import com.amazonaws.SignableRequest;
22 import com.amazonaws.auth.AWS4Signer;
23 import com.amazonaws.auth.AwsChunkedEncodingInputStream;
24 import com.amazonaws.auth.internal.AWS4SignerRequestParams;
25 import com.amazonaws.services.s3.Headers;
26 import com.amazonaws.services.s3.model.PutObjectRequest;
27 import com.amazonaws.services.s3.model.UploadPartRequest;
28 import com.amazonaws.services.s3.request.S3HandlerContextKeys;
29 import com.amazonaws.util.BinaryUtils;
30
31 import java.io.IOException;
32 import java.io.InputStream;
33
34 import static com.amazonaws.auth.internal.SignerConstants.X_AMZ_CONTENT_SHA256;
35
36
39 public class AWSS3V4Signer extends AWS4Signer {
40 private static final String CONTENT_SHA_256 = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD";
41
42
43 private static final String UNSIGNED_PAYLOAD = "UNSIGNED-PAYLOAD";
44
45
49 public AWSS3V4Signer() {
50 super(false);
51 }
52
53
56 @Override
57 protected void processRequestPayload(SignableRequest<?> request, byte[] signature,
58 byte[] signingKey, AWS4SignerRequestParams signerRequestParams) {
59 if (useChunkEncoding(request)) {
60 AwsChunkedEncodingInputStream chunkEncodededStream = new AwsChunkedEncodingInputStream(
61 request.getContent(), signingKey,
62 signerRequestParams.getFormattedSigningDateTime(),
63 signerRequestParams.getScope(),
64 BinaryUtils.toHex(signature), this);
65 request.setContent(chunkEncodededStream);
66 }
67 }
68
69 @Override
70 protected String calculateContentHashPresign(SignableRequest<?> request){
71 return "UNSIGNED-PAYLOAD";
72 }
73
74
79 @Override
80 protected String calculateContentHash(SignableRequest<?> request) {
81
82
83
84 request.addHeader(X_AMZ_CONTENT_SHA256, "required");
85
86 if (isPayloadSigningEnabled(request)) {
87 if (useChunkEncoding(request)) {
88 final String contentLength = request.getHeaders().get(Headers.CONTENT_LENGTH);
89 final long originalContentLength;
90 if (contentLength != null) {
91 originalContentLength = Long.parseLong(contentLength);
92 } else {
93
102 try {
103 originalContentLength = getContentLength(request);
104 } catch (IOException e) {
105 throw new SdkClientException(
106 "Cannot get the content-length of the request content.", e);
107 }
108 }
109 request.addHeader("x-amz-decoded-content-length",
110 Long.toString(originalContentLength));
111
112
113 request.addHeader(Headers.CONTENT_LENGTH, Long.toString(
114 AwsChunkedEncodingInputStream
115 .calculateStreamContentLength(originalContentLength)));
116 return CONTENT_SHA_256;
117 } else {
118 return super.calculateContentHash(request);
119 }
120 }
121
122 return UNSIGNED_PAYLOAD;
123 }
124
125
128 private boolean useChunkEncoding(SignableRequest<?> request) {
129
130
131 if (!isPayloadSigningEnabled(request) || isChunkedEncodingDisabled(request)) {
132 return false;
133 }
134 if (request.getOriginalRequestObject() instanceof PutObjectRequest
135 || request.getOriginalRequestObject() instanceof UploadPartRequest) {
136 return true;
137 }
138 return false;
139 }
140
141
145 private boolean isChunkedEncodingDisabled(SignableRequest<?> signableRequest) {
146 if (signableRequest instanceof Request) {
147 Request<?> request = (Request<?>) signableRequest;
148 Boolean isChunkedEncodingDisabled = request
149 .getHandlerContext(S3HandlerContextKeys.IS_CHUNKED_ENCODING_DISABLED);
150 return isChunkedEncodingDisabled != null && isChunkedEncodingDisabled;
151 }
152 return false;
153 }
154
155
158 private boolean isPayloadSigningEnabled(SignableRequest<?> signableRequest) {
159
162 if (!signableRequest.getEndpoint().getScheme().equals("https")) {
163 return true;
164 }
165
166 if (signableRequest instanceof Request) {
167 Request<?> request = (Request<?>) signableRequest;
168 Boolean isPayloadSigningEnabled = request
169 .getHandlerContext(S3HandlerContextKeys.IS_PAYLOAD_SIGNING_ENABLED);
170 return isPayloadSigningEnabled != null && isPayloadSigningEnabled;
171 }
172 return false;
173 }
174
175
180 static long getContentLength(SignableRequest<?> request) throws IOException {
181 final InputStream content = request.getContent();
182 if (!content.markSupported())
183 throw new IllegalStateException("Bug: request input stream must have been made mark-and-resettable at this point");
184 ReadLimitInfo info = request.getReadLimitInfo();
185 final int readLimit = info.getReadLimit();
186 long contentLength = 0;
187 byte[] tmp = new byte[4096];
188 int read;
189 content.mark(readLimit);
190 while ((read = content.read(tmp)) != -1) {
191 contentLength += read;
192 }
193 try {
194 content.reset();
195 } catch(IOException ex) {
196 throw new ResetException("Failed to reset the input stream", ex);
197 }
198 return contentLength;
199 }
200 }
201