InvalidSignatureException 从 SQL 向 AWS Kinesis 发送 POST 请求

InvalidSignatureException Sending POST request to AWS Kinesis from SQL

我正在尝试从 SQL 向我的运动流发送数据,但我只能找回错误。

{
    "__type": "InvalidSignatureException",
    "message": "The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details."
}

我的密钥和密码是正确的,因为我已经在 Postman 中成功测试了。

这是我创建身份验证的函数header...

ALTER FUNCTION [dbo].[CreateAuth]
(
    @awsAccessKey NVARCHAR(MAX),
    @awsSecretKey NVARCHAR(MAX),
    @content NVARCHAR(MAX),
    @dateTime Datetime
)
RETURNS VARCHAR(MAX)
AS
BEGIN

    DECLARE @awsRegion NVARCHAR(MAX) = 'eu-west-2';
    DECLARE @awsService NVARCHAR(MAX) = 'kinesis';
    DECLARE @timeStamp NVARCHAR(16) = FORMAT(@dateTime, 'yyyyMMddTHHmmssZ');
    DECLARE @scope NVARCHAR(MAX) = FORMAT(@dateTime, 'yyyyMMdd') + '/'+@awsRegion+'/'+@awsService+'/aws4_request'

    DECLARE @x_amz_content_sha256 NVARCHAR(MAX);
    DECLARE @hexbin VARBINARY(max) =  HASHBYTES('SHA2_256 ',@content);   

    SET @x_amz_content_sha256 = LOWER(CONVERT([varchar](512), @hexbin,2))
    
    -- CANONICAL REQUEST
        DECLARE @CanonicalRequest NVARCHAR(MAX) = '';
        -- HTTP verb
        SET @CanonicalRequest += 'POST' + CHAR(13) 
        -- URL
        SET @CanonicalRequest += 'kinesis.eu-west-2.amazonaws.com' + CHAR(13) 

        -- QUERYSTRING (must be sorted alphbetically)
        SET @CanonicalRequest += '' + CHAR(13) 

        -- HEADERS (must be sorted alphbetically)
        SET @CanonicalRequest += 'content-type:application/x-amz-json-1.1' + CHAR(13) 
        SET @CanonicalRequest += 'host:kinesis.eu-west-2.amazonaws.com' + CHAR(13) 
        SET @CanonicalRequest += 'x-amz-content-sha256:' + @x_amz_content_sha256 + CHAR(13) 
        SET @CanonicalRequest += 'x-amz-date:' + @timeStamp + CHAR(13) 
        SET @CanonicalRequest += 'x-amz-target:' + 'Kinesis_20131202.PutRecord' + CHAR(13) 

        -- SIGNED HEADERS
        DECLARE @signedheaders NVARCHAR(MAX) = 'content-type;host;x-amz-content-sha256;x-amz-date;x-amz-target'
        SET @CanonicalRequest += @signedheaders + CHAR(13) 

        -- HASHED PAYLOAD
        SET @CanonicalRequest += @x_amz_content_sha256

        DECLARE @CanonicalRequestHexbin VARBINARY(max) = HASHBYTES('SHA2_256 ',@CanonicalRequest);  

    -- STRING TO SIGN
        DECLARE @stringToSign NVARCHAR(MAX) = '';
        SET @stringToSign += 'AWS4-HMAC-SHA256' + CHAR(13) 
        SET @stringToSign += @timeStamp + CHAR(13) 
        SET @stringToSign += @scope + CHAR(13) 
        SET @stringToSign += LOWER(CONVERT([varchar](512), @CanonicalRequestHexbin,2))

    -- CALCULATE SIGNATURE
        DECLARE @DateKey VARBINARY(64); 
        DECLARE @DateRegionKey VARBINARY(64);
        DECLARE @DateRegionServiceKey VARBINARY(64);
        DECLARE @SigningKey VARBINARY(64);
        DECLARE @Signature VARBINARY(64);

        SET @DateKey                = dbo.HMAC('SHA2_256',CONVERT(VARBINARY(MAX), 'AWS4'+@awsSecretKey),CONVERT(VARBINARY(MAX),FORMAT(@dateTime, 'yyyyMMdd')))
        SET @DateRegionKey          = dbo.HMAC('SHA2_256', @DateKey,                CONVERT(VARBINARY(MAX),@awsRegion))
        SET @DateRegionServiceKey   = dbo.HMAC('SHA2_256', @DateRegionKey,          CONVERT(VARBINARY(MAX),@awsService))
        SET @SigningKey             = dbo.HMAC('SHA2_256', @DateRegionServiceKey,   CONVERT(VARBINARY(MAX),'aws4_request'))

        SET @Signature = dbo.HMAC('SHA2_256',@SigningKey,CONVERT(VARBINARY(MAX),@stringToSign));

    --BUILD Authorization
    DECLARE @AuthValue NVARCHAR(MAX) = '';
    SET @AuthValue += 'AWS4-HMAC-SHA256 Credential=' + @awsAccessKey + '/'+ @scope 
    SET @AuthValue += ',SignedHeaders=' + @signedheaders 
    SET @AuthValue += ',Signature=' + LOWER(CONVERT([varchar](512), @Signature,2))

    RETURN @AuthValue

