diff --git a/src/Cryptographic.cpp b/src/Cryptographic.cpp
new file mode 100644
index 0000000..92268e3
--- /dev/null
+++ b/src/Cryptographic.cpp
@@ -0,0 +1,91 @@
+ /*
+ * Copyright 2020, WindTerm.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Cryptographic.h"
+
+#include <QByteArray>
+#include <qpassworddigestor.h>
+#include <QRandomGenerator>
+
+#include "openssl/evp.h"
+
+constexpr int AES_256_IV_LENGTH			= 16;
+constexpr int AES_256_KEY_LENGTH		= 32;
+constexpr int PBKDF2_LENGTH				= AES_256_IV_LENGTH + AES_256_KEY_LENGTH;
+constexpr int PBKDF2_ITERATION_COUNT	= 100000;
+
+QByteArray Cryptographic::decrypt(const QByteArray &data, const QByteArray &pbkdf2) {
+	return doCrypt(QByteArray::fromBase64(data), Cryptographic::key(pbkdf2), Cryptographic::iv(pbkdf2), 0);
+}
+
+QByteArray Cryptographic::doCrypt(QByteArray data, const QByteArray &key, const QByteArray &iv, int enc) {
+	QByteArray crypted(data.length() + EVP_MAX_BLOCK_LENGTH, '\0');
+	int cryptedLength = 0;
+	int length;
+
+	EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+
+	EVP_CipherInit_ex(
+		ctx,
+		EVP_aes_256_cbc(),
+		nullptr,
+		reinterpret_cast<const uchar *>(key.constData()),
+		reinterpret_cast<const uchar *>(iv.constData()),
+		enc
+	);
+
+	EVP_CipherUpdate(
+		ctx,
+		reinterpret_cast<uchar *>(crypted.data()),
+		&length,
+		reinterpret_cast<const uchar *>(data.constData()),
+		data.length()
+	);
+	cryptedLength += length;
+
+	EVP_CipherFinal_ex(ctx, reinterpret_cast<uchar *>(crypted.data() + cryptedLength), &length);
+	cryptedLength += length;
+
+	EVP_CIPHER_CTX_free(ctx);
+
+	crypted.resize(cryptedLength);
+	return crypted;
+}
+
+QByteArray Cryptographic::encrypt(const QByteArray &data, const QByteArray &pbkdf2) {
+	QByteArray encrypted = doCrypt(data, Cryptographic::key(pbkdf2), Cryptographic::iv(pbkdf2), 1);
+	return encrypted.toBase64();
+}
+
+QByteArray Cryptographic::iv(const QByteArray &pbkdf2) {
+	Q_ASSERT(pbkdf2.length() == PBKDF2_LENGTH);
+	return QByteArray::fromRawData(pbkdf2.constData() + AES_256_KEY_LENGTH, AES_256_IV_LENGTH);
+}
+
+QByteArray Cryptographic::key(const QByteArray &pbkdf2) {
+	Q_ASSERT(pbkdf2.length() == PBKDF2_LENGTH);
+	return QByteArray::fromRawData(pbkdf2.constData(), AES_256_KEY_LENGTH);
+}
+
+QByteArray Cryptographic::pbkdf2(const QByteArray &password, const QByteArray &salt) {
+	return QPasswordDigestor::deriveKeyPbkdf2(QCryptographicHash::Sha3_512, password, salt, PBKDF2_ITERATION_COUNT, PBKDF2_LENGTH);
+}
+
+QByteArray Cryptographic::salt() {
+	QByteArray number = QByteArray::number(QRandomGenerator::system()->generate64());
+	QByteArray numberHash = QCryptographicHash::hash(number, QCryptographicHash::Sha512);
+	return numberHash.toBase64();
+}
diff --git a/src/Cryptographic.h b/src/Cryptographic.h
new file mode 100644
index 0000000..8a70fea
--- /dev/null
+++ b/src/Cryptographic.h
@@ -0,0 +1,39 @@
+ /*
+ * Copyright 2020, WindTerm.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CRYPTOGRAPHIC_H
+#define CRYPTOGRAPHIC_H
+
+#pragma once
+
+class QByteArray;
+
+class Cryptographic {
+public:
+	Cryptographic() = default;
+
+	static QByteArray decrypt(const QByteArray &data, const QByteArray &pbkdf2);
+	static QByteArray encrypt(const QByteArray &data, const QByteArray &pbkdf2);
+	static QByteArray pbkdf2(const QByteArray &password, const QByteArray &salt);
+	static QByteArray salt();
+
+private:
+	static QByteArray doCrypt(QByteArray data, const QByteArray &key, const QByteArray &iv, int enc);
+	static QByteArray iv(const QByteArray &pbkdf2);
+	static QByteArray key(const QByteArray &pbkdf2);
+};
+
+#endif // CRYPTOGRAPHIC_H
\ No newline at end of file
diff --git a/src/README.md b/src/README.md
index 7b38162..9cba008 100644
--- a/src/README.md
+++ b/src/README.md
@@ -8,6 +8,10 @@ Below is a list of (some) WindTerm components in alphabetical order, along with
 
 A quick circular buffer template class.
 
+## Cryptographic.h/cpp
+
+A very safe encryption class using the PBKDF2-algorithm as defined in RFC 8018. WindTerm uses this class together with the user's master password to protect user data, including passwords, private keys and so on.
+
 ## Onigmo
 
 An improved version based on Onigmo 5.13.5. In particular, **the addition of iterator makes it possible to match gap buffer or nonadjacent memory blocks.** Please refer to the sample files for how to use.