如何使用 libssl 对文件进行签名?

发布于 2024-12-10 13:12:10 字数 3555 浏览 0 评论 0原文

我想使用 OpenSSL 为文件创建签名。 我相信我的结果应该与 openssl 生成的 testfile.sig 相匹配:

$ openssl dgst -sha1 -binary < testfile > testfile.sha1
$ openssl rsautl -sign -in testfile.sha1 -inkey rsa_private.pem -keyform PEM -out testfile.sig

$ openssl rsautl -verify -inkey rsa_public.pem -pubin -in testfile.sig -hexdump
Loading 'screen' into random state - done
0000 - 9f 23 88 15 2b af d6 a6-9f 4f 1f 7e ee b0 90 dd   .#..+....O.~....
0010 - 69 99 2b 3d                                       i.+=

因此,我使用了此处发布的 caf 代码: 如何读取密钥文件以与 HMAC_Init_ex 一起使用( ) 只是添加了一小块来将 EVP_SignFinal() 之后的数据保存到文件中,所以我 可以用 openssl 和公钥验证它:

FILE * fp = fopen("out.sig", "wb");
if (fp) {
    printf("Writing the signature in a file. # of bytes: %u.\n", sig_len);
    fwrite(sig, 1, sig_len, fp);
    fclose(fp);
}

令我惊讶的是,我得到了这个:

$ openssl rsautl -verify -inkey rsa_public.pem -pubin -in out.sig -hexdump
Loading 'screen' into random state - done
0000 - 30 21 30 09 06 05 2b 0e-03 02 1a 05 00 04 14 9f   0!0...+.........
0010 - 23 88 15 2b af d6 a6 9f-4f 1f 7e ee b0 90 dd 69   #..+....O.~....i
0020 - 99 2b 3d                                          .+=

现在,如果您观察 out.sig 验证的输出,您会看到最后 20 bytes 匹配 testfile.sig 验证的数据输出。这 20 个字节是 确实是我文件的 SHA-1 哈希值。但开头的前 15 个字节是什么? EVP_SignInit() / EVP_SignUpdate() / EVP_SignFinal() 不应该只将哈希值放入 消息,还是我遗漏了一些明显的东西?

如果您需要更多信息,请告诉我。 感谢

用于签署文件的完整功能:

int do_evp_sign(FILE * rsa_pkey_file, FILE * in_file)
{
    OpenSSL_add_all_digests();
    ERR_load_crypto_strings();

    EVP_PKEY * pkey = PEM_read_PrivateKey(rsa_pkey_file, NULL, NULL, NULL);
    if (pkey == NULL) {
        ERR_print_errors_fp(stderr);
        return 1;
    }

    fseek(in_file, 0, SEEK_END);
    const size_t lSize = ftell(in_file);
    rewind(in_file);

    EVP_MD_CTX md_ctx;
    EVP_SignInit(&md_ctx, EVP_sha1());

    size_t len;
    unsigned char buffer[4096];

    size_t bytesLeft = lSize;
    while (bytesLeft > 0) {
        const size_t count = (bytesLeft > sizeof(buffer) ? sizeof(buffer) : bytesLeft);
        len = fread(buffer, 1, count, in_file);
        if (len != count) {
            fprintf(stderr, "Read error! len (%u) != count (%u).\n", len, count);
            EVP_PKEY_free(pkey);
            return 1;
        }
        if (!EVP_SignUpdate(&md_ctx, buffer, len))
        {
            ERR_print_errors_fp(stderr);
            EVP_PKEY_free(pkey);
            return 1;
        }
        bytesLeft -= len;
    }

    unsigned int sig_len;
    unsigned char * sig = (unsigned char *)malloc(EVP_PKEY_size(pkey));

    if (!sig) {
        fprintf(stderr, "Couldn't allocate %u bytes of memory.\n", EVP_PKEY_size(pkey));
        EVP_PKEY_free(pkey);
        return 1;
    }

    if (!EVP_SignFinal(&md_ctx, sig, &sig_len, pkey))
    {
        ERR_print_errors_fp(stderr);
        EVP_PKEY_free(pkey);
        free(sig);
        return 1;
    }

    FILE * fTemp = fopen("out.sig", "wb");
    if (fTemp) {
        printf("Writing the signature to a file. Number of bytes: %u.\n", sig_len);
        fwrite(sig, 1, sig_len, fTemp);
        fclose(fTemp);
    }

    printf("Signature: \n");
    for (unsigned int i = 0; i != sig_len; ++i)
    {
        printf("%02x", sig[i]);
        if (i % 16 == 15)
            printf("\n");
    }
    printf("\n");

    EVP_PKEY_free(pkey);
    free(sig);

    return 0;
}

I want to create a signature for a file using OpenSSL.
I believe that my result should match the testfile.sig generated by openssl this way:

$ openssl dgst -sha1 -binary < testfile > testfile.sha1
$ openssl rsautl -sign -in testfile.sha1 -inkey rsa_private.pem -keyform PEM -out testfile.sig

$ openssl rsautl -verify -inkey rsa_public.pem -pubin -in testfile.sig -hexdump
Loading 'screen' into random state - done
0000 - 9f 23 88 15 2b af d6 a6-9f 4f 1f 7e ee b0 90 dd   .#..+....O.~....
0010 - 69 99 2b 3d                                       i.+=

So, I used the code by caf that was posted here:
How to read key file for use with HMAC_Init_ex()
and just added a small piece to save the data after EVP_SignFinal() to a file, so then I
could just verify it with openssl and the public key:

