Get the server all setup for enabling encryption (alternate API calls implemented - just no encryption turned on yet).

This commit is contained in:
Ken Moore
2016-05-25 08:16:15 -04:00
parent 2ac91c7c70
commit 4ec1dd2694
6 changed files with 180 additions and 25 deletions

View File

@@ -110,6 +110,7 @@ void BridgeConnection::InjectMessage(QString msg){
//Add the "from" ID to the message
msg = msg.replace(0,lb,SockID); //replace the "to" ID with the "from" ID
emit SocketMessage(toID, msg); //forward the message on to the "to" connection
//qDebug() << "Message:" << toID << SockID << msg; //simple message snooping on the bridge - to verify server/client encryption
}
}
}

View File

@@ -338,13 +338,143 @@ QString AuthorizationManager::LoginUC(QHostAddress host, QString encstring){
}
}
QString AuthorizationManager::encryptString(QString msg, QString key){
//do nothing yet
return msg;
QString AuthorizationManager::encryptString(QString str, QByteArray key){
return str; //TEMPORARY BYPASS
bool pub=true;
if(key.contains("--BEGIN PUBLIC KEY--")){ pub=true; }
else if(key.contains(" PRIVATE KEY--")){ pub=false; }
else{ return str; } //unknown encryption - just return as-is
qDebug() << "Encrypt String:" << str << pub;//<< key;
//Reset/Load some SSL stuff
//OpenSSL_add_all_algorithms();
//ERR_load_crypto_strings();
//Now Encrypt the string
//qDebug() << "allocate encode array";
unsigned char *encode = (unsigned char*)malloc(2*str.length()); //give it plenty of extra space as needed
RSA *rsa= NULL;
BIO *keybio = NULL;
//qDebug() << "allocate keybio buffer";
keybio = BIO_new_mem_buf(key.data(), -1);
if(keybio==NULL){ return ""; }
if(!pub){
//Using PRIVATE key to encrypt
//qDebug() << "Read private key";
rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa,NULL, NULL);
if(rsa==NULL){ return ""; }
//qDebug() << "Encrypt with private key";
int len = RSA_private_encrypt(str.length(), (unsigned char*)(str.toLocal8Bit().data()), encode, rsa, RSA_PKCS1_PADDING);
if(len <0){ return ""; }
//qDebug() << "Return base-64 encoded version";
QByteArray str_encode( (char*)(encode), len);
str_encode = str_encode.toBase64();
return QString( str_encode );
}else{
//Using PUBLIC key to encrypt
//qDebug() << "Read Public Key";
rsa = PEM_read_bio_RSA_PUBKEY(keybio, &rsa,NULL, NULL);
if(rsa==NULL){ return ""; }
//qDebug() << "Encrypt with public key";
int len = RSA_public_encrypt(str.length(), (unsigned char*)(str.toLocal8Bit().data()), encode, rsa, RSA_PKCS1_PADDING);
if(len <0){ return ""; }
//qDebug() << "Return base-64 encoded version";
QByteArray str_encode = QByteArray::fromRawData( (char*)(encode), len);
qDebug() << "Encoded:" << str_encode;
str_encode = str_encode.toBase64();
qDebug() << "Could reverse encoding:" << (decryptString(str_encode, key) == str);
qDebug() << "Base64:" << str_encode;
return QString( str_encode );
}
return str;
}
QString AuthorizationManager::decryptString(QString msg, QString key){
return msg; //do nothing yet
QString AuthorizationManager::decryptString(QString str, QByteArray key){
return str; //TEMPORARY BYPASS
bool pub=true;
if(key.contains("--BEGIN PUBLIC KEY--")){ pub=true; }
else if(key.contains(" PRIVATE KEY--")){ pub=false; }
else{ return str; } //unknown encryption - just return as-is
//Reset/Load some SSL stuff
// OpenSSL_add_all_algorithms();
// ERR_load_crypto_strings();
//Turn the encrypted string into a byte array
QByteArray enc; enc.append(str.toLocal8Bit());
unsigned char *decode = (unsigned char*)malloc(2*str.length());
RSA *rsa= NULL;
BIO *keybio = NULL;
//qDebug() << " - Generate keybio";
keybio = BIO_new_mem_buf(key.data(), -1);
if(keybio==NULL){ return ""; }
//qDebug() << " - Read pubkey";
if(pub){
//PUBLIC KEY
rsa = PEM_read_bio_RSA_PUBKEY(keybio, &rsa,NULL, NULL);
if(rsa==NULL){ qDebug() << " - Invalid RSA key!!"; return ""; }
//qDebug() << " - Decrypt string";
int len = RSA_public_decrypt(enc.length(), (unsigned char*)(enc.data()), decode, rsa, RSA_PKCS1_PADDING);
if(len<0){ return ""; }
return QString( QByteArray( (char*)(decode), len) );
}else{
//PRIVATE KEY
rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa,NULL, NULL);
if(rsa==NULL){ qDebug() << " - Invalid RSA key!!"; return ""; }
//qDebug() << " - Decrypt string";
int len = RSA_private_decrypt(enc.length(), (unsigned char*)(enc.data()), decode, rsa, RSA_PKCS1_PADDING);
if(len<0){ return ""; }
return QString( QByteArray( (char*)(decode), len) );
}
}
//Additional SSL Encryption functions
QByteArray AuthorizationManager::GenerateSSLPrivkey(){
const int kBits = 4096; //25165824; // 3MB in bits
const int kExp = 3;
int keylen;
char *pem_key;
RSA *rsa = RSA_generate_key(kBits, kExp, 0, 0);
/* To get the C-string PEM form: */
BIO *bio = BIO_new(BIO_s_mem());
PEM_write_bio_RSAPrivateKey(bio, rsa, NULL, NULL, 0, NULL, NULL);
keylen = BIO_pending(bio);
pem_key = (char *)malloc(keylen+1); /* Null-terminate */
BIO_read(bio, pem_key, keylen);
QByteArray data = QByteArray::fromRawData(pem_key, keylen);
qDebug() << "New Priv Key:" << data;
return data;
}
QByteArray AuthorizationManager::pubkeyForMd5(QString md5_base64){
QByteArray md5 = QByteArray::fromBase64( md5_base64.toLocal8Bit() );
QStringList keys; //Format: "RegisteredCerts/<user>/<key>" (value is full text)
//Read all user's certs (since we only need checksums)
keys = CONFIG->allKeys().filter("RegisteredCerts/");
keys.sort();
QJsonArray arr;
QCryptographicHash chash(QCryptographicHash::Md5);
//qDebug() << "MD5 Generation:";
for(int i=0; i<keys.length(); i++){
//qDebug() << "User:" << keys[i].section("/",1,1);
QByteArray key = QByteArray::fromBase64( keys[i].section("/",2,-1).toLocal8Bit() ); //remember that the keys are stored internally as base64-encoded strings
//qDebug() << " - Key:" << key;
chash.addData( key );
QByteArray res = chash.result();
//qDebug() << " - md5:" << res;
chash.reset();
if(res==md5){
//qDebug() << "Found key for MD5:" << key;
return key;
}
}
return ""; //fallback - no matching key found
}
// =========================

