openssl подпись - верификация PHP - C++

230
16 марта 2019, 18:50

Стоит задача подписать строку приватным ключем на PHP и проверить подпись публичным ключем на C++. openssl сгенерил пару ключей. подписываю строку на PHP

<?php
$filename = "my_priv.key";
$handle = fopen($filename, "r");
$contents = fread($handle, filesize($filename));
fclose($handle);

$pkeyid = openssl_pkey_get_private($contents);
    openssl_sign("my test line", $signature, $pkeyid, OPENSSL_ALGO_SHA256);
    openssl_free_key($pkeyid);
echo base64_encode($signature); 
?>

Получаю сигнатуру. Пытаюсь делать проверку на С++. Сам класс:

#include "Sign.h"
RSA* Sign::createPrivateRSA(std::string key) {
    RSA *rsa = NULL;
    const char* c_string = key.c_str();
    BIO * keybio = BIO_new_mem_buf((void*)c_string, -1);
    if (keybio == NULL) {
        return 0;
    }
    rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa, NULL, NULL);
    return rsa;
}
RSA* Sign::createPublicRSA(std::string key) {
    RSA *rsa = NULL;
    BIO *keybio;
    const char* c_string = key.c_str();
    keybio = BIO_new_mem_buf((void*)c_string, -1);
    if (keybio == NULL) {
        return 0;
    }
    rsa = PEM_read_bio_RSA_PUBKEY(keybio, &rsa, NULL, NULL);
    return rsa;
}
bool Sign::RSASign(RSA* rsa,
    const unsigned char* Msg,
    size_t MsgLen,
    unsigned char** EncMsg,
    size_t* MsgLenEnc) {
    EVP_MD_CTX* m_RSASignCtx = EVP_MD_CTX_create();
    EVP_PKEY* priKey = EVP_PKEY_new();
    EVP_PKEY_assign_RSA(priKey, rsa);
    if (EVP_DigestSignInit(m_RSASignCtx, NULL, EVP_sha256(), NULL, priKey) <= 0) {
        return false;
    }
    if (EVP_DigestSignUpdate(m_RSASignCtx, Msg, MsgLen) <= 0) {
        return false;
    }
    if (EVP_DigestSignFinal(m_RSASignCtx, NULL, MsgLenEnc) <= 0) {
        return false;
    }
    *EncMsg = (unsigned char*)malloc(*MsgLenEnc);
    if (EVP_DigestSignFinal(m_RSASignCtx, *EncMsg, MsgLenEnc) <= 0) {
        return false;
    }
    EVP_MD_CTX_cleanup(m_RSASignCtx);
    return true;
}
bool Sign::RSAVerifySignature(RSA* rsa,
    unsigned char* MsgHash,
    size_t MsgHashLen,
    const char* Msg,
    size_t MsgLen,
    bool* Authentic) {
    *Authentic = false;
    EVP_PKEY* pubKey = EVP_PKEY_new();
    EVP_PKEY_assign_RSA(pubKey, rsa);
    EVP_MD_CTX* m_RSAVerifyCtx = EVP_MD_CTX_create();
    if (EVP_DigestVerifyInit(m_RSAVerifyCtx, NULL, EVP_sha256(), NULL, pubKey) <= 0) {
        return false;
    }
    if (EVP_DigestVerifyUpdate(m_RSAVerifyCtx, Msg, MsgLen) <= 0) {
        return false;
    }
    int AuthStatus = EVP_DigestVerifyFinal(m_RSAVerifyCtx, MsgHash, MsgHashLen);
    if (AuthStatus == 1) {
        *Authentic = true;
        EVP_MD_CTX_cleanup(m_RSAVerifyCtx);
        return true;
    }
    else if (AuthStatus == 0) {
        *Authentic = false;
        EVP_MD_CTX_cleanup(m_RSAVerifyCtx);
        return true;
    }
    else {
        *Authentic = false;
        EVP_MD_CTX_cleanup(m_RSAVerifyCtx);
        return false;
    }
}
void Sign::Base64Encode(const unsigned char* buffer,
    size_t length,
    char** base64Text) {
    BIO *bio, *b64;
    BUF_MEM *bufferPtr;
    b64 = BIO_new(BIO_f_base64());
    bio = BIO_new(BIO_s_mem());
    bio = BIO_push(b64, bio);
    BIO_write(bio, buffer, length);
    BIO_flush(bio);
    BIO_get_mem_ptr(bio, &bufferPtr);
    BIO_set_close(bio, BIO_NOCLOSE);
    BIO_free_all(bio);
    *base64Text = (*bufferPtr).data;
}
size_t Sign::calcDecodeLength(const char* b64input) {
    size_t len = strlen(b64input), padding = 0;
    if (b64input[len - 1] == '=' && b64input[len - 2] == '=') //last two chars are =
        padding = 2;
    else if (b64input[len - 1] == '=') //last char is =
        padding = 1;
    return (len * 3) / 4 - padding;
}
void Sign::Base64Decode(const char* b64message, unsigned char** buffer, size_t* length) {
    BIO *bio, *b64;
    int decodeLen = calcDecodeLength(b64message);
    *buffer = (unsigned char*)malloc(decodeLen + 1);
    (*buffer)[decodeLen] = '\0';
    bio = BIO_new_mem_buf(b64message, -1);
    b64 = BIO_new(BIO_f_base64());
    bio = BIO_push(b64, bio);
    *length = BIO_read(bio, *buffer, strlen(b64message));
    BIO_free_all(bio);
}
char* Sign::signMessage(std::string TermId, std::string plainText) {
    std::string privateKey;
    bool read_res = FUNC.ReadFile(std::string(GlobalSett.GetSetConfig("KEYS_path")) + "/public/" + TermId + ".key", privateKey, false, "\n");


    std::cout << privateKey << std::endl;
    if (read_res == true) {
        RSA* privateRSA = createPrivateRSA(privateKey);
        unsigned char* encMessage;
        char* base64Text;
        size_t encMessageLength;
        RSASign(privateRSA, (unsigned char*)plainText.c_str(), plainText.length(), &encMessage, &encMessageLength);
        Base64Encode(encMessage, encMessageLength, &base64Text);
        free(encMessage);
        return base64Text;
    }
    else
    {
        return "";
    }
}
bool Sign::verifySignature(std::string TermId, std::string plainText, char* signatureBase64) {
    std::string publicKey;
    bool read_res = FUNC.ReadFile(std::string(GlobalSett.GetSetConfig("KEYS_path"))+"/public/"+ TermId+".key", publicKey, false, "\n");

    if (read_res == true) {
        RSA* publicRSA = createPublicRSA(publicKey);
        unsigned char* encMessage;
        size_t encMessageLength;
        bool authentic;
        Base64Decode(signatureBase64, &encMessage, &encMessageLength);
        bool result = RSAVerifySignature(publicRSA, encMessage, encMessageLength, plainText.c_str(), plainText.length(), &authentic);
        return result & authentic;
    }
    else
    {
        return false;
    }
}

