0

我想使用 Amazon Chime API 扩展我用 JavaFX 编写的软件以使用其消息传递。我知道有 JS SDK 可以毫无问题地建立消息传递 websocket 会话。但是在 java SDK 中没有相关的类。所以我想使用 STOMP 库来使用 websocket 端点。

当时我正在努力提出正确的请求,即签署 AWS 请求(计算 X-AMZ-Signature)

根据帖子,我正在尝试计算正确的 X-AMZ-Signature 请求参数。这是课程:

@Slf4j
@Service
public class Aws4Signer {
    
    private final static String REQUEST_CONTENT_TYPE = "application/json";
    private final static String AUTH_ALGORITHM = "AWS4-HMAC-SHA256";
    private final static String REQUEST_METHOD = "GET";

    @Data
    class AuthenticationData {
        @NonNull
        String timestamp;
        @NonNull
        String date;
        @NonNull
        String authorizationHeader;
    }
    
    private AppConfig appConfig = new AppConfig();

    /**
     * Gets the timestamp in YYYYMMDD'T'HHMMSS'Z' format, which is the required
     * format for AWS4 signing request headers and credential string
     *
     * @param dateTime
     *            an OffsetDateTime object representing the UTC time of current
     *            signing request
     * @return the formatted timestamp string
     *
     * @see <a href=
     *      "https://docs.aws.amazon.com/general/latest/gr/sigv4-signed-request-examples.html">
     *      Examples of the Complete Version 4 Signing Process (Python)</a>
     */
    public String getTimeStamp(OffsetDateTime dateTime) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'");
        String formatDateTime = dateTime.format(formatter);
        return formatDateTime;
    }

    /**
     * Gets the date string in yyyyMMdd format, which is required to build the
     * credential scope string
     *
     * @return the formatted date string
     */
    public String getDate(OffsetDateTime dateTime) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        String formatDateTime = dateTime.format(formatter);
        return formatDateTime;
    }

    public byte[] generateAws4SigningKey(String timestamp) {
        String secretKey = appConfig.getAwsAuthConfig().getSecretKey();
        String regionName = appConfig.getAwsAuthConfig().getServiceRegion();
        String serviceName = appConfig.getAwsAuthConfig().getServiceName();

        byte[] signatureKey = null;
        try {
            signatureKey = Aws4SignatureKeyGenerator.generateSignatureKey(secretKey, timestamp, regionName,
                    serviceName);
        } catch (Exception e) {
            log.error("An error has ocurred when generate signature key: " + e, e);
        }

        return signatureKey;
    }

    /**
     * Builds an {@link AuthenticationData} object containing the timestamp, date,
     * payload hash and the AWS4 signature
     * <p>
     *
     * The signing logic was translated from the Python implementation, see this
     * link for more details: <a href=
     * "https://docs.aws.amazon.com/general/latest/gr/sigv4-signed-request-examples.html">Examples
     * of the Complete Version 4 Signing Process (Python)</a>
     *
     * @param target
     * @param requestBody
     *
     * @return
     * @throws NoSuchAlgorithmException
     * @throws UnsupportedEncodingException
     * @throws InvalidKeyException
     * @throws SignatureException
     * @throws IllegalStateException
     *
     */
    public AuthenticationData buildAuthorizationData() throws NoSuchAlgorithmException,
            UnsupportedEncodingException, InvalidKeyException, SignatureException, IllegalStateException {
        log.info("predict - start");

        // Starting building the lengthy signing data
        AwsAuthConfig awsAuthConfig = appConfig.getAwsAuthConfig();
        String payloadHash = Hmac.getSha256Hash(requestBody);

        OffsetDateTime now = OffsetDateTime.now(ZoneOffset.UTC);
        String timestamp = getTimeStamp(now);
        String date = getDate(now);

        // Step 1 is to define the verb (GET, POST, etc.) -- already done by defining
        // constant REQUEST_METHOD

        // Step 2: Create canonical URI--the part of the URI from domain to query
        // string (use '/' if no path)
        String canonical_uri = "/connect";

        // Step 3: Create the canonical query string. In this example, request
        // parameters are passed in the body of the request and the query string
        // is blank.
        String canonical_querystring = buildCanonicalQueryString();

        // Step 4: Create the canonical headers. Header names must be trimmed
        // and lowercase, and sorted in code point order from low to high.
        // Note that there is a trailing \n.
        String canonical_headers = "content-type:" + REQUEST_CONTENT_TYPE + "\n"
                                 + "host:" + awsAuthConfig.getServiceHost() + "\n"
                                 + "x-amz-date:" + timestamp + "\n";
        
        String signed_headers = "content-type;host;x-amz-date";

        log.debug("canonical_headers : {}", canonical_headers);

        String canonical_request = REQUEST_METHOD + "\n" + canonical_uri + "\n" + canonical_querystring + "\n"
                + canonical_headers + "\n" + signed_headers;

        log.debug("canonical_request : {}", canonical_request);

        String credential_scope = date + "/" + awsAuthConfig.getServiceRegion() + "/" + awsAuthConfig.getServiceName()
                + "/" + "aws4_request";
        String canonical_request_hash = Hmac.getSha256Hash(canonical_request);

        log.debug("canonical_request_hash : {}", canonical_request_hash);

        String string_to_sign = AUTH_ALGORITHM + "\n" + timestamp + "\n" + credential_scope + "\n"
                + canonical_request_hash;

        log.debug("string_to_sign : {}", string_to_sign);
        byte[] sigKey = generateAws4SigningKey(date);

        String signature = Hmac.calculateHMAC(string_to_sign, sigKey, Hmac.HMAC_SHA256);
        String authorization_header = AUTH_ALGORITHM + " " + "Credential=" + awsAuthConfig.getAccessKey() + "/"
                + credential_scope + ", " + "SignedHeaders=" + signed_headers + ", " + "Signature=" + signature;

        log.debug("authorization_header : {}", authorization_header);

        return new AuthenticationData(timestamp, date, authorization_header);
    }

    private String buildCanonicalQueryString() {
        String canonicalRequest = REQUEST_METHOD + "\n" +
                                 "/connect" + "\n" +
                "X-Amz-Algorithm=AWS4-HMAC-SHA256\n" +
                "&X-Amz-Credential=MYACCESKEY%2F"+ getDate(OffsetDateTime.now()) + "%2Fus-east-1%2Fchime%2Faws4_request\n" +
                "&X-Amz-Date=" + getTimeStamp(OffsetDateTime.now()) +"\n" +
                "&X-Amz-Expires=10\n" +
                "&X-Amz-SignedHeaders=host\n" +
                "&sessionId=" + UUID.randomUUID() +"\n" +
                "&userArn=" + "MYUSERARN";

        return canonicalRequest;
    }

}

提供的信息

  • 主机:node001.ue1.ws-messaging.chime.aws
  • 服务名称:钟声
  • 地区:us-east-1

它制作了签名,我正在尝试通过邮递员使用它,但邮递员无法连接到端点 node001.ue1.ws-messaging.chime.aws/connect,只是说“连接 ETIMEDOUT 54.162.103.101:80”。

我是亚马逊的新手,所以对我来说有点难。你能说我哪里错了吗?

任何帮助表示赞赏!

4

1 回答 1

0

为连接到 chime websocket 的签名 URL 编写了完整的工作代码。希望这会对某人有所帮助!

于 2021-08-26T12:56:42.000 回答