Russian Qt Forum

Программирование => Общий => Тема начата: Magvaj от Август 13, 2009, 19:14



Название: OpenSSL RSA_sign работает через раз
Отправлено: Magvaj от Август 13, 2009, 19:14
Использую RSA_sign и RSA_verify для цифровой подписи. Ключ длиной 2048 бит.

Ключи грузятся нормально... цифровая подпись создаётся и всё вроде бы прекрасно. Но! Если в строке для подписи есть Base64 строка, то он может создать подпись, а может и не создать. С некоторыми ключами работает хоть как-то, а некоторые вообще отказываются принимать наже малюсенькие Base64 строки внутри документа (документ XML).

Пробовал разные алгоритмы для подписи- NID_md5_sha1, NID_sha1, NID_md5, NID_sha1WithRSA.
Пробовал менять длину ключа на 512 бит.
Пробовал использовать вместо RSA_sign RSA_sign_ASN1_OCTET_STRING

Результат один: если в документе содержится Base64- то подпись не создаётся с ошибкой: "error:00000000:lib(0):func(0):reason(0)"

Вот код которым подписываю:

Код:
QString ClientThread::signRSA(QString st)
{
    unsigned char *sigret=new unsigned char[2048];
    unsigned int siglen=0;

    qDebug(st.toAscii().data());
    qDebug(QVariant(strlen(st.toAscii().data())).toString().toAscii());

    int i=RSA_sign_ASN1_OCTET_STRING(0, (unsigned char*)st.toAscii().data(), strlen(st.toAscii().data()), sigret, &siglen, rsa_private);
    if(i==1)
    {
        QString a=QByteArray((char*)sigret, siglen).toBase64();
        delete sigret;
        return a;
    }
    else
    {
        delete sigret;
        qDebug("sign failed!");
        qDebug(QVariant(i).toString().toAscii());
        qDebug(ERR_error_string(i, NULL));
        return "";
    }
}

На самом удачном ключе вот это подписывается:
Код:
<auth login="Magvaj" pass="wmmTMJIyBg15XcOsIFb59zA3ZO8=" />

а это уже нет:
Код:
<sysuser action="add" name="kot" login="kot" pass="NA4k9qXcsfBYRa2sCqo1A5t+3Qc=" key="LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJDZ0tDQVFFQTFpanY4RktFTTN2MklCaW4vL1JQQW9rb1R2cDlwbElKdHVuRVBKZ2dHV092aFdRc2Q3NzcKK2hkOVlWaU0xWVV2Lzg1TmRnNkhibXk4YXFSSEcrT2NvT2R4RmREbnFma0FrNENOUE42SmJnQnNmbFN0MXZGZQorWWpjL2FrNnY3akQvOEwwN2dld0JUTnNYU2JUQjBodUFsNGp0aWVFMTNzN3JwazlnNFQ2bTJCT2p0b0haR2swCm5wMlFKOVBhUHh3SXlIbENJcXBFZ2xINi9wQll4TjVDcFVpSUpGMy9xT2VFanNRSGdpb1RWVE9qWjAxQWYwQUQKM0cvRHRIbXNTODRCTG5aaXNlak5URkpsd3lTNUFDMHR3cW1WVmpOU1Uvcm56Ui8zUmo3Uyt3TS9RNG93clZ3bQpFL3N2SmMrQmJidGk0am1IY0xQeG5jbVhLRmhURTh2SWZ3SURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0KOExhd46mv9n0EC1LaoqbrcDU6f8WLkdhfJi10/ISIzVIXHGHnrbP6QQgPVt6mqu90OT5AQ==" />

На других не подписывается даже первая строка. Если пароль поставить некодированным- то подпись проходит на всех ключах.

Уже неделю не могу решить загвоздку. Помогите, пожалуйста...

P.S: OpenSSL 0.9.8g


Название: Re: OpenSSL RSA_sign работает через раз
Отправлено: spectre71 от Август 13, 2009, 19:29
Если честно, нифига не понимаю в твоей проблеме, OpenSSL использую пока только для шифрования данных.
Может поможет более свежая версия?
openssl-0.9.8k


Название: Re: OpenSSL RSA_sign работает через раз
Отправлено: Magvaj от Август 13, 2009, 19:42
Блин, думал если нет ответов- снести тему.

Решение нашлось(как всегда- пока не написал на форум- не получалось, как написал- решилось за две секунды)

Решение: почему-то нельзя использовать функцию strlen() для вычисления размера передаваемой на подпись строки... заменил функцию на sizeof()... и о чудо- работает.

Таким образом: RSA_sign_ASN1_OCTET_STRING(0, (unsigned char*)st.toAscii().data(), sizeof(st.toAscii().data()), sigret, &siglen, rsa_private) работает отменно, даже если сунуть ему 1,4 метровый XML полный Base64...

Вообщем, простите за глупость... все втыкались... я воткнулся...  ::)


Название: Re: OpenSSL RSA_sign работает через раз
Отправлено: Magvaj от Август 13, 2009, 19:48
На всякий случай замечу- что функция всё равно работает нормально только при алгоритмах NID_md5 или NID_sha1, если использовать двойные типа NID_md5_sha1- всё равно не создаёт подпись...

пожет пригодится тем, кто будет так же мучиться ;-)


Название: Re: OpenSSL RSA_sign работает через раз
Отправлено: spectre71 от Август 13, 2009, 20:33
Блин, думал если нет ответов- снести тему.