FILE * fp = fopen("out.sig", "wb");
if (fp) {
    printf("Writing the signature in a file. # of bytes: %u.\n", sig_len);
    fwrite(sig, 1, sig_len, fp);
    fclose(fp);
}

And to my suprise, I got this:

$ openssl rsautl -verify -inkey rsa_public.pem -pubin -in out.sig -hexdump
Loading 'screen' into random state - done
0000 - 30 21 30 09 06 05 2b 0e-03 02 1a 05 00 04 14 9f   0!0...+.........
0010 - 23 88 15 2b af d6 a6 9f-4f 1f 7e ee b0 90 dd 69   #..+....O.~....i
0020 - 99 2b 3d                                          .+=

Now, if you observe the output of the verification of out.sig, you'll see that the last 20
bytes match the data output of the verification of testfile.sig. And those 20 bytes are
indeed the SHA-1 hash of my file. But what are the first 15 bytes at the beginning?
Isn't EVP_SignInit() / EVP_SignUpdate() / EVP_SignFinal() supposed to put only the hash in
the message, or am I missing something obvious?

If you need more info, please let me know.
Thanks

Full function that was uset to sign the file:

int do_evp_sign(FILE * rsa_pkey_file, FILE * in_file)
{
    OpenSSL_add_all_digests();
    ERR_load_crypto_strings();

    EVP_PKEY * pkey = PEM_read_PrivateKey(rsa_pkey_file, NULL, NULL, NULL);
    if (pkey == NULL) {
        ERR_print_errors_fp(stderr);
        return 1;
    }

    fseek(in_file, 0, SEEK_END);
    const size_t lSize = ftell(in_file);
    rewind(in_file);

    EVP_MD_CTX md_ctx;
    EVP_SignInit(&md_ctx, EVP_sha1());

    size_t len;
    unsigned char buffer[4096];

    size_t bytesLeft = lSize;
    while (bytesLeft > 0) {
        const size_t count = (bytesLeft > sizeof(buffer) ? sizeof(buffer) : bytesLeft);
        len = fread(buffer, 1, count, in_file);
        if (len != count) {
            fprintf(stderr, "Read error! len (%u) != count (%u).\n", len, count);
            EVP_PKEY_free(pkey);
            return 1;
        }
        if (!EVP_SignUpdate(&md_ctx, buffer, len))
        {
            ERR_print_errors_fp(stderr);
            EVP_PKEY_free(pkey);
            return 1;
        }
        bytesLeft -= len;
    }

    unsigned int sig_len;
    unsigned char * sig = (unsigned char *)malloc(EVP_PKEY_size(pkey));

    if (!sig) {
        fprintf(stderr, "Couldn't allocate %u bytes of memory.\n", EVP_PKEY_size(pkey));
        EVP_PKEY_free(pkey);
        return 1;
    }

    if (!EVP_SignFinal(&md_ctx, sig, &sig_len, pkey))
    {
        ERR_print_errors_fp(stderr);
        EVP_PKEY_free(pkey);
        free(sig);
        return 1;
    }

    FILE * fTemp = fopen("out.sig", "wb");
    if (fTemp) {
        printf("Writing the signature to a file. Number of bytes: %u.\n", sig_len);
        fwrite(sig, 1, sig_len, fTemp);
        fclose(fTemp);
    }

    printf("Signature: \n");
    for (unsigned int i = 0; i != sig_len; ++i)
    {
        printf("%02x", sig[i]);
        if (i % 16 == 15)
            printf("\n");
    }
    printf("\n");

    EVP_PKEY_free(pkey);
    free(sig);

    return 0;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

缺⑴份安定 2024-12-17 13:12:10

您拥有的是哈希值的 ASN.1 编码。

0000 - 30 21 30 09 06 05 2b 0e-03 02 1a 05 00 04 14 9f   0!0...+.........
0010 - 23 88 15 2b af d6 a6 9f-4f 1f 7e ee b0 90 dd 69   #..+....O.~....i
0020 - 99 2b 3d                                          .+=

30 - SEQUENCE
21 - length (33 bytes)
 30 - SEQUENCE
 09 - length (9 bytes)
  06 - OID
  05 - length (5 bytes)
   2b 0e 03 02 1a = 1 2 14 3 2 26 = SHA-1
  05 - NULL
  00 - length (0 bytes)
 04 - Octet String
 14 - length (20 bytes)
  9f ... 3d - contents (the digest)

您到底是如何生成摘要的(以代码形式)?看起来您使用的任何内容都将其转换为 ASN.1,而不是保留“原始”,这就是您签名的内容。

What you have there is an ASN.1 encoding of the hash value.

0000 - 30 21 30 09 06 05 2b 0e-03 02 1a 05 00 04 14 9f   0!0...+.........
0010 - 23 88 15 2b af d6 a6 9f-4f 1f 7e ee b0 90 dd 69   #..+....O.~....i
0020 - 99 2b 3d                                          .+=

30 - SEQUENCE
21 - length (33 bytes)
 30 - SEQUENCE
 09 - length (9 bytes)
  06 - OID
  05 - length (5 bytes)
   2b 0e 03 02 1a = 1 2 14 3 2 26 = SHA-1
  05 - NULL
  00 - length (0 bytes)
 04 - Octet String
 14 - length (20 bytes)
  9f ... 3d - contents (the digest)

How exactly did you generate the digest (in code)? It looks like whatever you used converted it to ASN.1 instead of leaving it "raw", and that's what you signed.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文