【问题标题】:What key is used by openssl (command) for HMAC if key is not passed in as argument如果未将密钥作为参数传入,则 openssl(命令)对 HMAC 使用什么密钥
【发布时间】:2014-08-25 19:11:33
【问题描述】:

我正在使用 Java 编写一个程序,以使用 javax.crypto.Mac 为给定的字符串生成 HMAC。

但是为了排查和实验,我使用openssl dgst生成HAMC(使用SHA256)。我观察到即使没有传递密钥,该命令也会成功执行。例如,

在 shell 上使用 openssl 生成 HMAC 的标准方法,

echo -n "data" | opnessl dgst -sha256 -hmac "KEY1"

但是我也可以执行以下操作,

echo -n "data" | opnessl dgst -sha256 -hmac

两个命令都成功运行,输出不同。

我假设 openssl 可以将不存在的 key 视为 '\0' (null) key。

实际上我的问题是我无法使用 javax.crypto.Mac 生成类似的结果并为 SecretKey 提供 byte[0] = 0; (以 0 为值的单字节键)。

【问题讨论】:

  • 在文档中提交了错误报告。请参阅 OpenSSL 的 RT 中的 Bug 3504。如果需要,用户名和密码是“guest”。

标签: java openssl hmac


【解决方案1】:

编辑:根据 OpenSSSL 邮件列表上的 Rich Salz 所述,-hmac 选项的密钥不是可选的。预计openssl dgst 命令的行为将来会发生变化。

OpenSSL 1.0.2 或 1.1.0 可能会发生更改。它可能会向后移植到 1.0.1 等较低版本,但不能保证。


实际上我的问题是我无法使用 javax.crypto.Mac 生成类似的结果并为 SecretKey 提供 byte[0] = 0;

只需使用 SHA-256 哈希,而不是 HMAC。如果这是 TLDR;然后跳到最后;)

感兴趣的源文件是<openssl src>/apps/dgst.c。第 225 行是(或应该)设置键的位置,但这不是因为没有以下参数:

else if (!strcmp(*argv,"-hmac"))
{
    if (--argc < 1)
        break;
    hmac_key=*++argv;
}

如果未将密钥作为参数传入,则 openssl(命令)对 HMAC 使用什么密钥

要回答这个问题,我们需要查看&lt;openssl src&gt;/apps/dgst.c中的源代码。

-hamc 开关填充变量hmac_key,但它的NULL 因此被跳过。另一个感兴趣的变量是sigkey,但它也是NULL,所以它也被跳过了。

第 513 行左右是有趣的地方。在dgst.c(来自lldb)中,几乎所有其他内容都被跳过了:

   513      if (argc == 0)
   514      {
-> 515          BIO_set_fp(in,stdin,BIO_NOCLOSE);
   516          err=do_fp(out, buf,inp,separator, out_bin, sigkey, sigbuf,
   517                    siglen,NULL,NULL,"stdin",bmd);
   518      }
(lldb) p sigkey
(EVP_PKEY *) $25 = 0x0000000000000000

所以我们需要做的就是查看do_fp,如下所示。感兴趣的行是 627,执行此操作的位置:

len=BIO_gets(bp,(char *)buf,BUFSIZE);

BIO bp 是一个链,它把你的哈希链接到它上面。因此,无论您放入其中的任何内容都会被散列。并且哈希没有键控,因为 hmac_keysigkeyNULL

在第 654 行,遇到了这种情况:

BIO_printf(out, "(%s)= ", file);

在第 655 - 660 行,遇到了执行十六进制编码的循环。

结合起来,它们会产生类似的东西:

(stdin)= 6667b2d1aab6a00caa5aee5af8ad9f1465e567abf1c209d15727d57b3e8f6e5f

