How can I load a private / public key from a string / byte array or any other container

Is it possible to store the RSA private or public key in the source, for example, in byte[] or string or any other container and use this key for encryption / decryption?

The decoding function from the file will look like this:

 void Decode(const string& filename, BufferedTransformation& bt) { // http://www.cryptopp.com/docs/ref/class_file_source.html FileSource file(filename.c_str(), true /*pumpAll*/); file.TransferTo(bt); bt.MessageEnd(); } 

This loads the key from a file, which is not what I want.

I know this should be possible since I can create a key with AutoSeededRandomPool .

I just don't know how to use the existing one.

Perhaps I skipped this part in the documentation.

+5
source share
4 answers

Crypto ++ Keys and Formats and Crypto ++ RSA Cryptography pages may be of interest.

If you create RSA parameters as follows:

 AutoSeededRandomPool rng; InvertibleRSAFunction params; params.GenerateRandomWithKeySize(rng, 2048); 

You can use the DEREncode and BERDecode InvertibleRSAFunction to encode and decode all parameters, respectively:

 { FileSink output("rsaparams.dat"); params.DEREncode(output); } InvertibleRSAFunction params2; { FileSource input("rsaparams.dat", true); params2.BERDecode(input); } 

To encode / decode private and public material, separately use the DEREncode and BERDecode for RSA::PrivateKey and RSA::PublicKey objects:

 // Initialize keys from generated params RSA::PrivateKey rsaPrivate(params); RSA::PublicKey rsaPublic(params); // Write keys to file { FileSink output("rsaprivate.dat"); rsaPrivate.DEREncode(output); } { FileSink output("rsapublic.dat"); rsaPublic.DEREncode(output); } // Read keys from file into new objects RSA::PrivateKey rsaPrivate2; RSA::PublicKey rsaPublic2; { FileSource input("rsaprivate.dat", true); rsaPrivate2.BERDecode(input); } { FileSource input("rsapublic.dat", true); rsaPublic2.BERDecode(input); } 

FileSource and FileSink are just examples of source and stream objects that you could use. Encoding / decoding routines accept BufferedTransformation objects as parameters, so you can use any other suitable implementation of this interface.

For example, ArraySink can be used to write data to the memory buffer that you supply, and StringSource (also an alias like ArraySource ) can be used to read from the buffer.

Here's some code showing the use of ArraySink and ArraySource to round off private key material via std::vector<byte> :

 RSA::PrivateKey rsaPrivate(params); std::vector<byte> buffer(8192 /* buffer size */); ArraySink arraySink(&buffer[0], buffer.size()); rsaPrivate.DEREncode(arraySink); // Initialize variable with the encoded key material // (excluding unwritten bytes at the end of our buffer object) std::vector<byte> rsaPrivateMaterial( &buffer[0], &buffer[0] + arraySink.TotalPutLength()); RSA::PrivateKey rsaPrivate2; ArraySource arraySource( &rsaPrivateMaterial[0], rsaPrivateMaterial.size(), true); rsaPrivate2.BERDecode(arraySource); 

(see also @jww answer for an example that avoids a fixed-size buffer using ByteQueue ).

And another example of using std::string to store key material and using the StringSink class to write to it, which avoids some buffer management (the string will be automatically changed according to the amount of data that is encoded). Note that this is still binary data, even if it is in the std::string object.

 RSA::PrivateKey rsaPrivate(params); std::string rsaPrivateMaterial; StringSink stringSink(rsaPrivateMaterial); rsaPrivate.DEREncode(stringSink); RSA::PrivateKey rsaPrivate2; StringSource stringSource(rsaPrivateMaterial, true); rsaPrivate2.BERDecode(stringSource); 

Alternatively, if you want to control the format yourself, you can use the methods of the InvertibleRSAFunction object or key objects to extract individual parameters (as shown in the "Crypto ++ RSA Cryptography" link above) and use this to retrieve the values ​​to store in your own format :

 const Integer& n = params.GetModulus(); const Integer& p = params.GetPrime1(); const Integer& q = params.GetPrime2(); const Integer& d = params.GetPrivateExponent(); const Integer& e = params.GetPublicExponent(); 

Then they can be restored to a new InvertibleRSAFunction or RSA::*Key instance when reading from a file or container using the appropriate configuration methods ( SetModulus() , SetPrime1() , etc.).

+9
source

How can I load a private / public key from a string / byte array or any other container

The Crypto ++ library has built-in support for std:strings . But other C ++ containers will be more complicated. ArraySource was added in Crypto ++ 5.6, but its just a typedef for StringSource .

If you use sensitive material, you should also consider using SecByteBlock . It will destroy or nullify sensitive material when the destructor is running.

string and StringSource (download)

 string spki = ...; StringSource ss(spki, true /*pumpAll*/); RSA::PublicKey publicKey; publicKey.Load(ss); 

and ArraySource (load)

 vector<byte> spki = ...; ArraySource as(&spki[0], spki.length(), true /*pumpAll*/); RSA::PublicKey publicKey; publicKey.Load(as); 

string and StringSink (save)

 string spki; StringSink ss(spki); RSA::PublicKey publicKey(...); publicKey.Save(ss); 

vector (save)

See the code below.


Below is an example of saving and loading with std::vector . You must use an intermediate ByteQueue to save, because you cannot easily create a VectorSink .

 AutoSeededRandomPool prng; RSA::PrivateKey pk1, pk2; pk1.Initialize(prng, 1024); ByteQueue queue; pk1.Save(queue); vector<byte> spki; spki.resize(queue.MaxRetrievable()); ArraySink as1(&spki[0], spki.size()); queue.CopyTo(as1); ArraySource as2(&spki[0], spki.size(), true); pk2.Load(as2); bool valid = pk2.Validate(prng, 3); if(valid) cout << "Validated private key" << endl; else cout << "Failed to validate private key" << endl; 

We do not have an explicit VectorSink , and we cannot easily create it because of the implicit expectation of traits_type::char_type . For instance:

 using CryptoPP::StringSinkTemplate; typedef StringSinkTemplate< std::vector<byte> > VectorSink; In file included from cryptopp-test.cpp:65: In file included from /usr/local/include/cryptopp/files.h:5: /usr/local/include/cryptopp/filters.h:590:22: error: no member named 'traits_type' in 'std::vector<unsigned char, std::allocator<unsigned char> >' typedef typename T::traits_type::char_type char_type; ~~~^ cryptopp-test.cpp:243:20: note: in instantiation of template class 'CryptoPP::StringSinkTemplate<std::vector<unsigned char, std::allocator<unsigned char> > >' requested here VectorSink vs(spki); 

You can create VectorSource and VectorSink , and it just VectorSink . You can get an idea of ​​the work related to the source code of StringSource and StringSink in filters.h and filters.cpp .

+3
source

If you create DSA keys as follows, you will get two files, one of which contains the private key, and the other is the public key.

 void CreateDsaKeys(std::string folder) { AutoSeededRandomPool rng; // Generate Private Key DSA::PrivateKey privateKey; privateKey.GenerateRandomWithKeySize(rng, 1024); // Generate Public Key DSA::PublicKey publicKey; publicKey.AssignFrom(privateKey); if (!privateKey.Validate(rng, 3) || !publicKey.Validate(rng, 3)) { throw runtime_error("DSA key generation failed"); } std::string publicPath = folder + "/publickey.txt"; std::string privatePath = folder + "/privatekey.txt"; SaveHexPublicKey(publicPath, publicKey); SaveHexPrivateKey(privatePath, privateKey); } 

Copy the contents of these two files into the source code and put them in the lines:

std :: string publickey ("308201B73082012C ... F752BB791");

std :: string privatekey ("3082014C0201003 ... 0B8E805D83E9708");

Then you can use HexDecoder to convert strings to bytes and create public and private keys using these bytes:

 bool LoadDsaKeysFromStringsAndTest() { AutoSeededRandomPool rng; HexDecoder decoderPublic; decoderPublic.Put((byte*)publickey.data(), publickey.size()); decoderPublic.MessageEnd(); HexDecoder decoderPrivate; decoderPrivate.Put((byte*)privatekey.data(), privatekey.size()); decoderPrivate.MessageEnd(); DSA::PublicKey publicKey; publicKey.Load(decoderPublic); DSA::PrivateKey privateKey; privateKey.Load(decoderPrivate); string message = "DSA Signature"; string signature; try { DSA::Signer signer( privateKey ); StringSource ss1( message, true, new SignerFilter( rng, signer, new StringSink( signature ) ) // SignerFilter ); // StringSource bool result = false; DSA::Verifier verifier1( publicKey ); StringSource ss(message+signature, true, new SignatureVerificationFilter(verifier1, new ArraySink((uint8_t*)&result, sizeof(result)), SignatureVerificationFilter::PUT_RESULT | SignatureVerificationFilter::SIGNATURE_AT_END) ); return result; } catch(const CryptoPP::Exception& e) { std::cerr << e.what() << std::endl; } return false; } 

These are other procedures required to save keys.

 void Save(const string& filename, const BufferedTransformation& bt) { FileSink file(filename.c_str()); bt.CopyTo(file); file.MessageEnd(); } void SaveHex(const string& filename, const BufferedTransformation& bt) { HexEncoder encoder; bt.CopyTo(encoder); encoder.MessageEnd(); Save(filename, encoder); } void SaveHexPrivateKey(const string& filename, const PrivateKey& key) { ByteQueue queue; key.Save(queue); SaveHex(filename, queue); } void SaveHexPublicKey(const string& filename, const PublicKey& key) { ByteQueue queue; key.Save(queue); SaveHex(filename, queue); } 
0
source

How can I load a private / public key from a string / byte array or any other container


 //Create Cryptopp StringSource From Std::string std::string PublicKeyString = "<Your key as std::string value>"; CryptoPP::StringSource PKeyStringSource(PublicKeyString, true); CryptoPP::RSA::PublicKey publicKey; publicKey.Load(PKeyStringSource); 

Although, I'm not sure that cryptopp supports native CryptoPP :: StringSource containers. But I believe that storing the container as std :: vector> This should serve the purpose here.

0
source

Source: https://habr.com/ru/post/1215354/


All Articles