CELIB::RSA class has a rich interface which can used for many operations, such as generating keys, encrypt, decrypt, sign, ...ect.
Create RSA object:
There are many Constructors that can used to create rsa object, sipmle one is create rsa object with key size, constructor will generate keys (private & public) with specific size:
For more security, CryptoEngine uses X917RNG standard - from ANSI 9.17 - (if available) to generate keys.
CELIB::RSA rsa(1024); //! connect rsa object with caller object to catch errors (see Error Handling page): //connect(&rsa, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
Note that NIST recommend to use key with size 3072 bits or higher for more security.
We can print-out object's data in (human-readable) form:
std::cout << "\nrsa.toString:\n" << rsa.toString().toStdString() << std::endl;
output:
rsa.toString: ([CELIB::RSA::toString()]: [Size(Bytes):[128], n-Hex: [8A592CCC3C9278D9EE247AAC25BE44DAA7CBD504191A1FAF8BF71B7A54A030DAF4C062798B8CBBFA2E9E7F3EC056F7884358FC0ADB12EE9BE388B3D835087BCB601D918FB308A8443E7F5EDAE5EC41CAC40E511AB7C180AD139C2B14AF0F36AAC42F8CAF9F56FF042DBB7A759DCFAE46E0FCCDA8509734CA8C5E230CD55758D1], e-Hex:[11].] d:[!HIDDEN!].)
Encrypt/Decrypt:
QByteArray plain("RSA Example"), cipher, recover, signature; bool isOk(false); std::cout << "\nPlain: " << plain.data() << std::endl; cipher = rsa.encrypt(plain, &isOk); if(isOk) std::cout << "\nEncryption successful." << std::endl; else std::cout << "\nEncryption FAILED." << std::endl; std::cout << "\nCipher-Hex:\n" << cipher.toHex().toUpper().data() << std::endl; recover = rsa.decrypt(cipher, &isOk); if(isOk) std::cout << "\nDecryption successful." << std::endl; else std::cout << "\nDecryption FAILED." << std::endl; std::cout << "\nRecover: " << recover.data() << std::endl; Q_ASSERT(plain == recover);
output:
Plain: RSA Example Encryption successful. Cipher-Hex: 4EBF69EBDD5AA1F562176BD94B2A17A48FA6EB69A639D5EB03E6F097355A892B3E860A6FD1F8D88A848C2135A3239DFB67B44EA6019C43105FC22090D1B603AA7048A17322016289BE2299B3019F7D023D79BEBA550B724225ABD97D16183596F480175043A4378A77AB8F4A34ECF65EBA89B86D76FFE718D9D9A6B895DE30EC Decryption successful. Recover: RSA Example
Sign/Verify:
We can use rsa object for generating signatur of a plaintext and we can verify it later, we must assign the Hash Algorithm to use when signing; supported algorithms are SHA1, SHA2 and SHA3, default is SHA2-512:
signature = rsa.sign(plain, &isOk, CELIB::SHT_SHA2_256); if(isOk) std::cout << "\nSigning successful." << std::endl; else std::cout << "\nSigning FAILED." << std::endl; std::cout << "\nSignature-Hex:\n" << signature.toHex().toUpper().data() << std::endl; //! Attack first & last bytes: //signature[0] = 'A'; //signature[signature.size()-1] = 'A'; isOk = rsa.Verify(signature, plain, CELIB::SHT_SHA2_256); if(isOk) std::cout << "\nVerfying successful." << std::endl; else std::cout << "\nVerfying FAILED." << std::endl;
output:
Signing successful. Signature-Hex: 04FAD1A8A4C19683AAE2EEB3E8524D8294AB2A97A39EC7F7B6E8A39B436AE7FC8334143BB484F83D701E3EBA473A420A2B4E2FBE5FF2C78FB01ED216C0691B9726CC7AEB2470B9D7508EBAF4FE977E56B4BE4AEF0665DD17B5746795BE7BCE96DCCC87396C164639AAAC4339D2697D95E2A280F10CBCB8BE7E956F0D7CAE429B Verfying successful.
Signing with recovery
RSA Algorithm can used for generating a signature that can used later for recover plaintext:
signature = rsa.signWithRecovery(plain, &isOk, CELIB::SHT_SHA3_224); if(isOk) std::cout << "\nSigning-with-recovery successful." << std::endl; else std::cout << "\nSigning-with-recovery FAILED." << std::endl; std::cout << "\nSignature-Hex:\n" << signature.toHex().toUpper().data() << std::endl; //! Attack first & last bytes: //signature[0] = 'A'; //signature[signature.size()-1] = 'A'; recover = rsa.recoveryFromSignature(signature, &isOk, CELIB::SHT_SHA3_224); if(isOk) std::cout << "\nRecover-from-signature successful." << std::endl; else std::cout << "\nRecover-from-signature FAILED." << std::endl; std::cout << "\nRecover: " << recover.data() << std::endl; Q_ASSERT(plain == recover);
output:
Signing-with-recovery successful. Signature-Hex: 49FA280E14629327412048456D546A0EA5752000F057791FAF159D74948EAFA348F84E299B059FA18CC2CFD5000EF84BC6F188A71CBD7FBFD2B37E4379DE20373F14D39D764388F23836DBD84C1F0B1CE33C663DB796B7FA67FD3BFFCECA06E2A3F04D823E338D2E203875C685D71C367A255C672083FAF8AB8677562BE99622 Recover-from-signature successful. Recover: RSA Example
Save/Load Keys
If we need to save keys (private or public keys) to files, or if we need to load existing key file:
Note that private key is saved using (PKCS8) encoding, and public key is saved using (X509) encoding.
//create another RSA object to load keys: CELIB::RSA rsa2; //Load both keys together: isOk = rsa2.loadKeys(privKeyFile, pubKeyFile); /* Or load each key alone: isOk = rsa2.loadPrivateKey(privKeyFile); isOk = rsa2.loadPublicKey(pubKeyFile); */ if(isOk) std::cout << "\nKey load successful." << std::endl; else std::cout << "\nKey load FAILED." << std::endl; Q_ASSERT(rsa == rsa2);
We can also change keys private & public, at any time we need-to, using setKeys(...) functions.
Static functions:
For more flexibility, CELIB::RSA provide many Static Functions that can do all above operations, plus another operations, such as generating and validating keys ...etc.