Domanda

Ho bisogno di imitare ciò che fa nella codifica e decodifica MySQL stringhe utilizzando le funzioni built-in AES_ENCRYPT () e AES_DECRYPT ().

Ho letto un paio di post di blog e apparentemente MySQL utilizza la crittografia AES a 128 bit per tali funzioni. In cima a quello, dal momento che questo richiede una chiave di crittografia a 16 bit, MySQL pad la stringa con caratteri x0 (\ 0s) fino a quando è a 16 bit nel formato.

L'algoritmo in C dal codice sorgente di MySQL è macchiato qui .

Ora ho bisogno di replicare quello che MySQL fa in un'applicazione Rails, ma ogni singola cosa ho provato, non funziona.

Ecco un modo per replicare il comportamento sto ottenendo:

1) Creare una nuova applicazione Rails

rails encryption-test
cd encryption-test

2) Creazione di un nuovo ponteggio

script/generate scaffold user name:string password:binary

3) Modificare il config / database.yml e aggiungere un database MySQL test

development:
    adapter: mysql
    host: localhost
    database: test
    user: <<user>>
    password: <<password>>

4) Eseguire la migrazione

rake db:migrate

5) Inserire console, creare un utente e aggiornare la sua password dalla query MySQL

script/console
Loading development environment (Rails 2.2.2)
>> User.create(:name => "John Doe")
>> key = "82pjd12398JKBSDIGUSisahdoahOUASDHsdapdjqwjeASIduAsdh078asdASD087asdADSsdjhA7809asdajhADSs"
>> ActiveRecord::Base.connection.execute("UPDATE users SET password = AES_ENCRYPT('password', '#{key}') WHERE name='John Doe'")

Ecco dove mi sono bloccato. Se tento di decifrarlo, utilizzando MySQL funziona:

>> loaded_user = User.find_by_sql("SELECT AES_DECRYPT(password, '#{key}') AS password FROM users WHERE id=1").first
>> loaded_user['password']
=> "password"

Tuttavia, se tento di usare libreria OpenSSL, non c'è modo che posso farlo funzionare:

cipher = OpenSSL::Cipher::Cipher.new("AES-128-ECB") 
cipher.padding = 0
cipher.key = key
cipher.decrypt 

user = User.find(1)
cipher.update(user.password) << cipher.final #=> "########gf####\027\227"

Ho provato imbottitura la chiave:

desired_length = 16 * ((key.length / 16) + 1)
padded_key = key + "\0" * (desired_length - key.length)

cipher = OpenSSL::Cipher::Cipher.new("AES-128-ECB") 
cipher.key = key
cipher.decrypt 

user = User.find(1)
cipher.update(user.password) << cipher.final #=> ""|\e\261\205:\032s\273\242\030\261\272P##"

Ma in realtà non funziona.

Qualcuno ha un indizio su come posso imitare il MySQL AES_ENCRYPT () e AES_DECRYPT () il comportamento funzioni in Ruby?

Grazie!

È stato utile?

Soluzione

Per riferimento futuro:

Secondo il post sul blog che ho inviato prima, ecco come funziona con MySQL la chiave che fornisci AES_ENCRYPT / decifrare:

  

"L'algoritmo crea solo un 16 byte   tamponi a tutti zero, allora loop   attraverso tutti i personaggi del   stringa che fornisci e fa un   assegnazione con OR bit a bit tra la   due valori. Se iteriamo fino a quando non   colpire la fine del buffer 16 byte, abbiamo   basta iniziare da capo   facendo ^ =. Per le stringhe di lunghezza inferiore a 16   personaggi, ci fermiamo alla fine del   stringa ".

Non so se si può leggere C, ma ecco il frammento citato:

http://pastie.org/425161

specialmente questa parte:

bzero((char*) rkey,AES_KEY_LENGTH/8);      /* Set initial key  */

for (ptr= rkey, sptr= key; sptr < key_end; ptr++,sptr++)
{
  if (ptr == rkey_end)
    ptr= rkey;  /*  Just loop over tmp_key until we used all key */
  *ptr^= (uint8) *sptr;
}

Così mi è venuta con questo metodo (con un aiuto da Rob Biedenharn, dal forum ruby):

def mysql_key(key)
   final_key = "\0" * 16
   key.length.times do |i|
     final_key[i%16] ^= key[i]
   end
   final_key
end

che, data una stringa restituisce la chiave MySQL utilizza nella codifica e decodifica. Quindi tutto ciò che serve ora è:

def aes(m,k,t)
  (aes = OpenSSL::Cipher::AES128.new("ECB").send(m)).key = k
  aes.update(t) << aes.final
end

def encrypt(key, text)
  aes(:encrypt, key, text)
end

def decrypt(key, text)
  aes(:decrypt, key, text)
end

Per usare OpenSSL lib, integrato in rubino, e allora si può fare i due metodi "finali":

def mysql_encrypt(s, key)
  encrypt(mysql_key(key), s)
end

def mysql_decrypt(s, key)
  decrypt(mysql_key(key), s)
end

E il gioco è fatto! Inoltre, il codice completo può essere trovato in questo Gist:

http://gist.github.com/84093

: -)

Altri suggerimenti

In genere non si vuole per riempire la chiave, si pad / unpad i dati da cifrare / decifrati. Questo potrebbe essere un'altra fonte di problemi. Suggerisco utilizzando i dati di prova di un numero completo di blocchi per eliminare questa possibilità.

Inoltre, ho il sospetto che la chiave per l'API OpenSSL richiede una chiave "letterale", non è una rappresentazione ASCII della chiave, come avete nel vostro codice.

data l'esiguità dei documenti rubino OpenSSL e se si parla un po 'di Java, si consiglia di prototipo in JRuby con il fornitore BouncyCastle - questa è una cosa che ho fatto con buoni risultati quando si lavora con Twofish (non presente nel OpenSSL API).

EDIT: ho riletto i suoi commenti su imbottitura della chiave. Avete qualche bit / byte confusione nella tua domanda, e io non sono sicuro di come questo si applica in ogni caso, in quanto la chiave postato è di 89 caratteri (712 bit) di lunghezza. Forse si dovrebbe provare con una chiave a 128 bit / password per eliminare questo fenomeno padding?

Per inciso, sviluppatori MySQL dovrebbero essere sculacciato per crittografica debole, ci sono modi migliori per allungare password che semplicemente imbottitura con byte nullo: (

Se non ti dispiace utilizzando un'implementazione OpenSSL attr_encrypted è un gioiello che permetterà drop-in la crittografia sulla maggior parte delle classi, ActiveRecord o no. E, purtroppo, non sarà compatibile con le funzioni AES_EN / Decrypt di MySQL però.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top