View File

@@ -40,9 +40,13 @@ public:
QString LoginUC(QHostAddress host, QString encstring);
//Message Encryption/decryption methods
QString encryptString(QString msg, QString key);
QString decryptString(QString msg, QString key);
QString encryptString(QString msg, QByteArray key);
QString decryptString(QString msg, QByteArray key);
//Additional SSL Encryption functions
QByteArray GenerateSSLPrivkey();
QByteArray pubkeyForMd5(QString md5_base64);
private:
QHash<QString, QDateTime> HASH;
QHash <QString, QDateTime> IPFAIL;

View File

@@ -40,7 +40,7 @@ bool WebServer::startServer(quint16 port, bool websocket){
if(ok){
QCoreApplication::processEvents();
qDebug() << "Server Started:" << QDateTime::currentDateTime().toString(Qt::ISODate);
qDebug() << " Port:" << port;
qDebug() << " Port:" << (BRIDGE_ONLY ? "(Bridge Only)" : QString::number(port));
if(WSServer!=0){ qDebug() << " URL:" << WSServer->serverUrl().toString(); }
}else{
qCritical() << "Could not start server - exiting...";

View File

@@ -76,7 +76,7 @@ WebSocket::WebSocket(QObject *parent, QString url, QString ID, AuthorizationMana
TSOCKET = 0;
AUTHSYSTEM = auth;
SockPeerIP = SOCKET->peerAddress().toString();
LogManager::log(LogManager::HOST,"New Connection: "+SockPeerIP);
//LogManager::log(LogManager::HOST,"New Bridge Connection: "+SockPeerIP);
idletimer = new QTimer(this);
idletimer->setInterval(IDLETIMEOUTMINS*60000); //connection timout for idle sockets
idletimer->setSingleShot(true);
@@ -254,19 +254,40 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){
}else if(out.in_struct.name=="auth_ssl"){
if(out.in_struct.args.isObject() && out.in_struct.args.toObject().contains("encrypted_string")){
//Stage 2: Check the returned encrypted/string
qDebug() << "State 2 SSL Auth Request";
cur_auth_tok = AUTHSYSTEM->LoginUC(host, JsonValueToString(out.in_struct.args.toObject().value("encrypted_string")) );
}else{
//Stage 1: Send the client a random string to encrypt with their SSL key
QString key = AUTHSYSTEM->GenerateEncCheckString();
QJsonObject obj; obj.insert("test_string", key);
QJsonObject obj;
/*if(out.in_struct.args.toObject().contains("md5_key")){
qDebug() << "Encrypted SSL Auth Requested";
QString md5 = out.in_struct.args.toObject().value("md5_key").toString(); //Note: This is base64 encoded right now
//qDebug() << " - Get pub key for md5";
QByteArray pubkey = AUTHSYSTEM->pubkeyForMd5(md5);
//qDebug() << " - Generate new Priv key";
QByteArray privkey = AUTHSYSTEM->GenerateSSLPrivkey();
//Now break up the private key into 128 byte chunks and encrypt with public key for transport
//qDebug() << " - Destruct priv key into chunks" << "Length:" << privkey.size();
QJsonArray pkeyarr;
for(int i=0; i<privkey.size(); i+=64){
//qDebug() << " -- i:" << i;
pkeyarr << AUTHSYSTEM->encryptString( QString(privkey.mid(i,64)), pubkey);
}
obj.insert("new_ssl_key", pkeyarr); //send this to the client for re-assembly
//Also encrypt the test string with the public key as well
//qDebug() << " - Encrypt test string with pubkey";
key = AUTHSYSTEM->encryptString( key, pubkey);
//qDebug() << " - Done with special SSL section";
BRIDGE[REQ.bridgeID].enc_key = privkey;
}*/
obj.insert("test_string", key);
out.out_args = obj;
out.CODE = RestOutputStruct::OK;
QString msg = out.assembleMessage();
if(SOCKET!=0 && !REQ.bridgeID.isEmpty()){
//BRIDGE RELAY - alternate format
//Need to encrypt the message for output (TO-DO)
QString key = BRIDGE[REQ.bridgeID].enc_key;
if(!key.isEmpty()){ msg = AUTHSYSTEM->encryptString(msg, key); }
//BRIDGE RELAY - alternate format
//Note that the Stage 1 SSL auth reply is only partially encrypted (specific variables only, not bulk message encryption)
//Now add the destination ID
msg.prepend( REQ.bridgeID+"\n");
}
@@ -383,12 +404,10 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){
//Return any information
QString msg = out.assembleMessage();
if(SOCKET!=0 && !REQ.bridgeID.isEmpty()){
//BRIDGE RELAY - alternate format
//Need to encrypt the message for output (TO-DO)
QString key = BRIDGE[REQ.bridgeID].enc_key;
if(!key.isEmpty()){ msg = AUTHSYSTEM->encryptString(msg, key); }
//Now add the destination ID
msg.prepend( REQ.bridgeID+"\n");
//BRIDGE RELAY - alternate format
msg = AUTHSYSTEM->encryptString(msg, BRIDGE[REQ.bridgeID].enc_key);
//Now add the destination ID
msg.prepend( REQ.bridgeID+"\n");
}
if(out.CODE == RestOutputStruct::FORBIDDEN && SOCKET!=0 && SOCKET->isValid()){
this->sendReply(msg);
@@ -521,7 +540,7 @@ void WebSocket::EvaluateMessage(const QByteArray &msg){
}
void WebSocket::EvaluateMessage(const QString &msg){
//qDebug() << "New Text Message:";
qDebug() << "New Text Message:" << msg;
if(idletimer->isActive()){ idletimer->stop(); }
idletimer->start();
EvaluateREST(msg);
@@ -606,6 +625,8 @@ void WebSocket::SslError(const QList<QSslError> &err){ //sslErrors() signal
}
void WebSocket::startBridgeAuth(){
SockPeerIP = SOCKET->peerAddress().toString();
LogManager::log(LogManager::HOST,"New Bridge Connection: "+SockPeerIP);
//qDebug() << "Init Bridge Auth...";
QJsonObject obj;
obj.insert("id", "server_to_bridge_auth");
@@ -637,9 +658,7 @@ void WebSocket::EventUpdate(EventWatcher::EVENT_TYPE evtype, QJsonValue msg){
for(int i=0; i<conns.length(); i++){
if( !BRIDGE[conns[i]].sendEvents.contains(evtype) ){ continue; }
//Encrypt the data with the proper key
QString key = BRIDGE[conns[i]].enc_key;
QString enc_data = raw;
if(!key.isEmpty()){ enc_data = AUTHSYSTEM->encryptString(raw, key); }
QString enc_data = AUTHSYSTEM->encryptString(raw, BRIDGE[conns[i]].enc_key);
//Now add the destination ID
enc_data.prepend( conns[i]+"\n");
this->emit SendMessage(enc_data);

View File

@@ -12,7 +12,8 @@
#include "AuthorizationManager.h"
struct bridge_data{
QString enc_key, auth_tok;
QByteArray enc_key;
QString auth_tok;
QList<EventWatcher::EVENT_TYPE> sendEvents;
};