package com.mongodb.internal.connection;
import org.bson.internal.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.security.sasl.SaslException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
final class {
private static final String = "AWS4-HMAC-SHA256";
private static final String = "sts";
private final String ;
private final String ;
private final String ;
private final String ;
private final String ;
private final byte[] ;
private final Map<String, String> ;
private final String body;
private (final Builder builder) throws SaslException {
this.sessionToken = builder.sessionToken;
this.host = builder.host;
this.timestamp = builder.timestamp;
this.nonce = builder.nonce;
this.body = "Action=GetCallerIdentity&Version=2011-06-15";
this.requestHeaders = getRequestHeaders();
String canonicalRequest = createCanonicalRequest("POST", "", body, requestHeaders);
String toSign = createStringToSign(hash(canonicalRequest), getTimestamp(), getCredentialScope());
this.signature = calculateSignature(toSign, builder.secretKey, getDate(), getRegion(host), SERVICE);
this.authorizationHeader = String.format("%s Credential=%s/%s, SignedHeaders=%s, Signature=%s", AWS4_HMAC_SHA256,
builder.accessKeyID, getCredentialScope(), getSignedHeaders(this.requestHeaders), getSignature());
}
static String (final String method, final String query, final String body,
final Map<String, String> requestHeaders) throws SaslException {
final String headers = getCanonicalHeaders(requestHeaders);
final String signedHeaders = getSignedHeaders(requestHeaders);
final List<String> request = Arrays.asList(method, "/", query, headers, signedHeaders, hash(body));
return String.join("\n", request);
}
static String (final String hash, final String timestamp, final String credentialScope) {
final List<String> toSign = Arrays.asList(AWS4_HMAC_SHA256, timestamp, credentialScope, hash);
return String.join("\n", toSign);
}
static String (final String toSign, final String secret, final String date, final String region,
final String service) throws SaslException {
byte[] kDate = hmac(decodeUTF8("AWS4" + secret), decodeUTF8(date));
byte[] kRegion = hmac(kDate, decodeUTF8(region));
byte[] kService = hmac(kRegion, decodeUTF8(service));
byte[] kSigning = hmac(kService, decodeUTF8("aws4_request"));
return hexEncode(hmac(kSigning, decodeUTF8(toSign)));
}
private Map<String, String> () {
if (this.requestHeaders != null) {
return this.requestHeaders;
}
Map<String, String> requestHeaders = new HashMap<>();
requestHeaders.put("Content-Type", "application/x-www-form-urlencoded");
requestHeaders.put("Content-Length", String.valueOf(this.body.length()));
requestHeaders.put("Host", this.host);
requestHeaders.put("X-Amz-Date", this.timestamp);
requestHeaders.put("X-MongoDB-Server-Nonce", Base64.encode(this.nonce));
requestHeaders.put("X-MongoDB-GS2-CB-Flag", "n");
if (this.sessionToken != null) {
requestHeaders.put("X-Amz-Security-Token", this.sessionToken);
}
return requestHeaders;
}
private String () throws SaslException {
return String.format("%s/%s/%s/aws4_request", getDate(), getRegion(this.host), SERVICE);
}
static String (final Map<String, String> requestHeaders) {
return requestHeaders.keySet().stream()
.map(String::toLowerCase)
.sorted()
.collect(Collectors.joining(";"));
}
static String (final Map<String, String> requestHeaders) {
return requestHeaders.entrySet().stream()
.map(kvp -> String.format("%s:%s\n", kvp.getKey().toLowerCase(), kvp.getValue().trim().replaceAll(" +", " ")))
.sorted()
.collect(Collectors.joining(""));
}
static String (final String host) throws SaslException {
String word = "(\\w)+(-\\w)*";
if (host.equals("sts.amazonaws.com") || host.matches(String.format("%s", word))) {
return "us-east-1";
}
if (host.matches(String.format("%s(.%s)+", word, word))) {
return host.split("\\.")[1];
}
throw new SaslException("Invalid host");
}
String () {
return this.signature;
}
String () {
return this.timestamp;
}
private String () {
return getTimestamp().substring(0, "YYYYMMDD".length());
}
static String (final String str) throws SaslException {
return hexEncode(sha256(str)).toLowerCase();
}
private static String (final byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
private static byte[] (final String str) {
return str.getBytes(StandardCharsets.UTF_8);
}
private static byte[] (final byte[] secret, final byte[] message) throws SaslException {
byte[] hmacSha256;
try {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec spec = new SecretKeySpec(secret, "HmacSHA256");
mac.init(spec);
hmacSha256 = mac.doFinal(message);
} catch (Exception e) {
throw new SaslException(e.getMessage());
}
return hmacSha256;
}
private static byte[] (final String payload) throws SaslException {
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new SaslException(e.getMessage());
}
return md.digest(payload.getBytes(StandardCharsets.UTF_8));
}
@Override
public String () {
return this.authorizationHeader;
}
public static AuthorizationHeader.Builder () {
return new AuthorizationHeader.Builder();
}
static final class {
private String ;
private String ;
private String ;
private String ;
private String ;
private byte[] ;
private () {}
Builder (final String accessKeyID) {
this.accessKeyID = accessKeyID;
return this;
}
Builder (final String secretKey) {
this.secretKey = secretKey;
return this;
}
Builder (final String sessionToken) {
this.sessionToken = sessionToken;
return this;
}
Builder (final String host) {
this.host = host;
return this;
}
Builder (final String timestamp) {
this.timestamp = timestamp;
return this;
}
Builder (final byte[] nonce) {
this.nonce = nonce;
return this;
}
AuthorizationHeader () throws SaslException {
return new AuthorizationHeader(this);
}
}
}