这是对 kinesis 的调用

-- Open the connection.
    EXEC @ret = sp_OACreate 'MSXML2.ServerXMLHTTP', @token OUT;
    IF @ret <> 0 RAISERROR('Unable to open HTTP connection.', 10, 1);

-- Send the request.
    EXEC @ret = sp_OAMethod @token, 'open', NULL, 'POST', @url, 'false';
    SET @auth = dbo.CreateAuth('MYACCESSKEY','MYSECRETKEY',@postData, @datetime)
    EXEC @ret = sp_OAMethod @token, 'setRequestHeader', NULL, 'Authorization', @auth

    PRINT @auth
    PRINT ''

    DECLARE @hexbin VARBINARY(max) =  HASHBYTES('SHA2_256 ',@postData); --hash data
    DECLARE @x_amz_content_sha256 NVARCHAR(MAX) = LOWER(CONVERT([varchar](512), @hexbin,2)) -- get hex of hashed data
    EXEC @ret = sp_OAMethod @token, 'setRequestHeader', NULL, 'content-type', @contentType;
    EXEC @ret = sp_OAMethod @token, 'setRequestHeader', NULL, 'host', 'kinesis.eu-west-2.amazonaws.com';
    EXEC @ret = sp_OAMethod @token, 'setRequestHeader', NULL, 'x-amz-content-sha256', @x_amz_content_sha256;
    EXEC @ret = sp_OAMethod @token, 'setRequestHeader', NULL, 'x-amz-date', @xAmzDate;
    EXEC @ret = sp_OAMethod @token, 'setRequestHeader', NULL, 'x-amz-target', @xAmzTarget;
    EXEC @ret = sp_OAMethod @token, 'send', NULL, @postData;

-- Handle the response.
    EXEC @ret = sp_OAGetProperty @token, 'status', @status OUT;
    EXEC @ret = sp_OAGetProperty @token, 'statusText', @statusText OUT;
    EXEC @ret = sp_OAGetProperty @token, 'responseText', @responseText OUT;

-- Show the response.
    PRINT 'Status: ' + @status + ' (' + @statusText + ')';
    PRINT 'Response text: ' + @responseText;

-- Close the connection.
    EXEC @ret = sp_OADestroy @token;
    IF @ret <> 0 RAISERROR('Unable to close HTTP connection.', 10, 1);

我用的HMAC函数就是这个...https://gist.github.com/rmalayter/3130462 我已经使用 https://codebeautify.org/

验证了 SHA2_256 和 HMAC 函数产生的结果是正确的

我很确定我所做的一切都是根据... https://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html and https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html

我不知道下一步该看什么。

我现在已经解决了。除了我懒惰的硬编码之外,还有很多问题。

  • CanonicalURI 应该只是“/”。 - 感谢@AlwayLearning。
  • 将所有字符串变量更改为 VARCHAR 而不是 NVARCHAR,这会导致不正确的散列。
  • 用于@datekey 的日期也需要是 VARCHAR CONVERT(VARCHAR(MAX),FORMAT(@dateTime, 'yyyyMMdd')) 作为 FORMAT() 函数 returns NVARCHAR。

我发现这个网站非常有用 http://aws-signature.com.s3-website-us-west-2.amazonaws.com/

成功的功能现在看起来像这样...

