Class Aws
AWS Signature Version 4, and the request plumbing every AWS service shares.
This is the whole of what an AWS client needs that is not service specific:
canonicalise a request, derive a signing key, and send it. S3 is one
service built on it; SQS, DynamoDB and Secrets Manager are the same three steps
with a different host and payload, which is why the signer is a separate class
rather than something private to S3.
Written rather than pulled in because the AWS SDK is not an option here: it wants reflection, a class loader and a threading model a translated server binary does not have. SigV4 itself is a hash chain -- five HMACs and a SHA-256 -- over a canonical form of the request, and the specification is public and stable.
The part that is easy to get wrong, and the reason for the length of this file, is the CANONICAL form: the signature covers a normalised URI, a sorted query string, sorted lower-cased headers and a hash of the body, and a single difference from what the service computes produces a 403 with no indication of which field disagreed.
-
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final StringThe unsigned-payload marker, for a body the caller does not want hashed. -
Method Summary
Modifier and TypeMethodDescriptionstatic Stringauthorization(Credentials credentials, String region, String service, String method, String path, Map query, Map headers, String payloadHash, String timestamp) The Authorization header value for one request.static StringcanonicalQuery(Map query) Query parameters sorted by name, each name and value percent-encoded.static StringLeading and trailing space removed, internal runs collapsed to one space.static StringRFC 3986 unreserved characters pass; everything else becomes %XX with UPPER case hex.static StringencodePath(String path) The path, percent-encoded segment by segment.static Stringhex(byte[] data) static Stringpresign(Credentials credentials, String region, String service, String method, String host, String path, Map query, int expiresSeconds, String timestamp) A presigned URL: the signature travels in the query string, so anyone holding the URL can make that one request until it expires.static Stringpresign(Credentials credentials, String region, String service, String method, String host, String path, Map query, int expiresSeconds, String timestamp, boolean secure) As above, producing an http:// URL whensecureis false.static Web.Resultsend(Credentials credentials, String region, String service, String method, String host, String path, Map query, Map headers, byte[] body, String timestamp) A signed, sent request.static Web.Resultsend(Credentials credentials, String region, String service, String method, String host, String path, Map query, Map headers, byte[] body, String timestamp, boolean secure) As above, over plain HTTP whensecureis false.static Stringsha256Hex(byte[] data) static byte[]signingKey(String secretKey, String date, String region, String service) The four-step key derivation.
-
Field Details
-
UNSIGNED_PAYLOAD
The unsigned-payload marker, for a body the caller does not want hashed.- See Also:
-
-
Method Details
-
send
public static Web.Result send(Credentials credentials, String region, String service, String method, String host, String path, Map query, Map headers, byte[] body, String timestamp) throws IOException A signed, sent request.
headersare extra "Name: value" strings; Host, x-amz-date and x-amz-content-sha256 are added here because they are part of the signature.- Throws:
IOException
-
send
public static Web.Result send(Credentials credentials, String region, String service, String method, String host, String path, Map query, Map headers, byte[] body, String timestamp, boolean secure) throws IOException As above, over plain HTTP when
secureis false.The signature covers the host and not the scheme, so this changes only how the request travels. It exists for a local MinIO or a test double on loopback; nothing reachable off the machine should use it, and AWS itself does not accept it.
- Throws:
IOException
-
authorization
public static String authorization(Credentials credentials, String region, String service, String method, String path, Map query, Map headers, String payloadHash, String timestamp) throws IOException The Authorization header value for one request.- Throws:
IOException
-
presign
public static String presign(Credentials credentials, String region, String service, String method, String host, String path, Map query, int expiresSeconds, String timestamp) throws IOException A presigned URL: the signature travels in the query string, so anyone holding the URL can make that one request until it expires.
This is what hands a mobile client a direct download or upload without proxying the bytes through the server, which is most of the reason to use object storage from an app at all.
- Throws:
IOException
-
presign
public static String presign(Credentials credentials, String region, String service, String method, String host, String path, Map query, int expiresSeconds, String timestamp, boolean secure) throws IOException As above, producing an http:// URL whensecureis false. Seesend(Credentials, String, String, String, String, String, Map, Map, byte[], String).- Throws:
IOException
-
signingKey
public static byte[] signingKey(String secretKey, String date, String region, String service) throws IOException The four-step key derivation. The signing key is scoped to a date, a region and a service, which is what keeps a leaked signature from being reusable anywhere else.
Public, along with the four canonicalisation helpers below, because a service this class does not wrap -- SQS, DynamoDB, Secrets Manager -- is the same signature over a different payload, and because these are the pieces a known-answer test can pin. A signature implementation that can only be tested end to end is one whose failures all look like 403.
- Throws:
IOException
-
canonicalQuery
-
encodePath
The path, percent-encoded segment by segment. The slashes between segments are NOT encoded; everything else that is not unreserved is -- which is why this cannot just callencode(String)on the whole path. -
encode
-
collapse
-
sha256Hex
-
hex
-