Решение нашлось(как всегда- пока не написал на форум- не получалось, как написал- решилось за две секунды)

Решение: почему-то нельзя использовать функцию strlen() для вычисления размера передаваемой на подпись строки... заменил функцию на sizeof()... и о чудо- работает.

Таким образом: RSA_sign_ASN1_OCTET_STRING(0, (unsigned char*)st.toAscii().data(), sizeof(st.toAscii().data()), sigret, &siglen, rsa_private) работает отменно, даже если сунуть ему 1,4 метровый XML полный Base64...

Вообщем, простите за глупость... все втыкались... я воткнулся...  ::)

sizeof(st.toAscii().data())  ==  sizeof(char*)
Я ничего не путаю? :)


Название: Re: OpenSSL RSA_sign работает через раз
Отправлено: Magvaj от Август 14, 2009, 06:40

sizeof(st.toAscii().data())  ==  sizeof(char*)
Я ничего не путаю? :)


ну да... а что не так?


Название: Re: OpenSSL RSA_sign работает через раз
Отправлено: Tonal от Август 14, 2009, 07:35
Решение: почему-то нельзя использовать функцию strlen() для вычисления размера передаваемой на подпись строки... заменил функцию на sizeof()... и о чудо- работает.
Учи язык!

Таким образом: RSA_sign_ASN1_OCTET_STRING(0, (unsigned char*)st.toAscii().data(), sizeof(st.toAscii().data()), sigret, &siglen, rsa_private) работает отменно, даже если сунуть ему 1,4 метровый XML полный Base64...
Этим кодом ты создаёшь подпись для первых 2х или 4х символов строки в зависимости от платформы и компилятора.
Даже в случае пустой строки. :)
Потому как sizeof(st.toAscii().data()) всегда равна размеру указателя на QChar.
А сам QChar - это 16-bit Unicode character.

Может тебе будет проще использовать QCA (http://delta.affinix.com/qca/): Qt Cryptographic Architecture?
Или хотя бы поглядеть как оно там используется. :)


Название: Re: OpenSSL RSA_sign работает через раз
Отправлено: Magvaj от Август 17, 2009, 10:00
Учи язык!

Блин... действительно сглупил (ночью кодил- спать хотел)... значит всё таки косяк где-то есть... покурю QCA...


Название: Re: OpenSSL RSA_sign работает через раз
Отправлено: Magvaj от Август 20, 2009, 08:59
Если кому будет интересно- решение было выдрано из кода OpenSSH.

Перед тем как использовать RSA_sign и RSA_verify необходимо установить какие-то структуры EVP. Сразу говорю- что они делают не знаю, но всё работает.

Код сервера:

Код:
bool ClientThread::setPublicKey(QString key)
{
    SSL_load_error_strings();
    ERR_load_crypto_strings();
    OpenSSL_add_all_algorithms();
    BIO *mem = BIO_new(BIO_s_mem());
    BIO_puts (mem, key.toAscii().data());
    qDebug(key.toAscii());
    rsa_public=PEM_read_bio_RSAPublicKey(mem, NULL, NULL, NULL);

    if (rsa_public == NULL) return false;
    return true;
}

bool ClientThread::unsignRSA(QString text, QString sign)
{
    QByteArray a=QByteArray::fromBase64(sign.toAscii());
   
    unsigned char digest[2048];
    unsigned int dlen;
    const EVP_MD *evp_md;
    EVP_MD_CTX md;
    if ((evp_md = EVP_get_digestbynid(NID_md5)) == NULL) return false;
    EVP_DigestInit(&md, evp_md);
    EVP_DigestUpdate(&md, (unsigned char*)text.toAscii().data(), strlen(text.toAscii().data()));
    EVP_DigestFinal(&md, digest, &dlen);
   
    return RSA_verify(NID_md5, digest, dlen, (unsigned char*)a.data(), a.length(), rsa_public);
}

Код клиента:

Код:
QString ClientThread::signRSA(QString st)
{
    unsigned char *sigret=new unsigned char[2048];
    unsigned int siglen=0;

    unsigned char digest[2048];
    unsigned int dlen;
    const EVP_MD *evp_md;
    EVP_MD_CTX md;
    if ((evp_md = EVP_get_digestbynid(NID_md5)) == NULL) return false;
    EVP_DigestInit(&md, evp_md);
    EVP_DigestUpdate(&md, (unsigned char*)st.toAscii().data(), strlen(st.toAscii().data()));
    EVP_DigestFinal(&md, digest, &dlen);


    if(RSA_sign(NID_md5, digest, dlen, sigret, &siglen, rsa_private))
    {
        QString a=QByteArray((char*)sigret, siglen).toBase64();
        delete sigret;
        return a;
    }
    else
    {
        delete sigret;
        //qDebug("sign failed!");
        return "";
    }
}

bool ClientThread::setPrivateKey(QString filepath, QString pempass)
{
    SSL_load_error_strings();
  ERR_load_crypto_strings();
  OpenSSL_add_all_algorithms();

    FILE *priv_key_file=NULL;
    priv_key_file=fopen(filepath.toAscii(), "rb");
    if(priv_key_file)
    {
        rsa_private=PEM_read_RSAPrivateKey(priv_key_file, NULL, 0, pempass.toAscii().data());
        if (rsa_private == NULL) return false;
        fclose(priv_key_file);
        return true;
    }
    else return false;
}