2015-03-14 37 views
5

Czy jest możliwe zapisanie klucza prywatnego/publicznego RSA w źródle, na przykład w byte[] lub string lub innym container i użyć tego klucz do szyfrowania/odszyfrowywania?Jak mogę załadować klucz prywatny/publiczny z tablicy ciągów/bajtów lub dowolnego innego kontenera?

Funkcja dekodowania z pliku będzie wyglądać:

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(); 
} 

który ładuje klucz z pliku, który nie jest to, co chcę.

Wiem, że to musi być możliwe, ponieważ mogę utworzyć klucz za pomocą AutoSeededRandomPool.

Po prostu nie wiem, jak użyć istniejącego.

Może pominąć tę część w dokumentacji.

+0

Właściwie , Zastanawiam się teraz, czy problem polegał na wybraniu odpowiedniego źródła/zlewu (np. 'ArraySink' /' ArraySource' w innym miejscu niż 'FileSource' /' FileSink'), a nie na kodowaniu/dekodowaniu kluczowego materiału takiego jak ja odpowiedział. Możesz wyjaśnić? – softwariness

+0

@swarowność Twoja odpowiedź jest naprawdę dobra, ale tak, to raczej wybór odpowiedniego źródła/zlewu: D Nie wiedziałem o ArraySink przez O.o, które mogłoby pomóc. Jeśli umieścisz w odpowiedzi coś na temat zlewów, to zaakceptuję to. – deW1

+0

Odpowiedź teraz zaktualizowana z przykładem pokazującym materiał z kluczem w obie strony za pomocą bufora pamięci, a druga z kluczowym materiałem przechowywanym w std :: string z StringSink/StringSource, który omija niektóre z zarządzania buforami. – softwariness

Odpowiedz

9

Mogą to być interesujące strony Crypto++ Keys and Formats i Crypto++ RSA Cryptography.

Jeśli generowanie parametrów RSA tak:

AutoSeededRandomPool rng; 

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

Można użyć użyć DEREncode i BERDecode metod InvertibleRSAFunction kodować i dekodować wszystkie parametry odpowiednio:

{ 
    FileSink output("rsaparams.dat"); 
    params.DEREncode(output); 
} 

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

Aby kodować/dekodować osobno i publicznie materiał oddzielnie, użyj metod DEREncode i BERDecode na RSA::PrivateKey isamiobiektów:

// 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 i FileSink to tylko przykład źródło i zatopić obiektów można użyć. Procedury kodowania/dekodowania pobierają obiekty BufferedTransformation jako parametry, dzięki czemu można użyć dowolnych innych odpowiednich implementacji tego interfejsu.

Na przykład ArraySink może być używany do zapisywania danych w buforze pamięci, który można dostarczyć, a do odczytu z bufora można użyć StringSource (also aliased as ArraySource).

Oto niektóre kodu pokazujący wykorzystanie ArraySink i ArraySource na obie strony materiału klucza prywatnego przez 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); 

(Patrz także @jww's answer na przykład z pominięciem bufora o stałym rozmiarze za pomocą ByteQueue).

Kolejny przykład użycia std::string do przechowywania materiału klucza i użycia do napisania klasy StringSink, co pozwala uniknąć zarządzania buforami (ciąg zmienia rozmiar automatycznie, aby dopasować ilość zakodowanych danych). Zauważ, że jest to nadal dane binarne, mimo że są w obiekcie std::string.

RSA::PrivateKey rsaPrivate(params); 

std::string rsaPrivateMaterial; 
StringSink stringSink(rsaPrivateMaterial); 
rsaPrivate.DEREncode(stringSink); 

RSA::PrivateKey rsaPrivate2; 
StringSource stringSource(rsaPrivateMaterial, true); 
rsaPrivate2.BERDecode(stringSource); 

Ewentualnie, jeśli chcesz kontrolować formacie siebie, można użyć metod obiektu InvertibleRSAFunction lub klawisz obiektów wyodrębnić poszczególne parametry (jak pokazano w „Crypto ++ RSA Cryptography” link powyżej) i używać, aby wyodrębnić wartości dla przechowywania w swoim własnym formacie:

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(); 

mogłyby one być następnie przywrócony do nowego InvertibleRSAFunction lub RSA::*Key przykład podczas odczytu z pliku lub kontenera, przy użyciu odpowiednich metod ustawiających (SetModulus(), SetPrime1() itp.).

3

Jak załadować prywatne/klucz publiczny z tablicy łańcuch/bajtów lub innego pojemnika

Crypto ++ biblioteka ma wbudowane wsparcie dla std:strings. Ale inne pojemniki C++ będą trudniejsze. ArraySource został dodany na Crypto ++ 5.6, ale jest to tylko typedef dla StringSource.

Jeśli używasz delikatnego materiału, powinieneś rozważyć użycie urządzenia SecByteBlock. Będzie wytrzeć lub zneutralizować wrażliwy materiał podczas działania destruktora.

ciąg i StringSource (obciążenie)

string spki = ...; 
StringSource ss(spki, true /*pumpAll*/); 

RSA::PublicKey publicKey; 
publicKey.Load(ss); 

wektorowych i ArraySource (obciążenie)

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

RSA::PublicKey publicKey; 
publicKey.Load(as); 

ciąg i StringSink (zapisz)

string spki; 
StringSink ss(spki); 

RSA::PublicKey publicKey(...); 
publicKey.Save(ss); 

wektor (zapisz)

Zobacz kod poniżej.


Poniżej znajduje się przykład zapisywania i ładowania z poziomu std::vector. Musisz użyć pośredniego ByteQueue, aby zapisać, ponieważ nie można łatwo utworzyć 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; 

Nie mamy wyraźnego VectorSink, i nie możemy łatwo utworzyć z powodu domniemanym oczekiwaniem traits_type::char_type. Na przykład:

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); 

można utworzyć VectorSource i VectorSink, jej po prostu zajmie trochę pracy. Możesz uzyskać informacje na temat pracy związanej z pracą, patrząc na kod źródłowy StringSource i StringSink w filters.h i filters.cpp.

+0

nie powinien być tym wierszem 'publicKey.Save (ss);' zamiast 'publicKey.Save (spki);' ponieważ 'Save()' ma buforowana transformacja? – deW1

+0

to samo powinno dotyczyć obciążenia() – deW1

+0

@ deW1 - Tak, oczyszczone. Dzięki (i przykro mi z tego powodu). – jww

0

Jeśli utworzysz klucze DSA w następujący sposób, otrzymasz dwa pliki, jeden zawierający klucz prywatny, a drugi klucz publiczny.

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); 
} 

Kopiowanie treści tych dwóch plików do kodu źródłowego i umieścić je w ciągi:

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

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

Następnie można użyć HexDecoder do konwersji ciągów w bajtach i tworzenia kluczy publicznych i prywatnych przy użyciu tych bajtów:

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; 
} 

Są inne procedury potrzebne, aby zapisać klucze

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); 
}