#include "gdcmOpenSSLP7CryptographicMessageSyntax.h"
#include "gdcmTrace.h"
#include <limits>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#ifdef GDCM_USE_SYSTEM_OPENSSL
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#endif
namespace gdcm
{
#ifdef GDCM_USE_SYSTEM_OPENSSL
const EVP_CIPHER *CreateCipher( CryptographicMessageSyntax::CipherTypes ciphertype)
{
const EVP_CIPHER *cipher = 0;
switch( ciphertype )
{
case CryptographicMessageSyntax::DES3_CIPHER: cipher = EVP_des_ede3_cbc();
break;
case CryptographicMessageSyntax::AES128_CIPHER: cipher = EVP_aes_128_cbc();
break;
case CryptographicMessageSyntax::AES192_CIPHER: cipher = EVP_aes_192_cbc();
break;
case CryptographicMessageSyntax::AES256_CIPHER: cipher = EVP_aes_256_cbc();
break;
}
return cipher;
}
#endif
class CryptographicMessageSyntaxInternals
{
#ifdef GDCM_USE_SYSTEM_OPENSSL
public:
CryptographicMessageSyntaxInternals():recips(NULL),pkey(NULL),CipherType( CryptographicMessageSyntax::AES256_CIPHER ),cipher(NULL),p7(PKCS7_new()){
recips = sk_X509_new_null();
PKCS7_set_type(p7,NID_pkcs7_enveloped);
bio_buffer = BIO_new(BIO_s_mem());
Initialized = false;
}
~CryptographicMessageSyntaxInternals() {
EVP_PKEY_free(pkey);
PKCS7_free(p7);
p7 = NULL;
BIO_free_all(bio_buffer);
}
unsigned int GetNumberOfRecipients() const {
if(!recips) {
return 0;
}
return ::sk_X509_num(recips);
}
STACK_OF(X509)* GetRecipients( ) const {
return recips;
}
::X509* GetRecipient( unsigned int i ) const {
::X509 *ret = sk_X509_value(recips, i);
return ret;
}
void SetPrivateKey(::EVP_PKEY* thepkey) {
this->pkey = thepkey;
}
::EVP_PKEY* GetPrivateKey() const {
return pkey;
}
void SetCipherType(CryptographicMessageSyntax::CipherTypes ciphertype) {
CipherType = ciphertype;
}
bool Initialize()
{
if(!cipher)
{
cipher = CreateCipher( GetCipherType() );
}
if(!cipher) return false;
if( !PKCS7_set_cipher(p7,cipher) ) return false;
for(unsigned int i = 0; i < GetNumberOfRecipients(); i++) {
::X509* recip = GetRecipient(i);
if (!PKCS7_add_recipient(p7,recip)) return false;
}
sk_X509_pop_free(recips, X509_free);
return true;
}
bool Encrypt(char *output, size_t &outlen, const char *array, size_t len)
{
if( !Initialized )
{
bool b = Initialize();
if ( !b )
{
gdcmErrorMacro( "Initialize" );
return false;
}
Initialized = true;
}
if( len > (size_t)std::numeric_limits<int>::max() )
{
gdcmErrorMacro( "len is too big: " << len );
return false;
}
#ifdef OPENSSL_HAS_CONST_VOID_BIO_NEW_MEM_BUF
BIO *data = BIO_new_mem_buf((const void*)array, (int)len);
#else
BIO *data = BIO_new_mem_buf((void*)array, (int)len);
#endif
if(!data)
{
gdcmErrorMacro( "BIO_new_mem_buf" );
return false;
}
char buf[256];
BIO *p7bio;
if ((p7bio=PKCS7_dataInit(p7,NULL)) == NULL) return false;
for (;;)
{
int i = BIO_read(data,buf,sizeof(buf));
if (i <= 0) break;
BIO_write(p7bio,buf,i);
}
int bflush = BIO_flush(p7bio);
if( bflush != 1 )
{
gdcmErrorMacro( "BIO_flush: " << bflush );
return false;
}
if (!PKCS7_dataFinal(p7,p7bio))
{
gdcmErrorMacro( "PKCS7_dataFinal" );
return false;
}
if( BIO_reset(bio_buffer) != 1 )
{
gdcmErrorMacro( "BIO_reset" );
return false;
}
i2d_PKCS7_bio(bio_buffer,p7);
char *binary;
long biolen = BIO_get_mem_data(bio_buffer,&binary);
gdcmAssertMacro( biolen >= 0 );
if ( outlen < (size_t)biolen )
{
gdcmErrorMacro( "Allocation issue: " << outlen << " vs " << biolen << " from " << len );
return false;
}
outlen = biolen;
memcpy( output, binary, outlen );
BIO_free(data);
BIO_free_all(p7bio);
return true;
}
CryptographicMessageSyntax::CipherTypes GetCipherType() const {
return CipherType;
}
::PKCS7 *GetP7() const { return p7; }
private:
STACK_OF(X509) *recips;
::EVP_PKEY *pkey;
CryptographicMessageSyntax::CipherTypes CipherType;
const EVP_CIPHER *cipher;
::PKCS7 *p7;
BIO *bio_buffer;
bool Initialized;
#endif
};
OpenSSLP7CryptographicMessageSyntax::OpenSSLP7CryptographicMessageSyntax()
{
Internals = new CryptographicMessageSyntaxInternals;
}
OpenSSLP7CryptographicMessageSyntax::~OpenSSLP7CryptographicMessageSyntax()
{
delete Internals;
}
void OpenSSLP7CryptographicMessageSyntax::SetCipherType( CryptographicMessageSyntax::CipherTypes type )
{
#ifdef GDCM_USE_SYSTEM_OPENSSL
Internals->SetCipherType( type );
#else
(void)type;
#endif
}
CryptographicMessageSyntax::CipherTypes OpenSSLP7CryptographicMessageSyntax::GetCipherType() const
{
#ifdef GDCM_USE_SYSTEM_OPENSSL
return Internals->GetCipherType();
#else
return AES256_CIPHER; #endif
}
bool OpenSSLP7CryptographicMessageSyntax::Encrypt(char *output, size_t &outlen, const char *array, size_t len) const
{
#ifdef GDCM_USE_SYSTEM_OPENSSL
if( !RAND_status() )
{
gdcmErrorMacro( "PRNG was not seeded properly" );
outlen = 0;
return false;
}
return Internals->Encrypt(output, outlen, array, len);
#else
(void)output;
(void)array;
(void)len;
outlen = 0;
gdcmDebugMacro( "GDCM_USE_SYSTEM_OPENSSL is OFF" );
return false;
#endif
}
bool OpenSSLP7CryptographicMessageSyntax::Decrypt(char *output, size_t &outlen, const char *array, size_t len) const
{
#ifdef GDCM_USE_SYSTEM_OPENSSL
CryptographicMessageSyntaxInternals *x509 = Internals;
::PKCS7 *p7;
#undef PKCS7_SIGNER_INFO
::PKCS7_SIGNER_INFO *si;
X509_STORE_CTX *cert_ctx=NULL;
X509_STORE *cert_store=NULL;
BIO *data,*detached=NULL,*p7bio=NULL;
char buf[1024*4];
unsigned char *pp;
int i;
STACK_OF(PKCS7_SIGNER_INFO) *sk;
char * ptr = output;
outlen = 0;
OpenSSL_add_all_algorithms();
pp=NULL;
EVP_PKEY *pkey = x509->GetPrivateKey();
if( len > (size_t)std::numeric_limits<int>::max() )
{
gdcmErrorMacro( "len is too big: " << len );
return false;
}
#ifdef OPENSSL_HAS_CONST_VOID_BIO_NEW_MEM_BUF
data = BIO_new_mem_buf((const void*)array, (int)len);
#else
data = BIO_new_mem_buf((void*)array, (int)len);
#endif
if(!data) goto err;
if (pp == NULL)
BIO_set_fp(data,stdin,BIO_NOCLOSE);
if ((p7=d2i_PKCS7_bio(data,NULL)) == NULL) goto err;
if(!PKCS7_type_is_enveloped(p7)) {
goto err;
}
cert_ctx=X509_STORE_CTX_new();
cert_store=X509_STORE_new();
X509_STORE_set_default_paths(cert_store);
X509_STORE_load_locations(cert_store,NULL,"../../certs");
ERR_clear_error();
p7bio=PKCS7_dataDecode(p7,pkey,detached,NULL);
if (p7bio == NULL)
{
printf("problems decoding\n");
goto err;
}
for (;;)
{
i=BIO_read(p7bio,buf,sizeof(buf));
if (i <= 0) break;
memcpy(ptr, buf, i);
ptr += i;
outlen += i;
}
sk=PKCS7_get_signer_info(p7);
if (sk == NULL)
{
}
else
{
ERR_clear_error();
for (i=0; i<sk_PKCS7_SIGNER_INFO_num(sk); i++)
{
si=sk_PKCS7_SIGNER_INFO_value(sk,i);
i=PKCS7_dataVerify(cert_store,cert_ctx,p7bio,p7,si);
if (i <= 0)
goto err;
else
fprintf(stderr,"Signature verified\n");
}
}
X509_STORE_CTX_free(cert_ctx);
X509_STORE_free(cert_store);
BIO_free_all(p7bio);
PKCS7_free(p7); p7 = NULL;
BIO_free(data);
return true;
err:
ERR_load_crypto_strings();
ERR_print_errors_fp(stderr);
return false;
#else
(void)output;
(void)array;
(void)len;
outlen = 0;
gdcmDebugMacro( "GDCM_USE_SYSTEM_OPENSSL is OFF" );
return false;
#endif
}
bool OpenSSLP7CryptographicMessageSyntax::ParseKeyFile( const char *keyfile)
{
#ifdef GDCM_USE_SYSTEM_OPENSSL
::BIO *in;
::EVP_PKEY *pkey;
if ((in=::BIO_new_file(keyfile,"r")) == NULL)
{
return false;
}
(void)BIO_reset(in);
if ((pkey=PEM_read_bio_PrivateKey(in,NULL,NULL,NULL)) == NULL)
{
return false;
}
BIO_free(in);
Internals->SetPrivateKey( pkey );
return true;
#else
(void)keyfile;
gdcmDebugMacro( "GDCM_USE_SYSTEM_OPENSSL is OFF" );
return false;
#endif
}
bool OpenSSLP7CryptographicMessageSyntax::ParseCertificateFile( const char *keyfile)
{
#ifdef GDCM_USE_SYSTEM_OPENSSL
STACK_OF(X509) *recips = Internals->GetRecipients();
gdcm_assert( recips );
::X509 *x509 = NULL;
::BIO *in;
if (!(in=::BIO_new_file(keyfile,"r")))
{
return false;
}
if (!(x509=::PEM_read_bio_X509(in,NULL,NULL,NULL)))
{
return false;
}
::BIO_free(in); in = NULL;
::sk_X509_push(recips, x509);
return true;
#else
(void)keyfile;
gdcmDebugMacro( "GDCM_USE_SYSTEM_OPENSSL is OFF" );
return false;
#endif
}
}