Ruby에서 AES_ENCRYPT 및 AES_DECRYPT 함수를 모방합니다
-
21-08-2019 - |
문제
내장 함수 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를 읽을 수 있는지 모르겠지만 여기에 언급 된 스 니펫이 있습니다.
특히이 부분 :
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
그리고 당신은 설정되었습니다! 또한이 요점에서 완전한 코드를 찾을 수 있습니다.
:-)
다른 팁
일반적으로 키를 패드하고 싶지 않으며, 데이터를 암호화/암호화 할 데이터를 패드/끼 웁니다. 그것은 또 다른 문제의 원천 일 수 있습니다. 이 가능성을 제거하기 위해 완전한 수의 블록의 테스트 데이터를 사용하는 것이 좋습니다.
또한 OpenSSL API의 키에는 코드에서와 같이 키를 ASCII 표현이 아닌 "리터럴"키가 필요하다고 생각합니다.
OpenSSL Ruby Docs의 부족함을 감안할 때 약간의 Java를 말하면 Bouncycastle 제공 업체와 Jruby에서 프로토 타입을 원할 수 있습니다. 이것은 Twofish (OpenSSL API에 존재하지 않음)와 함께 일할 때 좋은 효과를 얻었습니다. .
편집 : 키 패딩에 대한 귀하의 의견을 다시 읽습니다. 귀하의 질문에 약간의 비트/바이트 혼동이 있으며, 게시 된 키의 길이가 89 자 (712 비트)이므로 어떤 경우에도 이것이 어떻게 적용되는지 잘 모르겠습니다. 아마도이 패딩 현상을 제거하기 위해 128 비트 키/암호로 시도해야합니까?
덧붙여서, MySQL 개발자는 약한 암호화를 위해 스팽킹되어야하며, 단순히 제로 바이트로 패딩하는 것보다 암호를 스트레칭하는 더 좋은 방법이 있습니다.
OpenSSL 구현을 사용하지 않는 경우 attr_encrypted 대부분의 클래스에서 드롭 인 암호화를 허용하는 보석입니다. 불행히도 MySQL의 aes_en/decrypt 함수와 호환되지 않습니다.