mirror of https://gitee.com/openkylin/gdcm.git
474 lines
12 KiB
C++
474 lines
12 KiB
C++
/*=========================================================================
|
|
|
|
Program: GDCM (Grassroots DICOM). A DICOM library
|
|
|
|
Copyright (c) 2006-2011 Mathieu Malaterre
|
|
All rights reserved.
|
|
See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
|
|
|
|
This software is distributed WITHOUT ANY WARRANTY; without even
|
|
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
PURPOSE. See the above copyright notice for more information.
|
|
|
|
=========================================================================*/
|
|
//#include "gdcmCryptographicMessageSyntax.h"
|
|
#include "gdcmOpenSSLP7CryptographicMessageSyntax.h"
|
|
#include "gdcmTrace.h"
|
|
|
|
#include <limits> // numeric_limits
|
|
|
|
#include <stdio.h> // stderr
|
|
#include <string.h> // strcmp
|
|
#include <assert.h>
|
|
#include <time.h> // time()
|
|
|
|
#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
|
|
|
|
/*
|
|
* http://en.wikipedia.org/wiki/PKCS
|
|
* PKCS#7 <=> Cryptographic Message Syntax Standard
|
|
*/
|
|
namespace gdcm
|
|
{
|
|
|
|
/*
|
|
* openssl genrsa -out CA_key.pem 2048
|
|
*
|
|
* openssl req -new -key CA_key.pem -x509 -days 365 -out CA_cert.cer
|
|
*/
|
|
/*
|
|
* openssl smime -encrypt -aes256 -in inputfile.txt -out outputfile.txt -outform DER /tmp/server.pem
|
|
*/
|
|
#ifdef GDCM_USE_SYSTEM_OPENSSL
|
|
const EVP_CIPHER *CreateCipher( CryptographicMessageSyntax::CipherTypes ciphertype)
|
|
{
|
|
const EVP_CIPHER *cipher = 0;
|
|
switch( ciphertype )
|
|
{
|
|
case CryptographicMessageSyntax::DES3_CIPHER: // Triple DES
|
|
cipher = EVP_des_ede3_cbc();
|
|
break;
|
|
case CryptographicMessageSyntax::AES128_CIPHER: // CBC AES
|
|
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 {
|
|
//::STACK_OF(X509) *recips = recips;
|
|
if(!recips) {
|
|
return 0;
|
|
}
|
|
return ::sk_X509_num(recips);
|
|
}
|
|
STACK_OF(X509)* GetRecipients( ) const {
|
|
return recips;
|
|
}
|
|
::X509* GetRecipient( unsigned int i ) const {
|
|
//::STACK_OF(X509) *recips = Internals->recips;
|
|
::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;
|
|
// The following is inspired by PKCS7_encrypt
|
|
// and openssl/crypto/pkcs7/enc.c
|
|
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);
|
|
|
|
/* Set the content of the signed to 'data' */
|
|
/* PKCS7_content_new(p7,NID_pkcs7_data); not used in envelope */
|
|
|
|
/* could be used, but not in this version :-)
|
|
if (!nodetach) PKCS7_set_detached(p7,1);
|
|
*/
|
|
|
|
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);
|
|
}
|
|
// BIO_flush() returns 1 for success and 0 or -1 for failure.
|
|
int bflush = BIO_flush(p7bio);
|
|
if( bflush != 1 )
|
|
{
|
|
gdcmErrorMacro( "BIO_flush: " << bflush );
|
|
return false;
|
|
}
|
|
|
|
if (!PKCS7_dataFinal(p7,p7bio))
|
|
{
|
|
gdcmErrorMacro( "PKCS7_dataFinal" );
|
|
return false;
|
|
}
|
|
|
|
// WARNING:
|
|
// BIO_reset() normally returns 1 for success and 0 or -1 for failure. File
|
|
// BIOs are an exception, they return 0 for success and -1 for failure.
|
|
if( BIO_reset(bio_buffer) != 1 )
|
|
{
|
|
gdcmErrorMacro( "BIO_reset" );
|
|
return false;
|
|
}
|
|
|
|
i2d_PKCS7_bio(bio_buffer,p7);
|
|
// (void)BIO_flush(wbio);
|
|
|
|
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; // why not :)
|
|
#endif
|
|
}
|
|
|
|
bool OpenSSLP7CryptographicMessageSyntax::Encrypt(char *output, size_t &outlen, const char *array, size_t len) const
|
|
{
|
|
#ifdef GDCM_USE_SYSTEM_OPENSSL
|
|
// RAND_status() and RAND_event() return 1 if the PRNG has been seeded with
|
|
// enough data, 0 otherwise.
|
|
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 /* GDCM_USE_SYSTEM_OPENSSL */
|
|
}
|
|
|
|
/*
|
|
$ openssl smime -decrypt -in /tmp/debug.der -inform DER -recip /tmp/server.pem -inkey CA_key.pem
|
|
*/
|
|
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();
|
|
//bio_err=BIO_new_fp(stderr,BIO_NOCLOSE);
|
|
|
|
//data=BIO_new(BIO_s_file());
|
|
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);
|
|
|
|
|
|
/* Load the PKCS7 object from a file */
|
|
//if ((p7=PEM_read_bio_PKCS7(data,NULL,NULL,NULL)) == NULL) goto err;
|
|
if ((p7=d2i_PKCS7_bio(data,NULL)) == NULL) goto err;
|
|
|
|
if(!PKCS7_type_is_enveloped(p7)) {
|
|
goto err;
|
|
}
|
|
|
|
// if(cert && !X509_check_private_key(cert, pkey)) {
|
|
// PKCS7err(PKCS7_F_PKCS7_DECRYPT,
|
|
// PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE);
|
|
// return 0;
|
|
// }
|
|
|
|
|
|
cert_ctx=X509_STORE_CTX_new();
|
|
/* This stuff is being setup for certificate verification.
|
|
* When using SSL, it could be replaced with a
|
|
* cert_stre=SSL_CTX_get_cert_store(ssl_ctx); */
|
|
cert_store=X509_STORE_new();
|
|
X509_STORE_set_default_paths(cert_store);
|
|
X509_STORE_load_locations(cert_store,NULL,"../../certs");
|
|
//X509_STORE_set_verify_cb_func(cert_store,verify_callback);
|
|
|
|
ERR_clear_error();
|
|
|
|
/* We need to process the data */
|
|
/* We cannot support detached encryption */
|
|
p7bio=PKCS7_dataDecode(p7,pkey,detached,NULL);
|
|
|
|
if (p7bio == NULL)
|
|
{
|
|
printf("problems decoding\n");
|
|
goto err;
|
|
}
|
|
|
|
/* We now have to 'read' from p7bio to calculate digests etc. */
|
|
for (;;)
|
|
{
|
|
i=BIO_read(p7bio,buf,sizeof(buf));
|
|
/* print it? */
|
|
if (i <= 0) break;
|
|
//fwrite(buf,1, i, stdout);
|
|
memcpy(ptr, buf, i);
|
|
ptr += i;
|
|
outlen += i;
|
|
}
|
|
|
|
/* We can now verify signatures */
|
|
sk=PKCS7_get_signer_info(p7);
|
|
if (sk == NULL)
|
|
{
|
|
//fprintf(stderr, "there are no signatures on this data\n");
|
|
}
|
|
else
|
|
{
|
|
/* Ok, first we need to, for each subject entry,
|
|
* see if we can verify */
|
|
ERR_clear_error();
|
|
for (i=0; i<sk_PKCS7_SIGNER_INFO_num(sk); i++)
|
|
{
|
|
//si=my_sk_PKCS7_SIGNER_INFO_value(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 /* GDCM_USE_SYSTEM_OPENSSL */
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
//if ((x509=openssl::PEM_read_bio_X509(in,NULL,NULL,NULL)) == NULL) goto err;
|
|
(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();
|
|
assert( recips );
|
|
::X509 *x509 = NULL;
|
|
|
|
::BIO *in;
|
|
if (!(in=::BIO_new_file(keyfile,"r")))
|
|
{
|
|
return false;
|
|
}
|
|
// -> LEAK reported by valgrind...
|
|
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
|
|
}
|
|
|
|
|
|
} // end namespace gdcm
|