C++ (17+) için kolay kullanımlı Symmetric (AES-256) ve Asymmetric (RSA) Encryption ve Hash (SHA-256) kütüphanesi
Github Repository: https://github.com/leventkaragol/libcpp-crypto
Amaç
Güvenli yazılım geliştirmenin kalbinde yer alan kriptografik işlemler, çoğu zaman karmaşıklıkla eş anlamlıdır. Bu alandaki endüstri standardı olan OpenSSL, sunduğu derinlemesine kontrol ve güvenilirliğin yanında, en temel şifreleme veya hash işlemleri için bile geliştiricileri çok adımlı, hataya açık ve karmaşık bir kodlama sürecine zorlayabilir. libcpp-crypto kütüphanesini, bu gücü erişilebilir kılma amacıyla hazırladım. OpenSSL'in kanıtlanmış kriptografik motorunu soyutlayarak, AES-256 ile simetrik şifreleme, RSA ile asimetrik şifreleme veya SHA-256 ile hash oluşturma gibi karmaşık görevleri, tek satır kod ile gerçekleştirebilen bir crypto kütüphanesi sunmaya çalıştım.
Projeye Nasıl Eklenir?
Bu header-only bir kütüphanedir. Yani aslında tek yapmanız gereken src klasöründeki libcpp-crypto.hpp dosyasını projenize eklemek ve #include ile kullanmaya başlamaktır.
Ancak bu kütüphane, arka planda OpenSSL'i kullanan bir wrapper olarak çalışır. Dolayısıyla, kullanmadan önce projenize OpenSSL kütüphanesini eklemeniz gerekir.
Kullanım örneklerini Github Repository'deki "examples" klasöründe bulabilirsiniz. Aşağıda örnek bir CMakeLists.txt dosyası içeriği de bulabilirsiniz.
cmake_minimum_required(VERSION 3.14)
project(myProject)
find_package(OpenSSL REQUIRED)
add_executable(myProject main.cpp libcpp-crypto.hpp)
target_link_libraries(myProject PRIVATE OpenSSL::SSL OpenSSL::Crypto)
Nasıl Kullanılır? (AES ile Simetrik Şifreleme)
Verilen metni AES-256 ile şifrelemek ve şifresini çözmek için, seçtiğiniz bir anahtarla "encryptWithAES" ve "decryptWithAES" fonksiyonlarını çağırmanız yeterlidir.
#include "libcpp-crypto.hpp"
using namespace lklibs;
int main() {
auto plainText = "Bu metin yakında şifrelenecek";
auto key = "benimGizliSifrem";
auto encryptedText = CryptoService::encryptWithAES(plainText, key);
std::cout << "Encrypted Text: " << encryptedText << std::endl;
auto decryptedText = CryptoService::decryptWithAES(encryptedText, key);
std::cout << "Decrypted Text: " << decryptedText << std::endl;
return 0;
}
İpucu!
Aslında, AES-256 ile kullanmanız gereken anahtar 32 karakter uzunluğunda olmalıdır. Ancak, kütüphane 32 karakterden kısa anahtarların sonuna yeterli miktarda "0" ekler ve 32 karakterden uzun anahtarların son kısımlarını yok sayarak istediğiniz anahtarı hatasız kullanmanıza olanak tanır.Nasıl Kullanılır? (SHA-256 ile Hash)
Verilen metni SHA-256 ile hashlemek için yapmanız gereken tek şey "hash" fonksiyonunu çağırmaktır.
#include "libcpp-crypto.hpp"
using namespace lklibs;
int main() {
auto plainText = "Bu metin yakında hash haline gelecek";
auto hashText = CryptoService::hash(plainText);
std::cout << "Hash: " << hashText << std::endl;
return 0;
}
Nasıl Kullanılır? (RSA ile Asimetrik Şifreleme)
Verilen metni RSA ile şifrelemek ve şifresini çözmek için tek yapmanız gereken public/private key çiftiyle "encryptWithRSA" ve "decryptWithRSA" fonksiyonlarını çağırmaktır.
İpucu!
Eğer public/private key çiftinin nasıl üretileceğini bilmiyorsanız lütfen bir sonraki başlığa bakın.
#include "libcpp-crypto.hpp"
using namespace lklibs;
int main() {
auto plainText = "Bu metin yakında şifrelenecek";
auto publicKey = "-----BEGIN PUBLIC KEY-----\n"
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwIASR0gIwizgv0j/Gzj6\n"
"E/gS1J6gwUXeDBND7c4rDdqh/NP78N6pWNKxa5YAytTAOsoqxLDRL29pq55HyRw5\n"
"47M35hwdmEfE8bOjnogvHRRKu7A2iGV7akkK0cP6XgHgcJVlXBX2xCT70nIX4dDk\n"
"vGhSKwrps1o+3XVhtnVoPsCDQEESApGalhQ55OT8s0fM7OTFMfqsV3GD9J9FO4wP\n"
"BlawHpQ5rbWGsyNYXnjXzGpmuyKl4xQBVdbx1tzh+1XlwqMhbXibMozo5U5De0oH\n"
"A9z1Owbt3++3t+LykQDcHEtiKcvYt71by1X3J2IQOBAwWJ2jRjZQ5QJWaGXirPdR\n"
"VwIDAQAB\n"
"-----END PUBLIC KEY-----";
auto privateKey = "-----BEGIN PRIVATE KEY-----\n"
"MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDAgBJHSAjCLOC/\n"
"SP8bOPoT+BLUnqDBRd4ME0PtzisN2qH80/vw3qlY0rFrlgDK1MA6yirEsNEvb2mr\n"
"nkfJHDnjszfmHB2YR8Txs6OeiC8dFEq7sDaIZXtqSQrRw/peAeBwlWVcFfbEJPvS\n"
"chfh0OS8aFIrCumzWj7ddWG2dWg+wINAQRICkZqWFDnk5PyzR8zs5MUx+qxXcYP0\n"
"n0U7jA8GVrAelDmttYazI1heeNfMama7IqXjFAFV1vHW3OH7VeXCoyFteJsyjOjl\n"
"TkN7SgcD3PU7Bu3f77e34vKRANwcS2Ipy9i3vVvLVfcnYhA4EDBYnaNGNlDlAlZo\n"
"ZeKs91FXAgMBAAECggEAKiO/HJiRxkQBvQ4TPlfWMsnjAWVqRnTve1A6VhQES8eZ\n"
"H1oedGehxb51tVoEeWJiZFw+SYl1eX9XsAh5qXZC2+wvJ/HurpfDbq/G+RzRx3la\n"
"NMUJ4wjoH+e2dR4EMFET20FxC1wJhX2dHL/6J2ZNtErX9fExIKB4U41vIvyHofis\n"
"ABjyPMWsoDnFgJS2dYlXqDE9du2gXMq+vxyAKFuG5iLjtoaDGiY6hUx6eSUAclHd\n"
"hRrmc1fftBWZUh1yr+yqH/VQGP0mVG3BIVvk7R75EST4+DxZcxxIE6NEOxFkS1Jc\n"
"jeVR6AsLz3+rkP1/K4SKD3HzQ+fNWYWulAAFsnChSQKBgQD0OgTGhpxP0X9k6NBA\n"
"urdZLGAePZaU+TUXwLL3+t6Upz7fJoh3bogkgbAUfjXDmrexXfIliMnm1KX9d4ZT\n"
"TB3VpDsjiYK82sLMs0adb+9jJqignX/nxoJdsWpamP2DJwAr3621nVNYLutzp4rn\n"
"dXKJYu1KtJdsQrsb/9mT87wD7wKBgQDJx7OxR992Ly8HB/GG9e+RCJVTHHwoYkaa\n"
"HkGFL/sxfANcWbqzJdd6qnNwUIK32GaY8F5pxo12zcpaVGLPk6J78HxQrnx6gQH/\n"
"KT3Bc9wT4M7nHIkt/Y879Z7BaiQ+2TafSdNWmobIB2H5X9zd0JoSs9gScbbaZ0kA\n"
"eW+KEtkBGQKBgDCEEgzKEuU8Td1i7nPdY6zgRtvbCj3f368vRZ5DhNHtA21a/0MS\n"
"fxMZfDwl8lJAOvuOGRthuBIV7j+S7elANrEJJgJiP8l6f7Ygawe9g8WjmV4Gy7Dk\n"
"W2N3ahRDTiEurzcIAT8R2MusznM0NkDSsQUf1NnBVE9aVkcypStiANibAoGAXWaT\n"
"Rvx987bjjd2fb1loCzpt5IrK7eaPx8c5jO0o2T8OTzE5urNJiv5bcSHTYEZLN4AM\n"
"M+o0kUmw4R8unec4zyYCZVZfSFVvFy1/6Iw40vq8yz3qQd+c7aREWENJg84H+rOx\n"
"n+Tnfq/sKgK1ufdVWlLlMaRxf6dPo2iSuNcAnAECgYActoBiupT5Kc2ZK/lCylJc\n"
"3UpJwB4dhuXypOjqvDQ6uyUSVbyD3TvneyB+8hYnPMXXTDtW6ne5pCMcxibtkK2T\n"
"Tmy283w8+RuGbK4+7ifgV97PUGrYFAmpqWhaympDCTp5WTwgKsmCDxX0B3bq77xW\n"
"oao1npvUquuvWx6cUaQN7Q==\n"
"-----END PRIVATE KEY-----";
auto encryptedText = CryptoService::encryptWithRSA(plainText, publicKey);
std::cout << "Encrypted Text: " << encryptedText << std::endl;
auto decryptedText = CryptoService::decryptWithRSA(encryptedText, privateKey);
std::cout << "Decrypted Text: " << decryptedText << std::endl;
return 0;
}
Public/Private Key Çiftini Nasıl Oluşturabilirim?
Public ve Private Key çifti oluşturmak için iki farklı seçeneğiniz var. İlk ve daha kolay olan seçenek, kütüphanedeki "generateRSAKeyPair" fonksiyonunu kullanarak istenen anahtar uzunluğunu parametre olarak geçmektir. Aşağıda bu kullanım için örnek bir kod bulunmaktadır.
auto keyPair = CryptoService::generateRSAKeyPair(2048);
std::cout << "2048 bit Public RSA Key:" << std::endl << keyPair.publicKey << std::endl;
std::cout << "2048 bit Private RSA Key:" << std::endl << keyPair.privateKey << std::endl;
İpucu!
İhtiyacınız olan anahtar uzunluğundan emin değilseniz lütfen bir sonraki konuya bakın.İsteğe bağlı olarak, anahtar oluşturma sırasında "generateRSAKeyPair" fonksiyonuna aşağıdaki gibi bir parola da geçirebilirsiniz. Bu durumda, metnin şifresini çözmek için bu parolayı "decryptWithRSA" fonksiyonuna geçirmeniz gerekecektir.
auto keyPair = CryptoService::generateRSAKeyPair(2048, "benimGizliSifrem");
std::cout << "2048 bit Public RSA Key:" << std::endl << keyPair.publicKey << std::endl;
std::cout << "2048 bit Private RSA Key:" << std::endl << keyPair.privateKey << std::endl;
İkinci seçenek olarak, işletim sisteminizde OpenSSL yüklüyse, komut satırından gerekli OpenSSL komutlarını kullanarak public ve private key çifti oluşturabilirsiniz. Bu seçeneğin ilk adımı olarak, komut satırına aşağıdaki satırı yazarak çalıştırdığınızda, özel anahtar bilgilerini içeren "private_key.pem" adlı bir metin dosyası oluşturulacaktır.
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
Daha sonra komut satırına aşağıdaki satırı yazarak çalıştırdığınızda bu özel anahtara ait açık anahtar bilgilerini içeren "public_key.pem" adında bir metin dosyası oluşturulacaktır.
openssl rsa -pubout -in private_key.pem -out public_key.pem
Anahtar Boyutuna Karar Vermek
RSA ile asimetrik şifreleme sırasında kullanılan Anahtarın boyutu yalnızca şifreleme işleminin güvenliğiyle ilgili olmakla kalmaz, aynı zamanda bu anahtarla şifrelenebilecek en uzun metnin ne kadar olabileceğini de belirler. Temel olarak, 2x bitlik bir anahtarla şifrelenebilecek en uzun metin, ASCII karakter kümesi için 2x-3-11 olarak hesaplanabilir. Diğer karakter kümeleri iki kat daha uzun olabilir. Hızlı referans olması için aşağıdaki tabloyu paylaşıyorum.
Anahtar Boyutu | Maksimum Metin Boyutu |
---|---|
2048 | 245 |
4096 | 501 |
8192 | 1013 |
16384 | 2037 |
32768 | 4085 |
65536 | 8181 |
İpucu!
Uzun bir metni RSA ile şifrelemek için kolayca daha uzun bir anahtar oluşturabileceğinizi düşünmeyin. Tablodaki her satır, şifreleme/şifre çözme işlemi sırasında yukarıdaki satıra göre 4 kat daha fazla CPU gücü tüketir. Ayrıca, 65K bitlik bir anahtar oluşturmak, üst düzey bir bilgisayar için bile oldukça zaman alır ve çok fazla sabır gerektirir. Uzun metinleri AES ile şifreleyip, şifreyi de RSA ile asimetrik şifrelemek genel kabul görmüş endüstri standardıdır.Dikkat!
1024 bitlik RSA anahtarları, günümüzün artan işlem gücü ve gelişmiş çarpanlara ayırma algoritmaları karşısında güvenli değildir. Lütfen en az 2048 bitlik anahtarlar kullanın.Hataları Nasıl Yakalarım? (AES)
Kütüphaneyi AES şifrelemesi için kullanırken karşılaşabileceğiniz iki temel istisna vardır. İlki, şifresini çözmek istediğiniz metnin şifreleme anahtarı yanlışsa alacağınız "InvalidKeyException" hatası, ikincisi ise şifresini çözmek istediğiniz metnin geçersiz olması durumunda alacağınız "CorruptedTextException" hatasıdır.
Aşağıdaki kod, geçersiz bir şifreleme anahtarı durumunda atılan istisnanın nasıl yakalanacağını gösterir.
#include "libcpp-crypto.hpp"
using namespace lklibs;
int main() {
auto plainText = "Bu metin yakında şifrelenecek";
auto key = "benimGizliSifrem";
auto invalidKey = "hataliSifre";
auto encryptedText = CryptoService::encryptWithAES(plainText, key);
try
{
auto decryptedText = CryptoService::decryptWithAES(encryptedText, invalidKey);
}
catch (const InvalidKeyException& e)
{
std::cerr << e.what() << std::endl; // Şifre çözme anahtarı hatalı
}
return 0;
}
Aşağıdaki kod ayrıca geçersiz bir metni şifresini çözmeye çalıştığınızda oluşan istisnayı nasıl yakalayacağınızı da gösterir.
#include "libcpp-crypto.hpp"
using namespace lklibs;
int main() {
auto encryptedText = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
auto key = "benimGizliSifrem";
try
{
auto decryptedText = CryptoService::decryptWithAES(encryptedText, key);
}
catch (const CorruptedTextException& e)
{
std::cerr << e.what() << std::endl; // Şifreli metin geçersiz
}
return 0;
}
Hataları Nasıl Yakalarım? (RSA)
RSA tarafı için istisna kısmı biraz farklıdır. Kullanılan public ve private key doğru değilse, "InvalidPublicKeyException" ve "InvalidPrivateKeyException" hataları oluşur. Ancak, bu istisnaların oluşması için kullanılan anahtarların yapısının bozuk olması gerekir. Uyumsuz ancak yapısal olarak geçerli anahtarlar kullanırsanız, herhangi bir istisna oluşmaz. Ancak, şifre çözme işleminden sonra elde edilen metin anlamsız karakterlerden oluşacaktır.
#include "libcpp-crypto.hpp"
using namespace lklibs;
int main() {
auto plainText = "Bu metin yakında şifrelenecek";
auto publicKey = "-----BEGIN PUBLIC KEY-----\n"
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwIASR0gIwizgv0j/Gzj6\n"
"E/gS1J6gwUXeDBND7c4rDdqh/NP78N6pWNKxa5YAytTAOsoqxLDRL29pq55HyRw5\n"
"47M35hwdmEfE8bOjnogvHRRKu7A2iGV7akkK0cP6XgHgcJVlXBX2xCT70nIX4dDk\n"
"vGhSKwrps1o+3XVhtnVoPsCDQEESApGalhQ55OT8s0fM7OTFMfqsV3GD9J9FO4wP\n"
"BlawHpQ5rbWGsyNYXnjXzGpmuyKl4xQBVdbx1tzh+1XlwqMhbXibMozo5U5De0oH\n"
"A9z1Owbt3++3t+LykQDcHEtiKcvYt71by1X3J2IQOBAwWJ2jRjZQ5QJWaGXirPdR\n"
"VwIDAQAB\n"
"-----END PUBLIC KEY-----";
auto privateKey = "-----BEGIN PRIVATE KEY-----\n"
"SOME_INVALID_KEY/\n"
"-----END PRIVATE KEY-----";
auto encryptedText = CryptoService::encryptWithRSA(plainText, publicKey);
try
{
auto decryptedText = CryptoService::decryptWithRSA(encryptedText, privateKey);
}
catch (const InvalidPrivateKeyException& e)
{
std::cerr << e.what() << std::endl; // RSA private key hatalı
}
return 0;
}
Semantik Versiyonlama
Kütüphanenin sürümlemesi, geleneksel semantik versiyonlama kullanılarak yapılır. Buna göre, MAJOR.MINOR.PATCH biçiminde yapılan sürümlemede;
PATCH: Olası hata düzeltmeleri ve iyileştirmeleri içerir. Bunu mutlaka edinmek istersiniz.
MINOR: Geriye dönük uyumluluk sayesinde eklenen ek işlevler. Muhtemelen bunu edinmek istersiniz, zararı olmaz.
MAJOR: Geriye dönük uyumluluğu bozan ek işlevler. Nelerin değiştiğini anlamanız ve muhtemelen kendi kodunuzda değişiklik yapmanız gerekecek. Böyle bir şey yayınlarsam, geçiş için gereken değişiklikleri kesinlikle dokümana eklerim.
Tam Fonksiyon Listesi
Kütüphanedeki fonksiyonların tam listesini aşağıda bulabilirsiniz.
İpucu!
Tüm metot ve parametre açıklamaları IDE'ler için yorum olarak kod içerisinde de mevcuttur.
std::string encryptWithAES(const std::string& plaintext, const std::string& key);
std::string decryptWithAES(const std::string& ciphertext, const std::string& key);
RSAKeyPair generateRSAKeyPair(int keyLength, const std::string& passphrase = "");
std::string encryptWithRSA(const std::string& plaintext, const std::string& publicKeyStr);
std::string decryptWithRSA(const std::string& ciphertext, const std::string& privateKeyStr);
std::string hash(const std::string& text);
Lisans
Proje MIT lisansı ile lisanslanmıştır. Ticari kullanıma uygundur. Tam metni aşağıda bulabilirsiniz.
MIT License
Copyright (c) 2024 Levent KARAGÖL
Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies
or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
İletişim
Kütüphaneyle ilgili sorun yaşıyorsanız, lütfen GitHub'da bir konu açın. Lütfen isteğinizi, sorununuzu veya sorunuzu olabildiğince ayrıntılı bir şekilde açıklayın ve derleyicinizin, işletim sisteminizin ve kullandığınız kütüphanenin sürümünü de belirtin. Yeni bir konu açmadan önce, lütfen konunun daha önce kapatılmış konularda bulunmadığından emin olun.
Yazar: Levent KARAGÖL