2013/11/07

[OpenSSL] Encrypted Digest加密摘要計算

在數位簽章的傳輸架構下,傳送方必須傳送檔案的明文以及Encrypted Digest(加密摘要)給接收方,接收方得到檔案之後,會用plaintext(明文)來計算的摘要訊息,以及用RSA公鑰來解密Encrypted Digest,進而比較兩者之間是否有不同,來確認檔案是否有被改寫過。底下為整個架構圖,UserA會把plaintext利用hash()計算出message digest,然後使用private key加密之後變成encrypted digest;接著把plaintext和encrypted digest傳送給UserB,UserB接收到之後,會把encrypted digest使用UserA的public key作解密,和接收到的plaintext代進hash()計算出來的message digest做比較,看看檔案是否有遭到竄改或者接收錯誤。




OpenSSL是怎麼來計算Encrypted Digest呢?做法是把驗證屬性(Authenticate Attributes)的部分,不包含格式前面的OID,然後把第一個byte改成0x31帶進去,利用RSA私鑰加密,就可以得到Encrypted Digest。也就是說,會影響Encrypted Digest結果的包含了contentsigning time以及message digest



以下以這篇OpenSSL數位簽章格式(SMIME,PEM and DER)的結果為範例
DER格式的authenticate attributes

A0 5D
[0] AuthenticateAttributes
30 18
06 09 2A 86 48 86 F7 0D 01 09 03
31 0B 06 09 2A 86 48 86 F7 0D 01 07 01
ContentType header
AttributeType=contentType
AttributeValue=Data
30 1C
06 09 2A 86 48 86 F7 0D 01 09 05
31 0F 17 0D 31 33 31 31 30 36 30 32 32 36 31 33 5A
SigningTime header
AttributeType=signingType
AttributeValue=2013/11/06:02:26:13Z
30 23
06 09 2A 86 48 86 F7 0D 01 09 04
31 16 04 14 D2 57 F9 23 AB 54 5D F7 0C EF DE 9E B5 D2 CA 24 9C 7F BA 71
MessageDigest header
AttributeType=messageDigest
AttributeValue=OOCTET STRING Message Digest
30 0D
06 09 2A 86 48 86 F7 0D 01 01 01
05 00
SIgestEncryptedAlgorithm header
Algorithm=RSA
Parameters=NULL
04 81 80
1B B6 55 0A 9A C9 59 2B 10 AE 2F F7 57 E8 06 EE
4D 5E 1D 98 3E 22 2C 1B 6A 55 4C 81 2D 66 D4 D0
6A C0 A9 D2 2F CD FD F0 A5 DC FD A0 AF 8F 5D 92
91 05 50 CF BF E7 14 FD 97 EA BB 44 DE 83 AF B3
84 F7 4B 1C 72 53 D5 25 06 C0 6D 91 10 CA 7B 49
36 F0 EF 17 0F 9E 2D EA 36 14 A2 51 26 90 9D 41
40 FF C6 F0 F3 2E A3 70 A4 8C C4 C3 60 DB 96 97
2E 84 F1 E0 55 8C BB 78 E1 F6 2B 68 59 9F FC 0B
OCTET STRING
EncryptedDigest
1024bits


利用以下程式碼做RSA加解密的動作。程式碼請參考

RSA.c
#include <stdio.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/objects.h>
#include <openssl/x509.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>

int main ()
{
    int err;
    int sig_len;
    unsigned char sig_buf [4096];
    static char certfile[] = "signer.pem";
    static char keyfile[]= "signer.pem";
    int data_len = 95;
    unsigned char data[95]=
     {0x31,0x5D,0x30,0x18,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x03,
    0x31,0x0B,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x07,0x01,
    0x30,0x1C,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x05,
    0x31,0x0F,0x17,0x0D,0x31,0x33,0x31,0x31,0x30,0x36,0x30,0x32,0x32,0x36,0x31,0x33,0x5A,
    0x30,0x23,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x04,
    0x31,0x16,0x04,0x14,0xD2,0x57,0xF9,0x23,0xAB,0x54,0x5D,0xF7,0x0C,0xEF,
    0xDE,0x9E,0xB5,0xD2,0xCA,0x24,0x9C,0x7F,0xBA,0x71};

    EVP_MD_CTX md_ctx;
    EVP_PKEY * pkey;
    FILE * fp;
    X509 * x509;

    int ii;

    printf("AuthenticateAttributes:\n");
    for(ii=0;ii<data_len;ii++) {
        printf("%02X ",data[ii]);
        if((ii+1)%16==0&&(ii+1)!=data_len) printf("\n");
    }
    printf(", AuthenticateAttributesLen: %d\n",data_len);

    /* Just load the crypto library error strings,
    * SSL_load_error_strings() loads the crypto AND the SSL ones */
    /* SSL_load_error_strings();*/
    ERR_load_crypto_strings();

    /* Read private key */

    fp = fopen (keyfile, "r");
    if (fp == NULL) exit (1);
    pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
    fclose (fp);

    if (pkey == NULL) {
        ERR_print_errors_fp (stderr);
        exit (1);
    }

    /* Do the signature */

    EVP_SignInit (&md_ctx, EVP_sha1());
    EVP_SignUpdate (&md_ctx, data, strlen(data));
    sig_len = sizeof(sig_buf);
    err = EVP_SignFinal (&md_ctx, sig_buf, &sig_len, pkey);

    printf("EncryptedDigest: \n");
    for(ii=0;ii<sig_len;ii++)
    {
        printf("%02X ",(unsigned int)sig_buf[ii]);
        if((ii+1)%16==0&&(ii+1)!=sig_len) printf("\n");
    }
    printf(", EncryptedDigest: %d\n",sig_len);

    if (err != 1) {
        ERR_print_errors_fp(stderr);
        exit (1);
    }

    EVP_PKEY_free (pkey);

    /* Read public key */

    fp = fopen (certfile, "r");
    if (fp == NULL) exit (1);
    x509 = PEM_read_X509(fp, NULL, NULL, NULL);
    fclose (fp);

    if (x509 == NULL) {
        ERR_print_errors_fp (stderr);
        exit (1);
    }

    /* Get public key - eay */
    pkey=X509_get_pubkey(x509);
    if (pkey == NULL) {
        ERR_print_errors_fp (stderr);         exit (1);     }

    /* Verify the signature */

    EVP_VerifyInit (&md_ctx, EVP_sha1());
    EVP_VerifyUpdate (&md_ctx, data, strlen((char*)data));
    err = EVP_VerifyFinal (&md_ctx, sig_buf, sig_len, pkey);
    EVP_PKEY_free (pkey);

    if (err != 1) {
        ERR_print_errors_fp (stderr);         exit (1);
    }
    printf ("Signature Verified Ok.\n");
    return(0);
}


執行結果


從執行結果得到的Encrypted Digest,可以看到與範例格式的加密摘要相同。驗證了Encrypted Digest是由authenticate attributecontentsigning timemessage digest加密而來。





沒有留言:

張貼留言