И далее проверка:

 bool authentic = Sign.verifySignature(root.get("terminal_id", 0).asString(), plainText, signature);
          if (authentic) {
              std::cout << "ok" << std::endl;
          }
          else {
              std::cout << "err" << std::endl;
          }

Если я просто читаю файл ключа, сгенерированого openssl, как есть то получаю ошибку Segmentation fault на строку

int AuthStatus = EVP_DigestVerifyFinal(m_RSAVerifyCtx, MsgHash, MsgHashLen);

Если же при чтении добавлять в конце каждой строки \n то ключ вгружается нормально, но верификация не проходит.

При подписи- проверки PHP - PHP или C++ - C++, все нормально. Думаю, что я как то не так вгружаю файл ключа.

В чем может быть проблема?

READ ALSO
Вставить *insert* массив в БД

Вставить *insert* массив в БД

как такой массив Insert в mysql ? Задача такова, что мне нужно данные пропарсить в БД, а как это сделать не соображаю, так как с такой задачей не сталкивался

163
Не работает условие в PHP

Не работает условие в PHP

Почему не работает условие ?

164
Как правильно наследовать сущность в symfony?

Как правильно наследовать сущность в symfony?

Как правильно наследовать сущность от другой сущности? У меня есть абстрактная сущность AbstractPerson в которой есть определенные свойства, есть...

148
Уникальность посетителя через cookie

Уникальность посетителя через cookie

Вообщем народ есть такая задача, нужно подсчитать количество уникальных активных посетителей на сайте в данный момент, уникальность определяется...

151