1 /* 2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"). 5 * You may not use this file except in compliance with the License. 6 * A copy of the License is located at 7 * 8 * http://aws.amazon.com/apache2.0 9 * 10 * or in the "license" file accompanying this file. This file is distributed 11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 * express or implied. See the License for the specific language governing 13 * permissions and limitations under the License. 14 */ 15 16 package software.amazon.awssdk.authcrt.signer.internal; 17 18 import static org.assertj.core.api.Assertions.assertThat; 19 import static org.junit.Assert.assertTrue; 20 import static org.mockito.ArgumentMatchers.any; 21 import static org.mockito.Mockito.when; 22 import static software.amazon.awssdk.authcrt.signer.internal.SigningUtils.SIGNING_CLOCK; 23 import static software.amazon.awssdk.authcrt.signer.internal.SigningUtils.buildCredentials; 24 import static software.amazon.awssdk.authcrt.signer.internal.SigningUtils.getSigningClock; 25 26 import java.io.ByteArrayInputStream; 27 import java.io.ByteArrayOutputStream; 28 import java.net.URI; 29 import java.nio.charset.StandardCharsets; 30 import java.text.ParseException; 31 import java.text.SimpleDateFormat; 32 import java.time.Clock; 33 import java.time.ZoneId; 34 import java.util.Arrays; 35 import java.util.Collections; 36 import java.util.LinkedHashMap; 37 import java.util.List; 38 import java.util.Map; 39 import java.util.TimeZone; 40 import java.util.stream.Collectors; 41 import java.util.stream.Stream; 42 import org.apache.commons.io.IOUtils; 43 import org.junit.Before; 44 import org.junit.Test; 45 import org.junit.runner.RunWith; 46 import org.mockito.Mock; 47 import org.mockito.junit.MockitoJUnitRunner; 48 import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; 49 import software.amazon.awssdk.auth.signer.AwsSignerExecutionAttribute; 50 import software.amazon.awssdk.core.checksums.Algorithm; 51 import software.amazon.awssdk.core.checksums.SdkChecksum; 52 import software.amazon.awssdk.core.internal.chunked.AwsChunkedEncodingConfig; 53 import software.amazon.awssdk.auth.signer.internal.chunkedencoding.AwsSignedChunkedEncodingInputStream; 54 import software.amazon.awssdk.authcrt.signer.internal.chunkedencoding.AwsS3V4aChunkSigner; 55 import software.amazon.awssdk.core.interceptor.ExecutionAttributes; 56 import software.amazon.awssdk.crt.auth.signing.AwsSigningConfig; 57 import software.amazon.awssdk.crt.auth.signing.AwsSigningResult; 58 import software.amazon.awssdk.crt.auth.signing.AwsSigningUtils; 59 import software.amazon.awssdk.http.SdkHttpFullRequest; 60 import software.amazon.awssdk.http.SdkHttpMethod; 61 import software.amazon.awssdk.regions.Region; 62 63 /** 64 * Functional tests for the S3 specific Sigv4a signer. These tests call the CRT native signer code. Because 65 * Sigv4 Asymmetric does not yield deterministic results, the signatures can only be verified by using 66 * a pre-calculated test case and calling a verification method with information from that test case. 67 */ 68 @RunWith(MockitoJUnitRunner.class) 69 public class ChunkedEncodingFunctionalTest { 70 71 private static final String CHUNKED_SIGV4A_CANONICAL_REQUEST = "PUT\n" + 72 "/examplebucket/chunkObject.txt\n" + 73 "\n" + 74 "content-encoding:aws-chunked\n" + 75 "content-length:66824\n" + 76 "host:s3.amazonaws.com\n" + 77 "x-amz-content-sha256:STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD\n" + 78 "x-amz-date:20130524T000000Z\n" + 79 "x-amz-decoded-content-length:66560\n" + 80 "x-amz-region-set:us-east-1\n" + 81 "x-amz-storage-class:REDUCED_REDUNDANCY\n" + 82 "\n" + 83 "content-encoding;content-length;host;x-amz-content-sha256;x-amz-date;x-amz-decoded-content-length;x-amz-region-set;x-amz-storage-class\n" + 84 "STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD"; 85 86 private static final String CHUNKED_TRAILER_SIGV4A_CANONICAL_REQUEST = "PUT\n" + 87 "/examplebucket/chunkObject.txt\n" + 88 "\n" + 89 "content-encoding:aws-chunked\n" + 90 "content-length:66824\n" + 91 "host:s3.amazonaws.com\n" + 92 "x-amz-content-sha256:STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD-TRAILER\n" + 93 "x-amz-date:20130524T000000Z\n" + 94 "x-amz-decoded-content-length:66560\n" + 95 "x-amz-region-set:us-east-1\n" + 96 "x-amz-storage-class:REDUCED_REDUNDANCY\n" + 97 "x-amz-trailer:first,second,third\n" + 98 "\n" + 99 "content-encoding;content-length;host;x-amz-content-sha256;x-amz-date;x-amz-decoded-content-length;x-amz-region-set;x-amz-storage-class;x-amz-trailer\n" + 100 "STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD-TRAILER"; 101 102 103 104 105 private static final String CHUNKED_ACCESS_KEY_ID = "AKIAIOSFODNN7EXAMPLE"; 106 private static final String CHUNKED_SECRET_ACCESS_KEY = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"; 107 private static final String CHUNKED_SIGV4A_TEST_ECC_PUB_X = "18b7d04643359f6ec270dcbab8dce6d169d66ddc9778c75cfb08dfdb701637ab"; 108 private static final String CHUNKED_SIGV4A_TEST_ECC_PUB_Y = "fa36b35e4fe67e3112261d2e17a956ef85b06e44712d2850bcd3c2161e9993f2"; 109 private static final String CHUNKED_TEST_REGION = "us-east-1"; 110 private static final String CHUNKED_TEST_SERVICE = "s3"; 111 private static final String CHUNKED_TEST_SIGNING_TIME = "2013-05-24T00:00:00Z"; 112 113 private static final String CHUNK_STS_PRE_SIGNATURE = "AWS4-ECDSA-P256-SHA256-PAYLOAD\n" + 114 "20130524T000000Z\n" + 115 "20130524/s3/aws4_request\n"; 116 117 private static final String CHUNK1_STS_POST_SIGNATURE = "\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n" + 118 "bf718b6f653bebc184e1479f1935b8da974d701b893afcf49e701f3e2f9f9c5a"; 119 120 private static final String CHUNK2_STS_POST_SIGNATURE = "\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n" + 121 "2edc986847e209b4016e141a6dc8716d3207350f416969382d431539bf292e4a"; 122 123 private static final String CHUNK3_STS_POST_SIGNATURE = "\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n" + 124 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; 125 public static final String X_AMZ_TRAILER_SIGNATURE = "x-amz-trailer-signature:"; 126 127 private static String TRAILING_HEADERS_STS_POST_SIGNATURE = "\n83d8f190334fb741bc8daf73c891689d320bd8017756bc730c540021ed48001f"; 128 129 private static String TRAILING_HEADERS_STS_PRE_SIGNATURE = "AWS4-ECDSA-P256-SHA256-TRAILER\n" + "20130524T000000Z\n" + 130 "20130524/s3/aws4_request\n"; 131 132 133 private static final String CRLF = "\r\n"; 134 private static final String SIGNATURE_KEY = "chunk-signature="; 135 136 private static final int DATA_SIZE = 66560; 137 /** 138 * This content-length is actually incorrect; the correct calculated length should be 67064. However, since 139 * the test case was developed using this number, it must be used when calculating the signatures or else 140 * the test will fail verification. This is also the reason the sigv4a signer cannot be called directly in 141 * a test since it would calculate a different content length. 142 */ 143 private static final int TOTAL_CONTENT_LENGTH = 66824; 144 private static final int STREAM_CHUNK_SIZE = 65536; 145 private static final int CHUNK2_SIZE = 1024; 146 private static final byte[] data; 147 148 private static final SimpleDateFormat DATE_FORMAT; 149 150 @Mock 151 SigningConfigProvider configProvider; 152 153 CrtHttpRequestConverter converter; 154 AwsCrt4aSigningAdapter adapter; 155 AwsSigningConfig chunkedRequestSigningConfig; 156 AwsSigningConfig chunkSigningConfig; 157 ExecutionAttributes executionAttributes; 158 159 static { 160 DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); 161 DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); 162 163 data = new byte[DATA_SIZE]; Arrays.fill(data, (byte) 'a')164 Arrays.fill(data, (byte) 'a'); 165 } 166 167 @Before setup()168 public void setup() throws Exception { 169 converter = new CrtHttpRequestConverter(); 170 adapter = new AwsCrt4aSigningAdapter(); 171 executionAttributes = buildBasicExecutionAttributes(); 172 chunkedRequestSigningConfig = createChunkedRequestSigningConfig(executionAttributes); 173 chunkSigningConfig = createChunkSigningConfig(executionAttributes); 174 } 175 176 @Test calling_adapter_APIs_directly_creates_correct_signatures()177 public void calling_adapter_APIs_directly_creates_correct_signatures() { 178 byte[] previousSignature = createAndVerifyRequestSignature(defaultHttpRequest().build()); 179 180 for (int i = 0; i < 3; i++) { 181 byte[] currentSignature = adapter.signChunk(getChunkData(i), previousSignature, chunkSigningConfig); 182 assertTrue(AwsSigningUtils.verifyRawSha256EcdsaSignature(createStringToSign(i, previousSignature), 183 currentSignature, 184 CHUNKED_SIGV4A_TEST_ECC_PUB_X, 185 CHUNKED_SIGV4A_TEST_ECC_PUB_Y)); 186 previousSignature = currentSignature; 187 } 188 } 189 190 @Test using_a_request_stream_creates_correct_signatures()191 public void using_a_request_stream_creates_correct_signatures() throws Exception { 192 SdkHttpFullRequest request = defaultHttpRequest() 193 .contentStreamProvider(() -> new ByteArrayInputStream(data)) 194 .build(); 195 byte[] requestSignature = createAndVerifyRequestSignature(request); 196 197 AwsS3V4aChunkSigner chunkSigner = new AwsS3V4aChunkSigner(adapter, chunkSigningConfig); 198 AwsChunkedEncodingConfig chunkedEncodingConfig = AwsChunkedEncodingConfig.builder() 199 .chunkSize(STREAM_CHUNK_SIZE) 200 .build(); 201 202 AwsSignedChunkedEncodingInputStream stream = 203 204 AwsSignedChunkedEncodingInputStream.builder() 205 .inputStream(request.contentStreamProvider().get().newStream()) 206 .awsChunkSigner(chunkSigner) 207 .awsChunkedEncodingConfig(chunkedEncodingConfig) 208 .headerSignature(new String(requestSignature, StandardCharsets.UTF_8)) 209 .build(); 210 211 ByteArrayOutputStream output = new ByteArrayOutputStream(); 212 IOUtils.copy(stream, output); 213 String result = new String(output.toByteArray(), StandardCharsets.UTF_8); 214 215 assertChunks(result, 3, requestSignature, false); 216 } 217 218 @Test calling_adapter_APIs_directly_creates_correct_signatures_for_trailer_headers()219 public void calling_adapter_APIs_directly_creates_correct_signatures_for_trailer_headers() throws Exception { 220 SdkHttpFullRequest sdkHttpFullRequest = createChunkedTrailerTestRequest().build(); 221 chunkedRequestSigningConfig.setAlgorithm(AwsSigningConfig.AwsSigningAlgorithm.SIGV4_ASYMMETRIC); 222 chunkedRequestSigningConfig.setSignedBodyValue(AwsSigningConfig.AwsSignedBodyValue.STREAMING_AWS4_ECDSA_P256_SHA256_PAYLOAD_TRAILER); 223 SdkSigningResult result = adapter.sign(sdkHttpFullRequest, chunkedRequestSigningConfig); 224 byte[] requestSignature = result.getSignature(); 225 226 assertTrue(AwsSigningUtils.verifySigv4aEcdsaSignature(converter.requestToCrt(sdkHttpFullRequest), 227 CHUNKED_TRAILER_SIGV4A_CANONICAL_REQUEST, 228 chunkedRequestSigningConfig, requestSignature, 229 CHUNKED_SIGV4A_TEST_ECC_PUB_X, 230 CHUNKED_SIGV4A_TEST_ECC_PUB_Y)); 231 byte[] previousSignature = result.getSignature(); 232 233 for (int i = 0; i < 3; i++) { 234 byte[] currentSignature = adapter.signChunk(getChunkData(i), previousSignature, chunkSigningConfig); 235 assertTrue(AwsSigningUtils.verifyRawSha256EcdsaSignature(createStringToSign(i, previousSignature), 236 currentSignature, 237 CHUNKED_SIGV4A_TEST_ECC_PUB_X, 238 CHUNKED_SIGV4A_TEST_ECC_PUB_Y)); 239 previousSignature = currentSignature; 240 } 241 updateTrailerHeaderSigningConfig(); 242 Map<String, List<String>> trailerHeader = getTrailerHeaderMap(); 243 AwsSigningResult trailingHeadersStringToSignResult = adapter.signTrailerHeaders(trailerHeader, previousSignature, 244 chunkedRequestSigningConfig); 245 byte[] trailingHeadersStringToSign = buildTrailingHeadersStringToSign(previousSignature, 246 TRAILING_HEADERS_STS_POST_SIGNATURE); 247 assertTrue(AwsSigningUtils.verifyRawSha256EcdsaSignature(trailingHeadersStringToSign, 248 trailingHeadersStringToSignResult.getSignature(), 249 CHUNKED_SIGV4A_TEST_ECC_PUB_X, 250 CHUNKED_SIGV4A_TEST_ECC_PUB_Y)); 251 } 252 253 254 @Test using_a_request_stream_with_checksum_trailer_creates_correct_signatures()255 public void using_a_request_stream_with_checksum_trailer_creates_correct_signatures() throws Exception { 256 SdkHttpFullRequest request = defaultHttpRequest() 257 .contentStreamProvider(() -> new ByteArrayInputStream(data)) 258 .build(); 259 byte[] requestSignature = createAndVerifyRequestSignature(request); 260 261 AwsS3V4aChunkSigner chunkSigner = new AwsS3V4aChunkSigner(adapter, chunkSigningConfig); 262 AwsChunkedEncodingConfig chunkedEncodingConfig = AwsChunkedEncodingConfig.builder() 263 .chunkSize(STREAM_CHUNK_SIZE) 264 .build(); 265 266 AwsSignedChunkedEncodingInputStream stream = 267 AwsSignedChunkedEncodingInputStream.builder() 268 .checksumHeaderForTrailer("x-amz-checksum-crc32") 269 .sdkChecksum(SdkChecksum.forAlgorithm(Algorithm.CRC32)) 270 .inputStream(request.contentStreamProvider().get().newStream()) 271 .awsChunkSigner(chunkSigner) 272 .awsChunkedEncodingConfig(chunkedEncodingConfig) 273 .headerSignature(new String(requestSignature, StandardCharsets.UTF_8)) 274 .build(); 275 276 ByteArrayOutputStream output = new ByteArrayOutputStream(); 277 IOUtils.copy(stream, output); 278 String result = new String(output.toByteArray(), StandardCharsets.UTF_8); 279 assertChunks(result, 3, requestSignature, true); 280 } 281 getTrailerHeaderMap()282 private Map<String, List<String>> getTrailerHeaderMap() { 283 Map<String, List<String>> trailerHeader = new LinkedHashMap<>(); 284 trailerHeader.put("first", Collections.singletonList("1st")); 285 trailerHeader.put("second", Collections.singletonList("2nd")); 286 trailerHeader.put("third", Collections.singletonList("3rd")); 287 return trailerHeader; 288 } 289 updateTrailerHeaderSigningConfig()290 private void updateTrailerHeaderSigningConfig() { 291 chunkedRequestSigningConfig.setAlgorithm(AwsSigningConfig.AwsSigningAlgorithm.SIGV4_ASYMMETRIC); 292 chunkedRequestSigningConfig.setSignatureType(AwsSigningConfig.AwsSignatureType.HTTP_REQUEST_TRAILING_HEADERS); 293 chunkedRequestSigningConfig.setSignedBodyHeader(AwsSigningConfig.AwsSignedBodyHeaderType.NONE); 294 } 295 296 assertChunks(String result, int numExpectedChunks, byte[] requestSignature, boolean isTrailerChecksum)297 private void assertChunks(String result, int numExpectedChunks, byte[] requestSignature, boolean isTrailerChecksum) { 298 List<String> lines = Stream.of(result.split(CRLF)).collect(Collectors.toList()); 299 assertThat(lines).hasSize(numExpectedChunks * 2 - 1 + (isTrailerChecksum ? 2 : 0)); 300 byte[] previousSignature = requestSignature; 301 302 int index = 0; 303 for (String line : lines) { 304 305 int chunk = lines.indexOf(line) / 2; 306 if (lines.indexOf(line) % 2 == 0) { 307 boolean isFinalTrailerSignature = isTrailerChecksum && index == lines.size() - 1; 308 String signatureValue = isFinalTrailerSignature ? 309 line.substring(line.indexOf(X_AMZ_TRAILER_SIGNATURE) 310 + X_AMZ_TRAILER_SIGNATURE.length()) : 311 line.substring(line.indexOf(SIGNATURE_KEY) + SIGNATURE_KEY.length()); 312 byte[] currentSignature = signatureValue.getBytes(StandardCharsets.UTF_8); 313 assertThat(signatureValue.length()).isEqualTo(AwsS3V4aChunkSigner.getSignatureLength()); 314 if (!isFinalTrailerSignature) { 315 assertTrue(AwsSigningUtils.verifyRawSha256EcdsaSignature(createStringToSign(chunk, previousSignature), 316 currentSignature, 317 CHUNKED_SIGV4A_TEST_ECC_PUB_X, 318 CHUNKED_SIGV4A_TEST_ECC_PUB_Y)); 319 } else { 320 assertThat(signatureValue).hasSize(144); 321 } 322 previousSignature = currentSignature; 323 } 324 index++; 325 } 326 } 327 createAndVerifyRequestSignature(SdkHttpFullRequest request)328 private byte[] createAndVerifyRequestSignature(SdkHttpFullRequest request) { 329 SdkSigningResult result = adapter.sign(request, chunkedRequestSigningConfig); 330 byte[] requestSignature = result.getSignature(); 331 assertTrue(AwsSigningUtils.verifySigv4aEcdsaSignature(converter.requestToCrt(request), 332 CHUNKED_SIGV4A_CANONICAL_REQUEST, 333 chunkedRequestSigningConfig, 334 requestSignature, 335 CHUNKED_SIGV4A_TEST_ECC_PUB_X, 336 CHUNKED_SIGV4A_TEST_ECC_PUB_Y)); 337 return requestSignature; 338 } 339 buildBasicExecutionAttributes()340 private ExecutionAttributes buildBasicExecutionAttributes() throws ParseException { 341 ExecutionAttributes executionAttributes = new ExecutionAttributes(); 342 executionAttributes.putAttribute(SIGNING_CLOCK, Clock.fixed(DATE_FORMAT.parse(CHUNKED_TEST_SIGNING_TIME).toInstant(), ZoneId.systemDefault())); 343 executionAttributes.putAttribute(AwsSignerExecutionAttribute.SERVICE_SIGNING_NAME, CHUNKED_TEST_SERVICE); 344 executionAttributes.putAttribute(AwsSignerExecutionAttribute.SIGNING_REGION, Region.of(CHUNKED_TEST_REGION)); 345 executionAttributes.putAttribute(AwsSignerExecutionAttribute.AWS_CREDENTIALS, 346 AwsBasicCredentials.create(CHUNKED_ACCESS_KEY_ID, CHUNKED_SECRET_ACCESS_KEY)); 347 executionAttributes.putAttribute(AwsSignerExecutionAttribute.SIGNER_DOUBLE_URL_ENCODE, false); 348 return executionAttributes; 349 } 350 createChunkedRequestSigningConfig(ExecutionAttributes executionAttributes)351 private AwsSigningConfig createChunkedRequestSigningConfig(ExecutionAttributes executionAttributes) throws Exception { 352 AwsSigningConfig config = createBasicSigningConfig(executionAttributes); 353 354 config.setUseDoubleUriEncode(executionAttributes.getAttribute(AwsSignerExecutionAttribute.SIGNER_DOUBLE_URL_ENCODE)); 355 config.setShouldNormalizeUriPath(true); 356 357 config.setSignedBodyHeader(AwsSigningConfig.AwsSignedBodyHeaderType.X_AMZ_CONTENT_SHA256); 358 config.setSignatureType(AwsSigningConfig.AwsSignatureType.HTTP_REQUEST_VIA_HEADERS); 359 360 config.setSignedBodyValue(AwsSigningConfig.AwsSignedBodyValue.STREAMING_AWS4_ECDSA_P256_SHA256_PAYLOAD); 361 362 return config; 363 } 364 createChunkSigningConfig(ExecutionAttributes executionAttributes)365 private AwsSigningConfig createChunkSigningConfig(ExecutionAttributes executionAttributes) { 366 AwsSigningConfig config = createBasicSigningConfig(executionAttributes); 367 368 config.setSignedBodyHeader(AwsSigningConfig.AwsSignedBodyHeaderType.NONE); 369 config.setSignatureType(AwsSigningConfig.AwsSignatureType.HTTP_REQUEST_CHUNK); 370 371 return config; 372 } 373 createBasicSigningConfig(ExecutionAttributes executionAttributes)374 private AwsSigningConfig createBasicSigningConfig(ExecutionAttributes executionAttributes) { 375 AwsSigningConfig config = new AwsSigningConfig(); 376 config.setCredentials(buildCredentials(executionAttributes)); 377 config.setService(executionAttributes.getAttribute(AwsSignerExecutionAttribute.SERVICE_SIGNING_NAME)); 378 config.setRegion(executionAttributes.getAttribute(AwsSignerExecutionAttribute.SIGNING_REGION).id()); 379 config.setTime(getSigningClock(executionAttributes).instant().toEpochMilli()); 380 config.setAlgorithm(AwsSigningConfig.AwsSigningAlgorithm.SIGV4_ASYMMETRIC); 381 return config; 382 } 383 defaultHttpRequest()384 private SdkHttpFullRequest.Builder defaultHttpRequest() { 385 return SdkHttpFullRequest.builder() 386 .method(SdkHttpMethod.PUT) 387 .putHeader("x-amz-storage-class", "REDUCED_REDUNDANCY") 388 .putHeader("Content-Encoding", "aws-chunked") 389 .uri(URI.create("https://s3.amazonaws.com/examplebucket/chunkObject.txt")) 390 .putHeader("x-amz-decoded-content-length", Integer.toString(DATA_SIZE)) 391 .putHeader("Content-Length", Integer.toString(TOTAL_CONTENT_LENGTH)); 392 } 393 createChunkedTrailerTestRequest()394 private SdkHttpFullRequest.Builder createChunkedTrailerTestRequest(){ 395 return defaultHttpRequest().putHeader("x-amz-trailer", "first,second,third"); 396 } 397 getChunkData(int chunk)398 private byte[] getChunkData(int chunk) { 399 switch(chunk) { 400 case 0: return Arrays.copyOfRange(data, 0, STREAM_CHUNK_SIZE); 401 case 1: return Arrays.copyOfRange(data, STREAM_CHUNK_SIZE, STREAM_CHUNK_SIZE + CHUNK2_SIZE); 402 default: return new byte[0]; 403 } 404 } 405 createStringToSign(int chunk, byte[] previousSignature)406 private byte[] createStringToSign(int chunk, byte[] previousSignature) { 407 switch(chunk) { 408 case 0: return buildChunkStringToSign(previousSignature, CHUNK1_STS_POST_SIGNATURE); 409 case 1: return buildChunkStringToSign(previousSignature, CHUNK2_STS_POST_SIGNATURE); 410 default: return buildChunkStringToSign(previousSignature, CHUNK3_STS_POST_SIGNATURE); 411 } 412 } 413 buildChunkStringToSign(byte[] previousSignature, String stsPostSignature)414 private byte[] buildChunkStringToSign(byte[] previousSignature, String stsPostSignature) { 415 StringBuilder stsBuilder = new StringBuilder(); 416 417 stsBuilder.append(CHUNK_STS_PRE_SIGNATURE); 418 String signature = new String(previousSignature, StandardCharsets.UTF_8); 419 int paddingIndex = signature.indexOf('*'); 420 if (paddingIndex != -1) { 421 signature = signature.substring(0, paddingIndex); 422 } 423 stsBuilder.append(signature); 424 stsBuilder.append(stsPostSignature); 425 426 return stsBuilder.toString().getBytes(StandardCharsets.UTF_8); 427 } buildTrailingHeadersStringToSign(byte[] previousSignature, String stsPostSignature)428 private byte[] buildTrailingHeadersStringToSign(byte[] previousSignature, String stsPostSignature) { 429 StringBuilder stsBuilder = new StringBuilder(); 430 431 stsBuilder.append(TRAILING_HEADERS_STS_PRE_SIGNATURE); 432 String signature = new String(previousSignature, StandardCharsets.UTF_8); 433 int paddingIndex = signature.indexOf('*'); 434 if (paddingIndex != -1) { 435 signature = signature.substring(0, paddingIndex); 436 } 437 stsBuilder.append(signature); 438 stsBuilder.append(stsPostSignature); 439 440 return stsBuilder.toString().getBytes(StandardCharsets.UTF_8); 441 } 442 443 } 444