문제

내장 함수 aes_encrypt () 및 aes_decrypt ()를 사용하여 문자열을 암호화하고 해독 할 때 MySQL이하는 일을 모방해야합니다.

나는 몇 개의 블로그 게시물을 읽었으며 분명히 MySQL은 이러한 기능에 AES 128 비트 암호화를 사용합니다. 또한이 암호화에는 16 비트 키가 필요하기 때문에 MySQL은 크기가 16 비트가 될 때까지 x0 숯 ( 0s)으로 문자열을 패드합니다.

MySQL 소스 코드의 C의 알고리즘이 발견되었습니다. 여기.

이제 Rails 응용 프로그램에서 MySQL이하는 일을 복제해야하지만 내가 시도한 모든 일은 작동하지 않습니다.

다음은 내가 얻는 행동을 복제하는 방법입니다.

1) 새 레일 앱을 만듭니다

rails encryption-test
cd encryption-test

2) 새로운 스캐 폴딩을 만듭니다

script/generate scaffold user name:string password:binary

3) config/database.yml을 편집하고 테스트 MySQL 데이터베이스 추가

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

4) 마이그레이션을 실행하십시오

rake db:migrate

5) Console을 입력하고 사용자를 만들고 MySQL Query에서 비밀번호를 업데이트하십시오.

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'")

그것이 내가 갇힌 곳입니다. MySQL을 사용하여 해독하려고하면 작동합니다.

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

그러나 OpenSSL 라이브러리를 사용하려고하면 작동 할 수있는 방법이 없습니다.

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"

키를 패딩하려고 시도했습니다.

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##"

그러나 실제로는 작동하지 않습니다.

Ruby에서 mysql aes_encrypt () 및 aes_decrypt () 함수 동작을 어떻게 모방 할 수 있습니까?

감사!

도움이 되었습니까?

해결책

향후 참조 :

내가 전에 보낸 블로그 게시물에 따르면, MySQL이 AES_encrypt / Decrypt를 제공하는 키와 어떻게 작동하는지에 따르면 :

"알고리즘은 단지 16 바이트 버퍼를 모두 0으로 설정 한 다음 제공하는 문자열의 모든 문자를 통해 루프를 사용하고 비트 또는 두 값 사이에 할당을 수행합니다. 16 바이트 버퍼의 끝을 누르기 전까지는 반복하면 , 우리는 처음부터 시작하여 ^=.

C를 읽을 수 있는지 모르겠지만 여기에 언급 된 스 니펫이 있습니다.

http://pastie.org/425161

특히이 부분 :

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

그래서 저는이 방법을 생각해 냈습니다 (Ruby Forum의 Rob Biedenharn의 도움으로) :

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

문자열이 주어지면 암호화 및 해독 할 때 MySQL이 사용하는 키를 반환합니다. 그래서 지금 당신이 필요로하는 것은 다음과 같습니다.

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

Ruby에 내장 된 OpenSSL LIB를 사용한 다음 두 가지 "최종"방법을 만들 수 있습니다.

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

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

그리고 당신은 설정되었습니다! 또한이 요점에서 완전한 코드를 찾을 수 있습니다.

http://gist.github.com/84093

:-)

다른 팁

일반적으로 키를 패드하고 싶지 않으며, 데이터를 암호화/암호화 할 데이터를 패드/끼 웁니다. 그것은 또 다른 문제의 원천 일 수 있습니다. 이 가능성을 제거하기 위해 완전한 수의 블록의 테스트 데이터를 사용하는 것이 좋습니다.

또한 OpenSSL API의 키에는 코드에서와 같이 키를 ASCII 표현이 아닌 "리터럴"키가 필요하다고 생각합니다.

OpenSSL Ruby Docs의 부족함을 감안할 때 약간의 Java를 말하면 Bouncycastle 제공 업체와 Jruby에서 프로토 타입을 원할 수 있습니다. 이것은 Twofish (OpenSSL API에 존재하지 않음)와 함께 일할 때 좋은 효과를 얻었습니다. .

편집 : 키 패딩에 대한 귀하의 의견을 다시 읽습니다. 귀하의 질문에 약간의 비트/바이트 혼동이 있으며, 게시 된 키의 길이가 89 자 (712 비트)이므로 어떤 경우에도 이것이 어떻게 적용되는지 잘 모르겠습니다. 아마도이 패딩 현상을 제거하기 위해 128 비트 키/암호로 시도해야합니까?

덧붙여서, MySQL 개발자는 약한 암호화를 위해 스팽킹되어야하며, 단순히 제로 바이트로 패딩하는 것보다 암호를 스트레칭하는 더 좋은 방법이 있습니다.

OpenSSL 구현을 사용하지 않는 경우 attr_encrypted 대부분의 클래스에서 드롭 인 암호화를 허용하는 보석입니다. 불행히도 MySQL의 aes_en/decrypt 함수와 호환되지 않습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top