无法使用HMAC在Ruby中生成正确的加密密钥

无法使用HMAC在Ruby中生成正确的加密密钥,ruby,azure,hmac,sha256,azure-cosmosdb,Ruby,Azure,Hmac,Sha256,Azure Cosmosdb,我试图按照文档和询问代码进行操作,但我无法这样做 我收到以下错误:401 Unauthorized:{“code”:“Unauthorized”,“message”:“输入授权令牌无法为请求提供服务。请检查预期的有效负载是否按照协议生成,并检查正在使用的密钥。服务器使用以下有效负载进行签名:'post\ndbs\n\n13 april 2015 18:21:05 gmt\n\n'\r\nActivityId: 我的ruby代码如下所示: require 'openssl' require 're

我试图按照文档和询问代码进行操作,但我无法这样做

我收到以下错误:401 Unauthorized:{“code”:“Unauthorized”,“message”:“输入授权令牌无法为请求提供服务。请检查预期的有效负载是否按照协议生成,并检查正在使用的密钥。服务器使用以下有效负载进行签名:'post\ndbs\n\n13 april 2015 18:21:05 gmt\n\n'\r\nActivityId:

我的ruby代码如下所示:

require 'openssl'
require 'rest-client'
require 'base64'
require 'uri'
require 'json'
require 'time'

def get_databases url, master_key
  time = Time.now.httpdate
  authorization = get_master_auth_token "get", "", "dbs", time, master_key
  header = { "authorization" => authorization, "x-ms-date" => time, "x-ms-version" => "2015-04-08" }
  RestClient.get url, header
end

def get_master_auth_token verb, resource_id, resource_type, date, master_key
  digest = OpenSSL::Digest::SHA256.new
  key = Base64.decode64 master_key
  text = verb + "\n" +
    resource_type + "\n" +
    resource_id + "\n" +
    date + "\n" +
    "\n"
  hmac = OpenSSL::HMAC.digest digest, key, text.downcase
  auth_string = "type=" + "master" + "&ver=" + "1.0" + "&sig=" + hmac
  URI.escape auth_string
end
var crypto = require("crypto")

function encode_message(masterKey, message) {
    var key = new Buffer(masterKey, "base64"); // encode/decode? base64 the masterKey
    var body = new Buffer(message.toLowerCase(), "utf8"); // convert message to "utf8" and lower case
    return crypto.createHmac("sha256", key).update(body).digest("base64"); // encrypt the message using key
 }
require 'base64'
require 'openssl'

def encode_message master_key, message
  key = Base64.urlsafe_decode64 master_key
  hmac = OpenSSL::HMAC.digest 'sha256', key, message
  Base64.urlsafe_encode64 hmac
end
2.2.1 :021 > encode_message("blah", "get\ncolls\n\nTue, 14 Apr 2015 13:34:22 GMT\n\n")
 => "N6BL3n4eSvYA8dIL1KzlTIvR3TcYpdqW2UNPtKWrjP8="
谢谢

编辑:根据Ryan的建议和示例,我将代码简化为以下snippit,这应该与他发布的节点代码相匹配,但在ruby中仍然失败:

def hard_coded_get_databases master_key, url
  verb = "get"
  resource_type = "dbs"
  resource_id = ""
  date = Time.now.httpdate
  serv_version = '2014-08-21'
  master_token = "master"
  token_version = "1.0"
  key = Base64.decode64 master_key
  text = verb + "\n" + resource_type + "\n" + resource_id + "\n" + date + "\n\n"
  body = text.downcase.force_encoding "utf-8"
  signature = OpenSSL::HMAC.digest OpenSSL::Digest::SHA256.new, key, body
  auth_token = URI.escape("type="+master_token + "&ver="+token_version + "&sig="+signature)

  header = { "accept" => "application/json", "x-ms-version" => serv_version, "x-ms-date" => date, "authorization" => auth_token }
  RestClient.get url, header
end
EDIT2:我相信我已经将问题隔离到如何进行主密钥身份验证

以Ryan为例,我们可以将他的节点代码缩减如下:

require 'openssl'
require 'rest-client'
require 'base64'
require 'uri'
require 'json'
require 'time'

def get_databases url, master_key
  time = Time.now.httpdate
  authorization = get_master_auth_token "get", "", "dbs", time, master_key
  header = { "authorization" => authorization, "x-ms-date" => time, "x-ms-version" => "2015-04-08" }
  RestClient.get url, header
end

def get_master_auth_token verb, resource_id, resource_type, date, master_key
  digest = OpenSSL::Digest::SHA256.new
  key = Base64.decode64 master_key
  text = verb + "\n" +
    resource_type + "\n" +
    resource_id + "\n" +
    date + "\n" +
    "\n"
  hmac = OpenSSL::HMAC.digest digest, key, text.downcase
  auth_string = "type=" + "master" + "&ver=" + "1.0" + "&sig=" + hmac
  URI.escape auth_string
end
var crypto = require("crypto")

function encode_message(masterKey, message) {
    var key = new Buffer(masterKey, "base64"); // encode/decode? base64 the masterKey
    var body = new Buffer(message.toLowerCase(), "utf8"); // convert message to "utf8" and lower case
    return crypto.createHmac("sha256", key).update(body).digest("base64"); // encrypt the message using key
 }
require 'base64'
require 'openssl'

def encode_message master_key, message
  key = Base64.urlsafe_decode64 master_key
  hmac = OpenSSL::HMAC.digest 'sha256', key, message
  Base64.urlsafe_encode64 hmac
end
2.2.1 :021 > encode_message("blah", "get\ncolls\n\nTue, 14 Apr 2015 13:34:22 GMT\n\n")
 => "N6BL3n4eSvYA8dIL1KzlTIvR3TcYpdqW2UNPtKWrjP8="
如果调用此节点代码,我可以生成以下键:

encode_message("blah", 'get\ncolls\n\nTue, 14 Apr 2015 13:34:22 GMT\n\n')
'IYlLuyZtVLx5ANkGMAxviDHgC/DJJXSj1gUGLvN0oM8='
如果我生成等效的ruby代码来创建身份验证,我的ruby代码如下所示:

require 'openssl'
require 'rest-client'
require 'base64'
require 'uri'
require 'json'
require 'time'

def get_databases url, master_key
  time = Time.now.httpdate
  authorization = get_master_auth_token "get", "", "dbs", time, master_key
  header = { "authorization" => authorization, "x-ms-date" => time, "x-ms-version" => "2015-04-08" }
  RestClient.get url, header
end

def get_master_auth_token verb, resource_id, resource_type, date, master_key
  digest = OpenSSL::Digest::SHA256.new
  key = Base64.decode64 master_key
  text = verb + "\n" +
    resource_type + "\n" +
    resource_id + "\n" +
    date + "\n" +
    "\n"
  hmac = OpenSSL::HMAC.digest digest, key, text.downcase
  auth_string = "type=" + "master" + "&ver=" + "1.0" + "&sig=" + hmac
  URI.escape auth_string
end
var crypto = require("crypto")

function encode_message(masterKey, message) {
    var key = new Buffer(masterKey, "base64"); // encode/decode? base64 the masterKey
    var body = new Buffer(message.toLowerCase(), "utf8"); // convert message to "utf8" and lower case
    return crypto.createHmac("sha256", key).update(body).digest("base64"); // encrypt the message using key
 }
require 'base64'
require 'openssl'

def encode_message master_key, message
  key = Base64.urlsafe_decode64 master_key
  hmac = OpenSSL::HMAC.digest 'sha256', key, message
  Base64.urlsafe_encode64 hmac
end
2.2.1 :021 > encode_message("blah", "get\ncolls\n\nTue, 14 Apr 2015 13:34:22 GMT\n\n")
 => "N6BL3n4eSvYA8dIL1KzlTIvR3TcYpdqW2UNPtKWrjP8="
如果我调用此代码,我会得到以下结果:

require 'openssl'
require 'rest-client'
require 'base64'
require 'uri'
require 'json'
require 'time'

def get_databases url, master_key
  time = Time.now.httpdate
  authorization = get_master_auth_token "get", "", "dbs", time, master_key
  header = { "authorization" => authorization, "x-ms-date" => time, "x-ms-version" => "2015-04-08" }
  RestClient.get url, header
end

def get_master_auth_token verb, resource_id, resource_type, date, master_key
  digest = OpenSSL::Digest::SHA256.new
  key = Base64.decode64 master_key
  text = verb + "\n" +
    resource_type + "\n" +
    resource_id + "\n" +
    date + "\n" +
    "\n"
  hmac = OpenSSL::HMAC.digest digest, key, text.downcase
  auth_string = "type=" + "master" + "&ver=" + "1.0" + "&sig=" + hmac
  URI.escape auth_string
end
var crypto = require("crypto")

function encode_message(masterKey, message) {
    var key = new Buffer(masterKey, "base64"); // encode/decode? base64 the masterKey
    var body = new Buffer(message.toLowerCase(), "utf8"); // convert message to "utf8" and lower case
    return crypto.createHmac("sha256", key).update(body).digest("base64"); // encrypt the message using key
 }
require 'base64'
require 'openssl'

def encode_message master_key, message
  key = Base64.urlsafe_decode64 master_key
  hmac = OpenSSL::HMAC.digest 'sha256', key, message
  Base64.urlsafe_encode64 hmac
end
2.2.1 :021 > encode_message("blah", "get\ncolls\n\nTue, 14 Apr 2015 13:34:22 GMT\n\n")
 => "N6BL3n4eSvYA8dIL1KzlTIvR3TcYpdqW2UNPtKWrjP8="

显然,这两个编码的auth令牌是不一样的。(Ryan再次感谢您的帮助。)

首先,我要为我有限的Ruby知识道歉,但让我在这里尝试帮助

在get_master_auth_token函数中,您似乎在使用密钥之前解码密钥。这是否正确?如果正确,原因是什么

下面是一个node.js示例,它使用主密钥,构建auth头值,并执行一个简单的http调用来列出数据库中的集合

var crypto = require("crypto");
var https = require("https");

https.globalAgent.options.secureProtocol = "TLSv1_client_method";

var verb = 'get'; 
var resourceType = 'dbs'; //the resource you are trying to get. dbs, colls, docs etc. 
var resourceId = ''; //the parent resource id. note: not the id, but the _rid. but for you, because you are trying to lookup list of databases there is no parent
var masterKey = '...'; //your masterkey 
var date = new Date().toUTCString();

var auth = getAuthorizationTokenUsingMasterKey(verb, resourceId, resourceType, date, masterKey);

var options = {
hostname: '...', //your hostname (without https://)
port: 443,
path: '/dbs/',
method: 'GET',
    headers: {
        accept: 'application/json',
        'x-ms-version': '2014-08-21',
        'x-ms-date': date,
        authorization: auth,
    }
};

for (var i = 0; i < 1000; i++) {
    var req = https.request(options, function (res) {
        process.stdout.write(new Date().toUTCString() + " - statusCode: " + res.statusCode + "\n");
        res.on('data', function (d) {
        }).on('error', function (e) {
        })
    });

    //console.log(req);

    req.end();
}

function getAuthorizationTokenUsingMasterKey(verb, resourceId, resourceType, date, masterKey) {
    var key = new Buffer(masterKey, "base64");

    var text = (verb || "") + "\n" + 
        (resourceType || "") + "\n" + 
        (resourceId || "") + "\n" + 
        (date || "") + "\n" + 
        ("") + "\n";

    var body = new Buffer(text.toLowerCase(), "utf8");
    var signature = crypto.createHmac("sha256", key).update(body).digest("base64");

    var MasterToken = "master";    
    var TokenVersion = "1.0";

    return encodeURIComponent("type=" + MasterToken + "&ver=" + TokenVersion + "&sig=" + signature);
}
var crypto=require(“crypto”);
var https=要求(“https”);
https.globalAgent.options.secureProtocol=“TLSv1\u client\u method”;
var动词='get';
var resourceType='dbs';//您试图获取的资源。dbs、colls、docs等。
var resourceId='';//父资源id。注意:不是id,而是_rid。但是对于您来说,因为您试图查找数据库列表,所以没有父资源id
var masterKey='…';//您的masterKey
var date=new date().toutString();
var auth=getAuthorizationTokenUsingMasterKey(动词、资源ID、资源类型、日期、masterKey);
变量选项={
主机名:“…”,//您的主机名(不带https://)
港口:443,
路径:'/dbs/',
方法:“GET”,
标题:{
接受:'application/json',
“x-ms-version”:“2014-08-21”,
“x-ms-date”:日期,
授权:auth,
}
};
对于(变量i=0;i<1000;i++){
var req=https.request(选项、函数(res){
process.stdout.write(new Date().toutString()+”-statusCode:“+res.statusCode+”\n”);
res.on('数据',功能(d){
}).on('error',函数(e){
})
});
//控制台日志(req);
请求结束();
}
函数getAuthorizationTokenUsingMasterKey(动词、resourceId、resourceType、日期、masterKey){
var key=新的缓冲区(masterKey,“base64”);
变量text=(动词| |“”)+“\n”+
(resourceType | |“”)+“\n”+
(resourceId | |“”)+“\n”+
(日期| |“”)+“\n”+
(“”)+“\n”;
var body=新缓冲区(text.toLowerCase(),“utf8”);
var signature=crypto.createHmac(“sha256”,key).update(body.digest)(“base64”);
var MasterToken=“master”;
var TokenVersion=“1.0”;
返回encodeURIComponent(“type=“+MasterToken+”&ver=“+TokenVersion+”&sig=“+signature”);
}
在您的示例中,传递给getAuthorizationTokenUsingMasterKey方法的resourceId应该是“”,resourceType应该是“dbs”

我确实注意到,在某些情况下,您必须对值进行URI编码,但我认为您已经在func的最后一行这样做了

我能在你的代码和我的代码中发现的唯一区别是,你似乎在解码主密钥,而我没有这样做


我建议您运行此节点示例,并将body&signature中的字符串值与your值进行比较。它们需要匹配。

我找到了答案。感谢Magnus Stahre…他是帮助我解决问题的人

正如我所想,这是编码,诀窍是:

def encode_message master_key, message
  key = Base64.urlsafe_decode64 master_key
  hmac = OpenSSL::HMAC.digest 'sha256', key, message.downcase
  Base64.encode64(hmac).strip
end

我过早地对代码进行了降级,我的Base64.encode64未能去掉ruby在末尾添加的换行符。

作为说明,我还引用了msdn的以下帖子:非常感谢您的帮助。我的理解是,当您这样做时:var key=new Buffer(masterkey,“Base64”)您正在解码主密钥,这不对吗?是的,字符串末尾的换行符就可以了。捕捉得好!