/*
* Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
/**
@file
@internalComponent
@released
*/
#ifdef _MSC_VER
#pragma warning (disable: 4786)
#endif // _MSC_VER
#include <iostream>
#include <fstream>
#include <string>
#include "sissignature.h"
#include "siscontroller.h"
#include <openssl/dsa.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
#include <openssl/evp.h>
#include <openssl/objects.h>
#include <openssl/x509.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
#define RSA_IDENTIFIER L"1.2.840.113549.1.1.5"
#define DSA_IDENTIFIER L"1.2.840.10040.4.3"
#define DSA_PRIME_SIZE 1024
#define OLD_PEM_FORMAT_TAG "Proc-Type"
#define ENCRYPTION_ALGORITHM_TAG "DEK-Info"
#define PEM_HEADER "----"
void CalculateHash (TSHADigest& aDigest, const TUint8* aBuffer, TUint32 aBufferSize)
{
assert (aBuffer);
assert (! IsBadReadPtr (aBuffer, aBufferSize));
memset (&aDigest, 0, SHA_DIGEST_LENGTH);
SHA1 (aBuffer, aBufferSize, aDigest);
}
CSignature::CSignature (CSignatureData& aSISSignature):
iSISSignature(aSISSignature),
iSignatureAlgorithm(const_cast<CSISSignatureAlgorithm&>(iSISSignature.GetAlgorithm()))
{
}
CSignature::~CSignature()
{
}
void CSignature::Sign (const std::wstring& aPrivateKey, const std::wstring& aPassPhrase, const TUint8* aBuffer, const TUint32 aBufferSize)
{
assert (aBuffer);
TSHADigest digest;
CalculateHash (digest, aBuffer, aBufferSize);
void* key = LoadKey (aPrivateKey, aPassPhrase);
assert (key);
switch (iSISSignature.GetAlgorithm().Algorithm())
{
case CSISSignatureAlgorithm::EAlgRSA :
RSASign (digest, key);
break;
case CSISSignatureAlgorithm::EAlgDSA :
DSASign (digest, key);
break;
default:
throw CSISException (CSISException::ECrypto, "unknown algorithm");
}
}
void CSignature::DSASign (const TSHADigest& aDigest, void* aKey)
{
DSA* dsa = reinterpret_cast <DSA*> (aKey);
assert (dsa);
unsigned int written = 0;
int status = 0;
try
{
unsigned int max = DSA_size (dsa);
iSISSignature.SetDataByteCount(max);
status = DSA_sign (NID_sha1, aDigest, SHA_DIGEST_LENGTH, const_cast<unsigned char*>(iSISSignature.Data()), &written, dsa);
}
catch (...)
{
status = -1;
}
if (status <= 0)
{
DSA_free (dsa);
throw CSISException (CSISException::ECrypto, L"cannot create DSA signature");
}
CSISException::ThrowIf (written > iSISSignature.DataSize (), CSISException::ECrypto, L"inconsistent DSA sizes");
}
void CSignature::RSASign (const TSHADigest& aDigest, void* aKey)
{
RSA* rsa = reinterpret_cast <RSA*> (aKey);
assert (rsa);
unsigned int written = 0;
int status = 0;
try
{
iSISSignature.SetDataByteCount(RSA_size (rsa));
status = RSA_sign (NID_sha1, aDigest, SHA_DIGEST_LENGTH, const_cast<unsigned char*>(iSISSignature.Data()), &written, rsa);
}
catch (...)
{
status = -1;
}
if (status <= 0)
{
RSA_free (rsa);
throw CSISException (CSISException::ECrypto, L"cannot create RSA signature");
}
CSISException::ThrowIf (written > iSISSignature.DataSize (), CSISException::ECrypto, L"inconsistent RSA sizes");
}
void CSignature::VerifySignature (X509* aX509, const TUint8* aBuffer, const TUint32 aBufferSize) const
{
assert (aBuffer);
TSHADigest digest;
CalculateHash (digest, aBuffer, aBufferSize);
switch (iSISSignature.GetAlgorithm().Algorithm ())
{
case CSISSignatureAlgorithm::EAlgRSA :
RSAVerify (digest, aX509);
break;
case CSISSignatureAlgorithm::EAlgDSA :
DSAVerify (digest, aX509);
break;
default:
throw CSISException (CSISException::ECrypto, "unknown algorithm");
}
}
void CSignature::DSAVerify (const TSHADigest& aDigest, X509* aX509) const
{
assert (aX509);
EVP_PKEY* pubkey = X509_get_pubkey (aX509);
if (! pubkey)
{
throw CSISException (CSISException::ECrypto, "no public key in certificate");
}
DSA* dsa = EVP_PKEY_get1_DSA (pubkey);
if (! dsa)
{
throw CSISException (CSISException::ECrypto, "not a DSA certificate");
}
TUint8* sig = const_cast <TUint8*> (iSISSignature.Data());
int status = DSA_verify (NID_sha1, aDigest, SHA_DIGEST_LENGTH, sig, iSISSignature.DataSize(), dsa);
if (status <= 0)
{
throw CSISException (CSISException::EVerification, "invalid signature");
}
}
void CSignature::RSAVerify (const TSHADigest& aDigest, X509* aX509) const
{
assert (aX509);
EVP_PKEY* pubkey = X509_get_pubkey (aX509);
if (! pubkey)
{
throw CSISException (CSISException::ECrypto, "no public key in certificate");
}
RSA* rsa = EVP_PKEY_get1_RSA (pubkey);
if (! rsa)
{
throw CSISException (CSISException::ECrypto, "not an RSA certificate");
}
TUint8* sig = const_cast <TUint8*> (iSISSignature.Data());
int status = RSA_verify (NID_sha1, aDigest, SHA_DIGEST_LENGTH, sig, iSISSignature.DataSize(), rsa);
if (! status)
{
throw CSISException (CSISException::EVerification, "invalid signature");
}
}
void* CSignature::LoadKey (const std::wstring& aName, const std::wstring& aPassPhrase)
{
try
{
return LoadTextKey (aName, aPassPhrase);
}
catch (...)
{
try
{
return LoadBinaryKey (aName, aPassPhrase);
}
catch (...)
{
}
throw;
}
}
// returns pointer to RSA or DSA, depending on algorithm.
void* CSignature::LoadBinaryKey (const std::wstring& aName, const std::wstring& aPassPhrase)
{
TUint64 size;
HANDLE file = OpenFileAndGetSize (aName, &size);
TUint8* buffer = NULL;
void* reply = NULL;
DSA* dsa = NULL;
RSA* rsa = NULL;
try
{
buffer = new TUint8 [size];
ReadAndCloseFile (file, size, buffer);
file = NULL;
const TUint8* buf2 = buffer;
bool unknown = ! iSignatureAlgorithm.IsAlgorithmKnown ();
if (unknown || (iSignatureAlgorithm.Algorithm () == CSISSignatureAlgorithm::EAlgRSA))
{
ERR_clear_error();
rsa = RSA_new ();
if (! rsa)
{
std::cout << ERR_error_string (ERR_get_error (), NULL);
throw 2;
}
if (! d2i_RSAPrivateKey (&rsa, &buf2, size))
{
if (! unknown)
{
std::cout << ERR_error_string (ERR_get_error (), NULL);
}
RSA_free (rsa);
rsa = NULL;
}
else
{
if (unknown)
{
iSignatureAlgorithm.SetAlgorithm (CSISSignatureAlgorithm::EAlgRSA);
unknown = false;
}
reply = rsa;
}
}
if (unknown || (iSignatureAlgorithm.Algorithm () == CSISSignatureAlgorithm::EAlgDSA))
{
ERR_clear_error();
dsa = DSA_generate_parameters (DSA_PRIME_SIZE, NULL, 0, NULL, NULL, NULL, NULL);
if (! dsa)
{
std::cout << ERR_error_string (ERR_get_error (), NULL);
throw 1;
}
if (! d2i_DSAPrivateKey (&dsa, &buf2, size))
{
std::cout << ERR_error_string (ERR_get_error (), NULL);
DSA_free (dsa);
dsa = NULL;
}
else
{
if (unknown)
{
iSignatureAlgorithm.SetAlgorithm (CSISSignatureAlgorithm::EAlgDSA);
unknown = false;
}
reply = dsa;
}
}
if (! reply || unknown)
{
throw 3;
}
delete [] buffer;
}
catch (...)
{
if (rsa)
{
RSA_free (rsa);
}
if (dsa)
{
DSA_free (dsa);
}
delete [] buffer;
if (file)
{
::CloseHandle(file);
}
throw CSISException (CSISException::ECrypto, std::wstring (L"Cannot read ") + aName);
}
return reply;
}
void* CSignature::LoadTextKey (const std::wstring& aName, const std::wstring& aPassPhrase)
{
std::ifstream keyFile;
std::string line ;
std::string buffer;
bool oldPemFormat = false;
char *fName = NULL;
keyFile.rdbuf()->open(wstring2string (aName).c_str (), std::ios::in);
if(!keyFile.is_open())
{
if((fName = Copy2TmpFile(aName.c_str(), CERTFILE)) != NULL)
{
keyFile.rdbuf()->open(fName, std::ios::in);
}
}
//check if file is successfully opened.
if(keyFile.is_open())
{
//scans the file until it encounters the first header.
do
{
getline(keyFile,line);
}while(!(line.find(PEM_HEADER , 0) != std::string::npos));
buffer.append(line);
buffer.append("\n");
//scanning the file until the next non blank line is obtained.
do
{
getline(keyFile,line);
}while(!(line.length()));
//check whether the next non blank line contains the Proc Type Tag.
if((line.find(OLD_PEM_FORMAT_TAG , 0)) != std::string::npos)
{
oldPemFormat = true;
}
buffer.append(line);
buffer.append("\n");
while(!keyFile.eof())
{
getline(keyFile , line);
//skips any blank line between the Proc Type tag and the encryption algorithm tag
//and appends a blank line.After which all the blank lines will be skipped.
if(oldPemFormat)
{
if((line.find(ENCRYPTION_ALGORITHM_TAG , 0) != std::string::npos))
{
buffer.append(line);
buffer.append("\n\n");
}
else if(line.length())
{
buffer.append(line);
buffer.append("\n");
}
}
else
{
//if key file in new format
if(line.length())
{
buffer.append(line);
buffer.append("\n");
}
}
}
}
else
{
CSISException::ThrowIf (1, CSISException::EFileProblem, std::wstring (L"cannot open ") + aName);
}
BIO* mem = NULL;
void* reply = NULL;
char* pass = NULL;
bool unknown = ! iSignatureAlgorithm.IsAlgorithmKnown ();
try
{
//pass = new char [aPassPhrase.size () + 1];
//strcpy (pass, wstring2string (aPassPhrase).c_str ());
DWORD ilen = aPassPhrase.length();
int targetLen = ConvertWideCharToMultiByte(aPassPhrase.c_str(), -1, NULL, 0, CP_UTF8);
pass = new char[targetLen+1];
ConvertWideCharToMultiByte(aPassPhrase.c_str(), -1, pass, targetLen+1, CP_UTF8);
if (unknown || (iSignatureAlgorithm.Algorithm () == CSISSignatureAlgorithm::EAlgRSA))
{
ERR_clear_error();
//creates a memory BIO and writes the buffer data into it.
mem = BIO_new(BIO_s_mem());
BIO_puts(mem , buffer.c_str());
RSA* rsa = RSA_new ();
if (! PEM_read_bio_RSAPrivateKey (mem, &rsa, NULL, pass))
{
if (! unknown)
{
std::cout << ERR_error_string (ERR_get_error (), NULL);
}
BIO_free(mem);
RSA_free (rsa);
}
else
{
if (unknown)
{
iSignatureAlgorithm.SetAlgorithm (CSISSignatureAlgorithm::EAlgRSA);
unknown = false;
}
reply = rsa;
}
}
if (unknown || (iSignatureAlgorithm.Algorithm () == CSISSignatureAlgorithm::EAlgDSA))
{
ERR_clear_error();
//creates a memory BIO and writes the buffer data into it.
mem = NULL;
mem = BIO_new(BIO_s_mem());
BIO_puts(mem , buffer.c_str());
DSA* dsa = DSA_generate_parameters (DSA_PRIME_SIZE, NULL, 0, NULL, NULL, NULL, NULL);
if (! PEM_read_bio_DSAPrivateKey (mem, &dsa, NULL, pass))
{
std::cout << ERR_error_string (ERR_get_error (), NULL);
BIO_free(mem);
DSA_free (dsa);
}
else
{
if (unknown)
{
iSignatureAlgorithm.SetAlgorithm (CSISSignatureAlgorithm::EAlgDSA);
unknown = false;
}
reply = dsa;
}
}
if (unknown || ! reply)
{
throw 3;
}
}
catch (...)
{
if (keyFile.rdbuf()->is_open())
{
keyFile.rdbuf()->close();
}
if(mem)
{
BIO_free(mem);
}
delete[] pass;
throw CSISException (CSISException::ECrypto, std::wstring (L"Cannot load ") + aName);
}
BIO_free(mem);
delete pass;
return reply;
}