OpenSSL и MS CryptoAPI: разные цифровые подписи - PullRequest
5 голосов
/ 20 декабря 2011

Я сгенерировал сертификат X509 с закрытым ключом с помощью утилиты makecert

makecert -n "CN=RootCATest" -r -sv RootCATest.pvk RootCATest.cer 
makecert -sk MyKeyName -iv RootCATest.pvk -n "CN=tempCert" -ic RootCATest.cer -sr currentuser -ss my -sky signature —pe 

Затем я преобразовал RootCATest.pvk в RootCATest.pem с помощью OpenSSL.И я извлек открытый ключ: pubRootCATest.pem

У меня есть небольшой файл с именем «msg».И я подписываю этот файл с помощью SHA1.

openssl dgst -sha1 -sign c:\RootCATest.pem -out c:\openssl c:\msg

Затем я хочу получить ту же цифровую подпись с использованием MS CryptoAPI.

Вот мой код (Примечание: это код для понимания концепцийпоэтому я не освобождаю выделенную память)

void SwapBytes(BYTE *pv, int n)
{
    BYTE *p = pv;
    int lo, hi;
    for(lo=0, hi=n-1; hi>lo; lo++, hi--)
    {
        BYTE tmp=p[lo];
        p[lo] = p[hi];
        p[hi] = tmp;
    }
}

void sign()
{
    FILE *file;
    BYTE *msg;
    int msg_size;

    HCRYPTPROV hProv;
    HCERTSTORE hStore;
    PCCERT_CONTEXT pCert;
    DWORD dwKeySpec;
    BOOL fCallerFreeProv;
    BYTE  *pSignature;
    DWORD sigLen;

    // Read message bytes from file
    file = fopen("c:\\msg", "r");
    fseek(file, 0, SEEK_END);
    msg_size = ftell(file);
    fseek(file, 0, SEEK_SET);
    msg = new BYTE[msg_size];
    fread(msg, sizeof(BYTE), msg_size, file);
    fclose(file);

    hStore = CertOpenSystemStore(NULL, "My");
    pCert = CryptUIDlgSelectCertificateFromStore(hStore, NULL, NULL, NULL, 0, 0, NULL);
    CryptAcquireCertificatePrivateKey(pCert, CRYPT_ACQUIRE_COMPARE_KEY_FLAG, NULL, &hProv, &dwKeySpec, &fCallerFreeProv);
    PrintCryptoProviderName(hProv); // prints Microsoft Strong Cryptographic Provider

    ALG_ID hashAlgId = CALG_SHA1;
    HCRYPTHASH hHash;
    CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash);
    CryptHashData(hHash, msg, msg_size, 0);

    CryptSignHash(hHash, dwKeySpec, NULL, 0, NULL, &sigLen);
    pSignature = new BYTE[sigLen];
    CryptSignHash(hHash, dwKeySpec, NULL, CRYPT_NOHASHOID, pSignature, &sigLen);

    SwapBytes(pSignature, sigLen); // Here i reverse byte order as I read that MS CryptoAPI uses reversed byte order

    // Write signature bytes to file
    file = fopen("c:\\CryptSignHash", "w");
    fwrite(pSignature, sizeof(BYTE), sigLen, file);
    fclose(file);
}

В качестве вывода я получаю подпись, совершенно отличную от подписи, сделанной OpenSSL.Как я могу получить такую ​​же подпись?

Как я считаю, есть некоторые моменты, на которые следует обратить внимание:

  • Мой размер msg_size равен размеру файла.Так что это число байтов для подписи.На некоторых сайтах я видел рекомендации по добавлению нулевого байта в байтовый массив.Мне действительно нужно в таком случае?
  • Флаг CRYPT_NOHASHOID.Без этого я получаю подпись размером 130 байт, когда подпись, сделанная OpenSSL, составляет 128 байт.Поэтому я думаю, что CRYPT_NOHASHOID должен быть там.
  • SwapBytes (...) Я пробовал с этим и без него.И в обоих случаях у меня есть подписи, абсолютно отличающиеся от подписи OpenSSL.

1 Ответ

0 голосов
/ 24 декабря 2011

Как мне получить такую ​​же подпись?

Большинство алгоритмов цифровой подписи, включая RSA, который, я полагаю, вы использовали здесь, являются недетерминированными. Попробуйте дважды подписать один и тот же файл одной и той же программой, и вы получите разные результаты.

Это означает, что выполнение одного и того же алгоритма дважды с одним и тем же вводом даст вам разные подписи. Это не проблема, если алгоритму проверки все еще удается принять все подписи, сгенерированные алгоритмом подписи (с подходящим ключом).

Этот недетерминизм часто действительно необходим для безопасности схемы подписи.

Чтобы проверить, действительно ли два ваших алгоритма подписи совместимы, попробуйте проверить подпись OpenSSL с помощью MS Crypto API и проверить подпись MS Crypto с помощью OpenSSL. (Затем измените файл на один байт и убедитесь, что он больше не проверяется.)

...