summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Suhren <suhren.stefan@fh-swf.de>2015-09-19 00:41:04 +0200
committerStefan Suhren <suhren.stefan@fh-swf.de>2015-09-19 01:46:23 +0200
commit0a914f5edc5a5981de82c3f63c7ae5061dc6b966 (patch)
tree10aafe542b06d6236558ce6b8c44548418e11abc
parentaac2957ab6f98d087f8fb9ff68da39fe68c40e3d (diff)
downloadsrc-0a914f5edc5a5981de82c3f63c7ae5061dc6b966.tar.gz
src-0a914f5edc5a5981de82c3f63c7ae5061dc6b966.zip
Implementiere erste Version von RSA für PEM Keys
-rw-r--r--crypt/cryptexception.h3
-rw-r--r--crypt/hybridcrypt.cpp208
-rw-r--r--crypt/hybridcrypt.h18
3 files changed, 228 insertions, 1 deletions
diff --git a/crypt/cryptexception.h b/crypt/cryptexception.h
index 768e144..513fb8d 100644
--- a/crypt/cryptexception.h
+++ b/crypt/cryptexception.h
@@ -20,7 +20,8 @@ public:
OpenSslError,
KeyNotRsa,
CsprngNotSeeded,
- NoUserKeyCreated
+ NoUserKeyCreated,
+ NoRecipients
};
/**
diff --git a/crypt/hybridcrypt.cpp b/crypt/hybridcrypt.cpp
index 0d56038..63e241e 100644
--- a/crypt/hybridcrypt.cpp
+++ b/crypt/hybridcrypt.cpp
@@ -42,6 +42,142 @@ HybridCrypt::~HybridCrypt()
void HybridCrypt::encrypt(QString infileName, QString outfileName,
QVector<QString> recipientKeyfileNames)
{
+ if (recipientKeyfileNames.length() < 1)
+ {
+ throw CryptException("Keine Empfänger wurden angegeben.",
+ CryptException::NoRecipients);
+ }
+
+ throwExceptionIfCsprngIsNotSeeded();
+
+ QFile infile(infileName);
+ QFile outfile(outfileName);
+
+ if (!infile.open(QFile::ReadOnly))
+ {
+ throw CryptException("Datei nicht gefunden: " + infileName.toStdString(),
+ CryptException::FileNotFound);
+ }
+
+ if (!outfile.open(QFile::ReadWrite | QFile::Truncate))
+ {
+ throw CryptException("Konnte Datei nicht schreiben: " +
+ outfileName.toStdString(), CryptException::FileNotWritable);
+ }
+
+ QDataStream infileStream(&infile);
+ QDataStream outfileStream(&outfile);
+
+ QByteArray aesKey = getCsprngBytes(EVP_CIPHER_key_length(EVP_aes_256_cbc()));
+ QByteArray aesIv = getCsprngBytes(EVP_CIPHER_iv_length(EVP_aes_256_cbc()));
+
+ int keyCount = recipientKeyfileNames.length();
+
+ // Nur wenn ein Nutzerschlüssel existiert wird er auch für die Verschlüsselung verwendet
+ if (userKeypair != NULL)
+ {
+ ++keyCount;
+ }
+
+ // Schreibe die Anzahl der PublicKeys als erstes in die Datei
+ outfileStream << (qint64) keyCount;
+
+ EVP_PKEY *tmpKey;
+ QByteArray tmpData;
+ FILE *tmpKeyfile;
+
+ for (int i = 0; i < recipientKeyfileNames.length(); i++)
+ {
+ tmpKeyfile = fopen(recipientKeyfileNames[i].toStdString().c_str(), "r");
+
+ // Datei existiert nicht
+ if (tmpKeyfile == NULL)
+ {
+ throw CryptException("Datei nicht gefunden: " +
+ recipientKeyfileNames[i].toStdString(), CryptException::FileNotFound);
+ }
+
+ // Ließ den aktuellen Empfängerschlüssel ein
+ tmpKey = PEM_read_PUBKEY(tmpKeyfile, NULL, NULL, NULL);
+
+ // Räume die Ressourcen auf
+ fclose(tmpKeyfile);
+
+ // Wirf Fehler, falls der Schlüssel nicht gelesen werden konnte
+ if (tmpKey == NULL)
+ {
+ throwOpenSslException();
+ }
+
+ // Schreibe den Header in die Datei
+ tmpData = encryptAesData(tmpKey, aesKey + aesIv);
+ outfileStream.writeRawData(tmpData.data(), tmpData.length());
+
+ // Räume Schlüssel auf
+ freeEvpKey(&tmpKey);
+ }
+
+ // Damit der Nutzer selbst die Datei auch entschlüsseln kann, falls er einen Schlüssel hat
+ if (userKeypair != NULL)
+ {
+ tmpData = encryptAesData(userKeypair, aesKey + aesIv);
+ outfileStream.writeRawData(tmpData.data(), tmpData.length());
+ }
+
+ // Erzeuge den symmetrischen Kontext
+ EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+
+ // Überprüfe den Schlüssel
+ if (!ctx)
+ {
+ throwOpenSslException();
+ }
+
+ // Setze den Verschlüsselungsalgorithmus, sowie den Schlüssel und den IV
+ if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL,
+ (unsigned char *) aesKey.data(), (unsigned char *) aesIv.data()))
+ {
+ // Räume den symmetrischen Schlüsselkontext ab
+ freeCipherCtx(&ctx);
+ throwOpenSslException();
+ }
+
+ QByteArray inBuf(EVP_CIPHER_block_size(EVP_aes_256_cbc()), 0);
+ QByteArray outBuf(EVP_CIPHER_block_size(EVP_aes_256_cbc()), 0);
+ int bufRead;
+ int len;
+
+ while (!infileStream.atEnd())
+ {
+ // Hole jeweils einen Block an Daten aus der Datei
+ bufRead = infileStream.readRawData(inBuf.data(), inBuf.size());
+
+ // Verschlüssele den Block mit AES
+ if (1 != EVP_EncryptUpdate(ctx, (unsigned char *) outBuf.data(), &len,
+ (unsigned char *) inBuf.data(), bufRead))
+ {
+ // Räume den symmetrischen Schlüsselkontext ab
+ freeCipherCtx(&ctx);
+ throwOpenSslException();
+ }
+
+ // Schreibe den Block in die Ausgabedatei
+ outfileStream.writeRawData(outBuf.data(), len);
+ }
+
+ // Nun bekommen wir den letzten Block, falls die Datei nicht ein Vielfaches der AES Blöckgröße war
+ if (1 != EVP_EncryptFinal_ex(ctx, (unsigned char *)outBuf.data(),
+ &len))
+ {
+ // Räume den symmetrischen Schlüsselkontext ab
+ freeCipherCtx(&ctx);
+ throwOpenSslException();
+ }
+
+ outfileStream.writeRawData(outBuf.data(), len);
+
+ // Räume den symmetrischen Schlüsselkontext ab
+ freeCipherCtx(&ctx);
}
@@ -182,11 +318,83 @@ void HybridCrypt::exportPublicUserKey(QString keyfileName)
* Private Funktionen
*/
+QByteArray HybridCrypt::encryptAesData(EVP_PKEY *pkey, QByteArray data)
+{
+ // Das QByteArray das zurück gegeben wird
+ QByteArray out;
+
+ // Wird benutzt um die Länge von out zu bestimmen
+ size_t outlen;
+
+ // Lege Schlüsselkontext an
+ EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
+
+ // Überprüfe erflogreiche Schlüsselkontexterzeugung
+ if (!ctx)
+ {
+ throwOpenSslException();
+ }
+
+ // Initialisiere den Schlüsselkontext
+ if (EVP_PKEY_encrypt_init(ctx) <= 0)
+ {
+ // Räume den Schlüsselkontext ab
+ freePkeyCtx(&ctx);
+ throwOpenSslException();
+ }
+
+ // Setze für den Schlüsselkontext das RSA Padding
+ if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0)
+ {
+ // Räume den Schlüsselkontext ab
+ freePkeyCtx(&ctx);
+ throwOpenSslException();
+ }
+
+ // Finde die Blockgröße für RSA raus
+ if (EVP_PKEY_encrypt(ctx, NULL, &outlen, (unsigned char *) data.data(),
+ data.length()) <= 0)
+ {
+ // Räume den Schlüsselkontext ab
+ freePkeyCtx(&ctx);
+ throwOpenSslException();
+ }
+
+ // Setze out auf RSA Blockgröße
+ out.resize(outlen);
+
+ // Verschlüssele den AES Key und den IV
+ if (EVP_PKEY_encrypt(ctx, (unsigned char *) out.data(), &outlen,
+ (unsigned char *) data.data(), data.length()) <= 0)
+ {
+ // Räume den Schlüsselkontext ab
+ freePkeyCtx(&ctx);
+ throwOpenSslException();
+ }
+
+ // Räume den Schlüsselkontext ab
+ freePkeyCtx(&ctx);
+
+ return out;
+}
+
bool HybridCrypt::isCsprngSeeded()
{
return RAND_status() == 1;
}
+QByteArray HybridCrypt::getCsprngBytes(int count)
+{
+ QByteArray out(count, 0);
+
+ if (1 != RAND_bytes((unsigned char *) out.data(), count))
+ {
+ throwOpenSslException();
+ }
+
+ return out;
+}
+
bool HybridCrypt::isKeyRsa(EVP_PKEY *key)
{
return EVP_PKEY_type(key->type) == EVP_PKEY_RSA;
diff --git a/crypt/hybridcrypt.h b/crypt/hybridcrypt.h
index ce8a553..6cf92df 100644
--- a/crypt/hybridcrypt.h
+++ b/crypt/hybridcrypt.h
@@ -90,6 +90,16 @@ private:
EVP_PKEY *userKeypair; // Enthält nur den privaten Schlüssel, da OpenSSL nicht mehr braucht.
/**
+ * @brief encryptAesData
+ * Verschlüsselt den IV und den Key vom AES mit RSA.
+ * Und nutzt als Padding RSA_OAEP_PADDING.
+ * @param pkey Der EVP_PKEY mit dem Verschlüsselt wird.
+ * @param data Der AES Key und IV.
+ * @return Den RSA Verschlüsselten Block.
+ */
+ QByteArray encryptAesData(EVP_PKEY *pkey, QByteArray data);
+
+ /**
* @brief isCsprngSeeded
* Gibt an, ob der Zufallszahlengenerator von OpenSSL mit ausreichend Entropie initialisiert wurde.
* @return Gibt wahr zurück wenn ausreichend intialisert wurde, ansonsten falsch.
@@ -97,6 +107,14 @@ private:
bool isCsprngSeeded();
/**
+ * @brief getCsprngData
+ * Holt Zufallsblöcke aus dem Csprng.
+ * @param count Die Anzahl der Zufallsbytes die geholt werden sollen.
+ * @return Ein QByteArray mit den Zufallsblöcken.
+ */
+ QByteArray getCsprngBytes(int count);
+
+ /**
* @brief isKeyRsa
* Überprüft, ob der Schlüssel vom Typ RSA ist.
* @param key Der Schlüssel, der überprüft wird.