int do_fp(BIO *out, unsigned char *buf, BIO *bp, int sep, int binout,
          EVP_PKEY *key, unsigned char *sigin, int siglen,
          const char *sig_name, const char *md_name,
          const char *file,BIO *bmd)
{
    size_t len;
    int i;

    for (;;)
    {
        i=BIO_read(bp,(char *)buf,BUFSIZE);
        if(i < 0)
        {
            BIO_printf(bio_err, "Read Error in %s\n",file);
            ERR_print_errors(bio_err);
            return 1;
        }
        if (i == 0) break;
    }
    if(sigin)
    {
        EVP_MD_CTX *ctx;
        BIO_get_md_ctx(bp, &ctx);
        i = EVP_DigestVerifyFinal(ctx, sigin, (unsigned int)siglen);
        if(i > 0)
            BIO_printf(out, "Verified OK\n");
        else if(i == 0)
        {
            BIO_printf(out, "Verification Failure\n");
            return 1;
        }
        else
        {
            BIO_printf(bio_err, "Error Verifying Data\n");
            ERR_print_errors(bio_err);
            return 1;
        }
        return 0;
    }
    if(key)
    {
        EVP_MD_CTX *ctx;
        BIO_get_md_ctx(bp, &ctx);
        len = BUFSIZE;
        if(!EVP_DigestSignFinal(ctx, buf, &len))
        {
            BIO_printf(bio_err, "Error Signing Data\n");
            ERR_print_errors(bio_err);
            return 1;
        }
    }
    else
    {
        len=BIO_gets(bp,(char *)buf,BUFSIZE);
        if ((int)len <0)
        {
            ERR_print_errors(bio_err);
            return 1;
        }
    }

    if(binout) BIO_write(out, buf, len);
    else if (sep == 2)
    {
        for (i=0; i<(int)len; i++)
            BIO_printf(out, "%02x",buf[i]);
        BIO_printf(out, " *%s\n", file);
    }
    else
    {
        if (sig_name)
        {
            BIO_puts(out, sig_name);
            if (md_name)
                BIO_printf(out, "-%s", md_name);
            BIO_printf(out, "(%s)= ", file);
        }
        else if (md_name)
            BIO_printf(out, "%s(%s)= ", md_name, file);
        else
            BIO_printf(out, "(%s)= ", file);
        for (i=0; i<(int)len; i++)
        {
            if (sep && (i != 0))
                BIO_printf(out, ":");
            BIO_printf(out, "%02x",buf[i]);
        }
        BIO_printf(out, "\n");
    }
    return 0;
}

如果您想自己执行步骤,请下载并解压 OpenSSL 源代码。然后,使用调试版本进行配置:

cd openssl-1.0.1h
./Configure debug-darwin64-x86_64-cc no-ssl2 enable-ec_nistp_64_gcc_128
make depend && make

在调试器下打开它:

cd openssl-1.0.1h
lldb apps/openssl

最后运行它:

(lldb) b dgst.c:513
(lldb) r dgst -sha256 -hmac

调试器应该靠近do_fp:

Process 27371 stopped
* thread #1: tid = 0x286a3, 0x000000010000ba99 openssl`dgst_main(argc=0, argv=0x00007fff5fbffa60) + 5961 at dgst.c:513, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x000000010000ba99 openssl`dgst_main(argc=0, argv=0x00007fff5fbffa60) + 5961 at dgst.c:513
   512      
-> 513      if (argc == 0)
   514      {
   515          BIO_set_fp(in,stdin,BIO_NOCLOSE);
   516          err=do_fp(out, buf,inp,separator, out_bin, sigkey, sigbuf,
(lldb) 

诀窍是:当您输入do_fp 时,程序将从stdin 开始读取。输入您的字符串(即data),然后执行 CTRL+D 对其进行 EOF。


现在,毕竟,这是简短的答案:)

$ echo -n "data" | openssl dgst -sha256 -hmac
(stdin)= 3a6eb0790f39ac87c94f3856b2dd2c5d110e6811602261a9a923d3bb23adc8b7
$ echo -n "data" | openssl dgst -sha256 
(stdin)= 3a6eb0790f39ac87c94f3856b2dd2c5d110e6811602261a9a923d3bb23adc8b7

【讨论】:

  • 希望我能投票两次。谢谢@jww,你真的解决了这个问题。
猜你喜欢
  • 2021-01-20
  • 1970-01-01
  • 2020-03-15
  • 1970-01-01
  • 2011-05-16
  • 1970-01-01
  • 2020-09-09
  • 2019-12-20
  • 2019-08-09
相关资源
最近更新 更多