ALTER FUNCTION [dbo].[CreateAuth]
(
    @awsAccessKey VARCHAR(MAX),
    @awsSecretKey VARCHAR(MAX),
    @content VARCHAR(MAX),
    @dateTime Datetime,
    @awsRegion VARCHAR(MAX),
    @awsService VARCHAR(MAX),
    @CanonicalRequestURI VARCHAR(MAX),
    @host VARCHAR(MAX),
    @xAmzTarget VARCHAR(MAX),
    @contentType VARCHAR(MAX)
)
RETURNS VARCHAR(MAX)
AS
BEGIN
    DECLARE @date VARCHAR(8) = FORMAT(@dateTime, 'yyyyMMdd')
    DECLARE @timeStamp VARCHAR(16) = FORMAT(@dateTime, 'yyyyMMddTHHmmssZ');
    DECLARE @scope VARCHAR(MAX) = @date + '/'+@awsRegion+'/'+@awsService+'/aws4_request'

    DECLARE @x_amz_content_sha256 VARCHAR(MAX);
    DECLARE @hexbin VARBINARY(max) =  HASHBYTES('SHA2_256 ',@content);   

    SET @x_amz_content_sha256 = LOWER(CONVERT([varchar](MAX), @hexbin,2))
    
    -- CANONICAL REQUEST
        DECLARE @CanonicalRequest VARCHAR(MAX) = '';
        -- HTTP verb
        SET @CanonicalRequest += 'POST' + CHAR(10) 
        -- URL
        SET @CanonicalRequest += @CanonicalRequestURI + CHAR(10) 

        -- QUERYSTRING (must be sorted alphbetically)
        SET @CanonicalRequest += '' + CHAR(10) 

        -- HEADERS (must be sorted alphbetically)
        SET @CanonicalRequest += 'content-type:' + @contentType + CHAR(10) 
        SET @CanonicalRequest += 'host:' + @host + CHAR(10) 
        SET @CanonicalRequest += 'x-amz-content-sha256:' + @x_amz_content_sha256 + CHAR(10) 
        SET @CanonicalRequest += 'x-amz-date:' + @timeStamp + CHAR(10) 
        SET @CanonicalRequest += 'x-amz-target:' + @xAmzTarget + CHAR(10) 
        SET @CanonicalRequest +=  CHAR(10)  

        -- SIGNED HEADERS
        DECLARE @signedheaders VARCHAR(MAX) = 'content-type;host;x-amz-content-sha256;x-amz-date;x-amz-target'
        SET @CanonicalRequest += @signedheaders + CHAR(10) 

        -- HASHED PAYLOAD
        SET @CanonicalRequest += @x_amz_content_sha256

        DECLARE @CanonicalRequestHexbin VARBINARY(max) = HASHBYTES('SHA2_256 ',@CanonicalRequest);  

    -- STRING TO SIGN
        DECLARE @stringToSign VARCHAR(MAX) = '';
        SET @stringToSign += 'AWS4-HMAC-SHA256' + CHAR(10) 
        SET @stringToSign += @timeStamp + CHAR(10) 
        SET @stringToSign += @scope + CHAR(10) 
        SET @stringToSign += LOWER(CONVERT([varchar](MAX), @CanonicalRequestHexbin,2))

    -- CALCULATE SIGNATURE
        DECLARE @DateKey VARBINARY(MAX); 
        DECLARE @DateRegionKey VARBINARY(MAX);
        DECLARE @DateRegionServiceKey VARBINARY(MAX);
        DECLARE @SigningKey VARBINARY(MAX);
        DECLARE @Signature VARBINARY(MAX);

        SET @DateKey                = dbo.HMAC('SHA2_256',CONVERT(VARBINARY(MAX), 'AWS4'+@awsSecretKey),CONVERT(VARBINARY(MAX),@date))
        SET @DateRegionKey          = dbo.HMAC('SHA2_256', @DateKey,                CONVERT(VARBINARY(MAX),@awsRegion))
        SET @DateRegionServiceKey   = dbo.HMAC('SHA2_256', @DateRegionKey,          CONVERT(VARBINARY(MAX),@awsService))
        SET @SigningKey             = dbo.HMAC('SHA2_256', @DateRegionServiceKey,   CONVERT(VARBINARY(MAX),'aws4_request'))

        SET @Signature = dbo.HMAC('SHA2_256',@SigningKey,CONVERT(VARBINARY(MAX),@stringToSign));

    --BUILD Authorization
    DECLARE @AuthValue VARCHAR(MAX) = '';
    SET @AuthValue += 'AWS4-HMAC-SHA256 Credential=' + @awsAccessKey + '/'+ @scope 
    SET @AuthValue += ',SignedHeaders=' + @signedheaders 
    SET @AuthValue += ',Signature=' + LOWER(CONVERT([varchar](MAX), @Signature,2))

    RETURN @AuthValue
    
END