在与.net不同的节点中创建的安全令牌

时间:2015-11-03 15:59:24

标签: node.js encryption aes recaptcha

我试图在nodejs中实现reCaptcha Secure Tokens

查看Java.NET中的示例,并为节点创建了此版本:

exports.getSecureToken = function() {
  var algorithm = 'aes-128-ecb';
  var tokenObj = { session_id: 'ab0069ec-3c2c-436c-868b-43c7a10db229'/*uuid.v4()*/, ts_ms: 1446560931992/*(new Date()).getTime()*/ };
  var text = JSON.stringify(tokenObj);

  var shaHash = new Buffer(crypto.createHash('sha1').update('6LeyNOTTVALIDH2RLNaivqrrpm2zh56Y3uHqOjFO'/*config.reCAPTCHASecret*/).digest('hex'), 'hex');

  var key = shaHash.slice(0, 16);

  var cipher = crypto.createCipher(algorithm, key, key);

  var encryptedToken = cipher.update(text, 'utf8', 'base64') + cipher.final('base64');

  var result = encryptedToken.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');

  return result;
};

问题是在.NET中我获得了一个有效的令牌(使用了帖子中没有包含的右键),但是在节点中我得到了相同输入的不同令牌,但它不起作用:< / p>

.NET - LhPTUELia5vc0X6aDGDtqpsbmB7oqm6vUnzk5BL2auactYXRU5TEUzML8gZ_JubXG07rvJxk1Sb5_a-wqVUGEf_UuO1gGi-WO83yJHOxnjI

节点 - EGr7drd1JEylwzLGakZ6dpPRSf2nFdpzHOrJlLZlyHYmVRj5obAw7WjPt4W5l0vsywNEqCQ-2_d7qIZOMiOedianfBrQPOBaOmmq44IOB8Q

我在加密之前看到密钥和输入是相同的(在.NET和节点中),所以问题必须(?)是密码,任何线索?

.NET代码供参考:

public static void Main(string[] args)
{
    //Your code goes here
    Console.WriteLine(EncryptJsonToken(GetJsonToken()));
}

public static string GetJsonToken()
{
  //Example: {"session_id": e6e9c56e-a7da-43b8-89fa-8e668cc0b86f,"ts_ms":1421774317718}
  string jsonRequest = "{" + string.Format("\"session_id\": {0},\"ts_ms\":{1}", "ab0069ec-3c2c-436c-868b-43c7a10db229", 1446560931992) + "}";
  return jsonRequest;
}

public static byte[] getKey()
{
  string secretKey = "6LeyNOTTVALIDH2RLNaivqrrpm2zh56Y3uHqOjFO";
  SHA1 sha = SHA1.Create();
  byte[] dataToHash = Encoding.UTF8.GetBytes(secretKey);
  byte[] shaHash = sha.ComputeHash(dataToHash);
  byte[] first16OfHash = new byte[16];
  Array.Copy(shaHash, first16OfHash, 16);
  return first16OfHash;
}

public static byte[] EncryptStringToBytes_Aes(string plainText, byte[] Key, byte[] IV)
{
  // Check arguments. 
  if (plainText == null || plainText.Length <= 0)
    throw new ArgumentNullException("plainText");
  if (Key == null || Key.Length <= 0)
    throw new ArgumentNullException("Key");
  if (IV == null || IV.Length <= 0)
    throw new ArgumentNullException("IV");
  byte[] encrypted;
  // Create an AesManaged object 
  // with the specified key and IV. 
  using (AesManaged aesAlg = new AesManaged())
  {
    aesAlg.Key = Key;
    aesAlg.IV = IV;
    aesAlg.Padding = PaddingMode.PKCS7;
    aesAlg.Mode = CipherMode.ECB;

    // Create a decrytor to perform the stream transform.
    ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

    // Create the streams used for encryption. 
    using (MemoryStream msEncrypt = new MemoryStream())
    {
      using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
      {
        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
        {

          //Write all data to the stream.
          swEncrypt.Write(plainText);
        }
        encrypted = msEncrypt.ToArray();
      }
    }
  }
  // Return the encrypted bytes from the memory stream. 
  return encrypted;
}

public static string EncryptJsonToken(string jsonToken)
{
  byte[] encrypted = EncryptStringToBytes_Aes(jsonToken, getKey(), getKey());

  //Base64 encode the encrypted data
  //Also applys the URL variant of base64 encoding, unfortunately the HttpServerUtility.UrlTokenEncode(encrypted) seems to truncate the last value from the string so we can't use it?
  return Convert.ToBase64String(encrypted, Base64FormattingOptions.None).Replace("=", String.Empty).Replace('+', '-').Replace('/', '_');
}

在.NET中进行调试:DEMO

1 个答案:

答案 0 :(得分:3)

你有两个问题:

  • 您正在使用JSON.stringify()生成有效的JSON字符串,但C#代码中的GetJsonToken()方法不会生成有效的JSON字符串。 UUID缺少",由于某种原因,session_id键与其值之间存在空格。您必须在JavaScript中反映这些差异:

    var uuidToken = "ab0069ec-3c2c-436c-868b-43c7a10db229";
    var time = 1446560931992;
    var text = "{\"session_id\": "+uuidToken+",\"ts_ms\":"+time+"}";
    
  • 没有这样的功能crypto.createCipher(algorithm, key, key)。但是crypto.createCipheriv(algorithm, key, iv)。如果有密码而不是您没有的密钥,则可以使用createCipher(algorithm, password)。由于ECB模式没有IV,您可以传入一个空的(二进制)字符串作为IV。

完整代码:

var crypto = require("crypto");

var algorithm = 'aes-128-ecb';

var uuidToken = "ab0069ec-3c2c-436c-868b-43c7a10db229";
var time = 1446560931992;
var text = "{\"session_id\": "+uuidToken+",\"ts_ms\":"+time+"}";
console.log("Token: " + text);

var shaHash = crypto.createHash('sha1').update('6LeyNOTTVALIDH2RLNaivqrrpm2zh56Y3uHqOjFO').digest();
var key = shaHash.slice(0, 16);

var cipher = crypto.createCipheriv(algorithm, key, "");
var encryptedToken = cipher.update(text, 'utf8', 'base64') + cipher.final('base64');

var result = encryptedToken.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');

console.log("result:   " + result);
console.log("expected: LhPTUELia5vc0X6aDGDtqpsbmB7oqm6vUnzk5BL2auactYXRU5TEUzML8gZ_JubXG07rvJxk1Sb5_a-wqVUGEf_UuO1gGi-WO83yJHOxnjI");

输出:

Token: {"session_id": ab0069ec-3c2c-436c-868b-43c7a10db229,"ts_ms":1446560931992}
result:   LhPTUELia5vc0X6aDGDtqpsbmB7oqm6vUnzk5BL2auactYXRU5TEUzML8gZ_JubXG07rvJxk1Sb5_a-wqVUGEf_UuO1gGi-WO83yJHOxnjI
expected: LhPTUELia5vc0X6aDGDtqpsbmB7oqm6vUnzk5BL2auactYXRU5TEUzML8gZ_JubXG07rvJxk1Sb5_a-wqVUGEf_UuO1gGi-WO83yJHOxnjI