4646import java .security .NoSuchAlgorithmException ;
4747import java .time .Instant ;
4848import java .time .ZoneId ;
49+ import java .time .format .DateTimeFormatter ;
50+ import java .time .format .DateTimeFormatterBuilder ;
4951import java .util .Arrays ;
5052import java .util .Collections ;
5153import java .util .List ;
@@ -76,6 +78,11 @@ public class SigV4AuthProvider implements AuthProvider {
7678 SIGV4_INITIAL_RESPONSE = initialResponse .asReadOnlyBuffer ();
7779 }
7880
81+ private static final int AWS_FRACTIONAL_TIMESTAMP_DIGITS = 3 ; // SigV4 expects three digits of nanoseconds for timestamps
82+ private static final DateTimeFormatter timestampFormatter =
83+ (new DateTimeFormatterBuilder ()).appendInstant (AWS_FRACTIONAL_TIMESTAMP_DIGITS ).toFormatter ();
84+
85+
7986 private static final byte [] NONCE_KEY = "nonce=" .getBytes (StandardCharsets .UTF_8 );
8087 private static final int EXPECTED_NONCE_LENGTH = 32 ;
8188
@@ -206,7 +213,7 @@ public CompletionStage<ByteBuffer> evaluateChallenge(ByteBuffer challenge) {
206213 String .format ("signature=%s,access_key=%s,amzdate=%s" ,
207214 signature ,
208215 credentials .getAWSAccessKeyId (),
209- requestTimestamp );
216+ timestampFormatter . format ( requestTimestamp ) );
210217
211218 if (credentials instanceof AWSSessionCredentials ) {
212219 response = response + ",session_token=" + ((AWSSessionCredentials )credentials ).getSessionToken ();
@@ -270,7 +277,7 @@ private String generateSignature(byte[] nonce, Instant requestTimestamp, AWSCred
270277
271278 String stringToSign = String .format ("%s%n%s%n%s%n%s" ,
272279 SignerConstants .AWS4_SIGNING_ALGORITHM ,
273- requestTimestamp ,
280+ timestampFormatter . format ( requestTimestamp ) ,
274281 signingScope ,
275282 sha256Digest (canonicalRequest ));
276283
@@ -297,7 +304,7 @@ private static String canonicalizeRequest(String accessKey,
297304 String .format ("X-Amz-Credential=%s%%2F%s" ,
298305 accessKey ,
299306 URLEncoder .encode (signingScope , StandardCharsets .UTF_8 .name ())),
300- "X-Amz-Date=" + URLEncoder .encode (requestTimestamp . toString ( ), StandardCharsets .UTF_8 .name ()),
307+ "X-Amz-Date=" + URLEncoder .encode (timestampFormatter . format ( requestTimestamp ), StandardCharsets .UTF_8 .name ()),
301308 AMZ_EXPIRES_HEADER
302309 );
303310
0 commit comments