/** * Wrapper for OpenSSL cryptographic functions. * @author SE/Linux Team */ #include "security.h" #include #include #include #include #include #include #include #include #include #include #include #include //! Implementation of the Security interface. //! We didn't put this declaration into the public header, beacause it //! contains OpenSSL related IMPLEMENTATION DETAILS. class SecurityImpl : public Security { public: virtual void sign( const std::string& privateKeyFile, const ByteStream& data, ByteStream& output ); virtual void verifySignature( const std::string& certificateFile, const ByteStream& data, const ByteStream& signature ); virtual void encryptPublic( const std::string& certificateFile, const ByteStream& data, ByteStream& iv, ByteStream& ek, ByteStream& output ); virtual void decryptPrivate( const std::string& privateKeyFile, const ByteStream& data, const ByteStream& iv, const ByteStream& ek, ByteStream& output ); virtual std::string getCommonName( const std::string& certificateFile ); private: //! Opens the given file. //! @throws SecurityException on error. FILE *openFile(const std::string& file); //! Throws an SecurityException containing the last OpenSSL error. void throwSslError(); //! Reads a private key from the given file. //! @throws SecurityException on error. EVP_PKEY* readPrivKey(const std::string& file); //! Reads a public key from the given certificate file. //! @throws SecurityException on error. EVP_PKEY* readPubKey(const std::string& file); //! Reads a X509 certificate from the given certificate file. //! @throws SecurityException on error. X509 *readX509(const std::string& file); }; // ----------------------------------------------------------------------------- // SecurityException // ----------------------------------------------------------------------------- SecurityException::SecurityException(const std::string& what) : _what(what) { } // ----------------------------------------------------------------------------- // Security // ----------------------------------------------------------------------------- Security& Security::instance() { if (_instance == 0) { _instance = new SecurityImpl(); } return *_instance; } Security *Security::_instance = 0; // ----------------------------------------------------------------------------- // SecurityImpl // ----------------------------------------------------------------------------- FILE *SecurityImpl::openFile(const std::string& file) { FILE *ret = fopen(file.c_str(), "r"); if (ret == NULL) { std::ostringstream os; os << "Could not open file " << file << ": " << strerror(errno); throw SecurityException(os.str()); } return ret; } void SecurityImpl::throwSslError() { ERR_print_errors_fp(stderr); throw SecurityException("ssl error"); } EVP_PKEY* SecurityImpl::readPrivKey(const std::string& file) { FILE *f = NULL; EVP_PKEY *ret = NULL; f = openFile(file); ret = PEM_read_PrivateKey(f, NULL, NULL, NULL); fclose(f); if (! ret) { throwSslError(); } return ret; } X509 *SecurityImpl::readX509(const std::string& file) { FILE *f = openFile(file); X509 *ret = PEM_read_X509(f, NULL, NULL, NULL); fclose(f); if (!ret) { throwSslError(); } return ret; } void SecurityImpl::sign( const std::string& privateKeyFile, const ByteStream& data, ByteStream& output ) { EVP_PKEY *priv = readPrivKey(privateKeyFile); EVP_MD_CTX ctx; EVP_SignInit(&ctx, EVP_sha1()); EVP_SignUpdate(&ctx, &data[0], data.size()); output.resize(EVP_PKEY_size(priv)); unsigned int len = output.size(); int err = EVP_SignFinal(&ctx, &output[0], &len, priv); EVP_PKEY_free(priv); EVP_MD_CTX_cleanup(&ctx); if (err == 1) { output.resize(len); } else { throwSslError(); } } void SecurityImpl::verifySignature( const std::string& certificateFile, const ByteStream& data, const ByteStream& signature ) { X509 *x509 = readX509(certificateFile); EVP_PKEY *pub = X509_get_pubkey(x509); bool ok = true; int err; if (pub == NULL) { ok = false; goto failout; } EVP_MD_CTX ctx; EVP_VerifyInit(&ctx, EVP_sha1()); EVP_VerifyUpdate(&ctx, &data[0], data.size()); err = EVP_VerifyFinal(&ctx, const_cast(&signature[0]), signature.size(), pub); if (err != 1) { ok = false; goto failout; } failout: if (pub) EVP_PKEY_free(pub); if (x509) X509_free(x509); if (! ok) throwSslError(); } void SecurityImpl::encryptPublic( const std::string& certificateFile, const ByteStream& data, ByteStream& iv, ByteStream& ek, ByteStream& output ) { X509 *x509 = readX509(certificateFile); EVP_PKEY *pub = X509_get_pubkey(x509); bool ok = true; int len, len2; const EVP_CIPHER *cipher = EVP_aes_256_cbc(); EVP_CIPHER_CTX ctx; ek.resize(EVP_PKEY_size(pub)); int ekl = ek.size(); Byte *ek2 = &ek[0]; iv.resize(EVP_CIPHER_iv_length(cipher)); if (! EVP_SealInit(&ctx, cipher, &ek2, &ekl, &iv[0], &pub, 1)) { ok = false; goto failout; } ek.resize(ekl); output.resize( data.size() + EVP_CIPHER_block_size(cipher) - 1 + // for update EVP_CIPHER_block_size(cipher) // for final ); len = output.size(); if (! EVP_SealUpdate(&ctx, &output[0], &len, &data[0], data.size())) { ok = false; goto failout; } len2 = output.size() - len; if (! EVP_SealFinal(&ctx, &output[len], &len2)) { ok = false; goto failout; } output.resize(len + len2); failout: if (pub) EVP_PKEY_free(pub); if (x509) X509_free(x509); if (! ok) throwSslError(); } void SecurityImpl::decryptPrivate( const std::string& privateKeyFile, const ByteStream& data, const ByteStream& iv, const ByteStream& ek, ByteStream& output ) { EVP_PKEY *priv = readPrivKey(privateKeyFile); const EVP_CIPHER *cipher = EVP_aes_256_cbc(); EVP_CIPHER_CTX ctx; int len, len2; bool ok = true; output.resize(data.size() + EVP_CIPHER_block_size(cipher)); if (!EVP_OpenInit( &ctx, cipher, const_cast(&ek[0]), ek.size(), const_cast(&iv[0]), priv )) { ok = false; goto failout; } if (!EVP_OpenUpdate(&ctx, &output[0], &len, &data[0], data.size())) { ok = false; goto failout; } if (!EVP_OpenFinal(&ctx, &output[len], &len2)) { ok = false; goto failout; } output.resize(len + len2); failout: if (priv) EVP_PKEY_free(priv); if (!ok) throwSslError(); } std::string SecurityImpl::getCommonName( const std::string& certificateFile ) { X509 *x509 = readX509(certificateFile); X509_NAME *subj = 0; char data[256]; bool ok = true; subj = X509_get_subject_name(x509); if (subj == 0) { ok = false; goto failout; } if (! X509_NAME_get_text_by_NID(subj, NID_commonName, data, sizeof(data))) { ok = false; goto failout; } failout: X509_free(x509); if (!ok) { throwSslError(); } return data; }