2015-03-14 24 views
5

È possibile memorizzare una chiave privata/pubblica RSA nell'origine, ad esempio in un byte[] o string o in qualsiasi altro container e utilizzare questo chiave per crittografia/decrittografia?Come caricare una chiave privata/pubblica da un array stringa/byte o qualsiasi altro contenitore

Una funzione di decodifica da file sarà simile:

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

che carica la chiave da file che non è quello che voglio.

So che deve essere possibile poiché posso creare la chiave con AutoSeededRandomPool.

Non so proprio come usarne uno esistente.

Forse sto trascurando questa parte nella documentazione.

+0

realtà , Mi chiedo ora se il problema fosse scegliere un source/sink adatto (es. 'ArraySink' /' ArraySource' invece di 'FileSource' /' FileSink'), non la codifica/decodifica del materiale chiave come ho risposta. Puoi chiarire? – softwariness

+0

@softwariness la tua risposta è davvero buona, ma sì, si tratta di scegliere un source/sink adatto: D Non sapevo di ArraySink anche se O.o poteva essere d'aiuto. Se puoi includere qualcosa anche nei lavandini nella tua risposta, lo accetto. – deW1

+0

Risposta ora aggiornata con un esempio che mostra il materiale della chiave round-tripping attraverso un buffer di memoria e un altro che mostra il materiale chiave memorizzato in una stringa std :: con StringSink/StringSource che evita parte della gestione del buffer. – softwariness

risposta

9

Le pagine Crypto++ Keys and Formats e Crypto++ RSA Cryptography possono essere di interesse.

Se si sta generando i parametri di RSA in questo modo:

AutoSeededRandomPool rng; 

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

È possibile utilizzare l'uso dei DEREncode e BERDecode metodi di InvertibleRSAFunction per codificare e decodificare tutti i parametri, rispettivamente:

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

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

Per codificare/decodificare separatamente il materiale privato e pubblico, utilizzare i metodi DEREncode e BERDecode su RSA::PrivateKey e RSA::PublicKey oggetti stessi:

// 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 e FileSink sono solo sorgente di esempio e lavandino oggetti è possibile utilizzare. Le routine di codifica/decodifica prendono gli oggetti BufferedTransformation come parametri, quindi è possibile utilizzare qualsiasi altra implementazione adatta di tale interfaccia.

Ad esempio, ArraySink può essere utilizzato per scrivere dati in un buffer di memoria fornito dall'utente e StringSource (also aliased as ArraySource) può essere utilizzato per leggere da un buffer.

Ecco po 'di codice che mostra l'uso di ArraySink e ArraySource di andata e ritorno il materiale chiave privata attraverso una 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); 

(Vedi anche @jww's answer per un esempio che evita il buffer di dimensioni fisse utilizzando una ByteQueue).

E un altro esempio utilizza std::string per archiviare il materiale della chiave e utilizzare la classe StringSink per scrivere su questo, che evita parte della gestione del buffer (la stringa verrà ridimensionata automaticamente per corrispondere alla quantità di dati codificata). Si noti che questo è ancora dati binari anche se si trova in un oggetto 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); 

In alternativa, se si desidera controllare il formato da soli, è possibile utilizzare i metodi dell'oggetto InvertibleRSAFunction o gli oggetti chiave per estrarre i singoli parametri (come mostrato nel link "Crypto ++ RSA Cryptography" di cui sopra) e l'uso che per estrarre i valori per la conservazione nel proprio formato:

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

Questi potrebbero poi essere ripristinati in un nuovo InvertibleRSAFunction o RSA::*Key esempio quando letta dal file o il contenitore, utilizzando i metodi setter corrispondenti (SetModulus(), SetPrime1() , eccetera.).

3

Come faccio a caricare un privato/chiave pubblica da un array di stringhe/di byte o qualsiasi altro contenitore

biblioteca La Crypto ++ è dotato di supporto per std:strings. Ma altri contenitori C++ saranno più complicati. ArraySource è stato aggiunto in Crypto ++ 5.6, ma è solo un typedef per StringSource.

Se si utilizza materiale sensibile, è consigliabile prendere in considerazione l'utilizzo di SecByteBlock. Pulirà o azzererà il materiale sensibile quando viene eseguito il distruttore.

corda e StringSource (carico)

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

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

vettoriale e ArraySource (carico)

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

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

corda e StringSink (salvare)

string spki; 
StringSink ss(spki); 

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

vettore (salvare)

See codice qui sotto.


seguito è un esempio di salvataggio e caricamento da un std::vector. È necessario utilizzare un intermedio ByteQueue da salvare perché non è possibile creare facilmente un 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; 

Noi non abbiamo un esplicito VectorSink, e non possiamo creare facilmente uno a causa di una implicita aspettativa di traits_type::char_type. Per esempio:

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

è possibile creare il VectorSource e VectorSink, il suo solo andando a prendere un po 'di lavoro. Puoi avere un'idea del lavoro svolto guardando il codice sorgente StringSource e StringSink in filters.h e filters.cpp.

+0

non dovrebbe questa riga essere "publicKey.Save (ss);" piuttosto che "publicKey.Save (spki);" poiché "Save()" prende un trasformazione bufferizzata? – deW1

+0

si dovrebbe applicare per Load() – deW1

+0

@ deW1 - Sì, ripulito. Grazie (e scusami per quello). – jww

0

Se si creano le chiavi DSA come segue, si otterranno due file, uno contenente la chiave privata e l'altro la chiave pubblica.

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

Copiare il contenuto di questi due file nel codice sorgente e metterli in stringhe:

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

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

quindi è possibile utilizzare HexDeCoder per convertire le stringhe in byte e creare le chiavi pubbliche e private utilizzando questi byte:

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

Queste sono le altre routine necessari per salvare le chiavi

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