$ openssl -h
aes-128-cbc aes-128-ecb aes-192-cbc aes-192-ecb
aes-256-cbc aes-256-ecb base64 bf
bf-cbc bf-cfb bf-ecb bf-ofb
camellia-128-cbc camellia-128-ecb camellia-192-cbc camellia-192-ecb
camellia-256-cbc camellia-256-ecb cast cast-cbc
cast5-cbc cast5-cfb cast5-ecb cast5-ofb
des des-cbc des-cfb des-ecb
des-ede des-ede-cbc des-ede-cfb des-ede-ofb
des-ede3 des-ede3-cbc des-ede3-cfb des-ede3-ofb
des-ofb des3 desx idea
idea-cbc idea-cfb idea-ecb idea-ofb
rc2 rc2-40-cbc rc2-64-cbc rc2-cbc
rc2-cfb rc2-ecb rc2-ofb rc4
rc4-40 seed seed-cbc seed-cfb
seed-ecb seed-ofb zlib
一開始還真不知要挑個 :P 並且用 openssl 加密出來,在其他程式語言卻解不出來 Orz
$ echo -n "Hello World" | openssl aes-128-cbc -e -k "password" -out /tmp/output
$ openssl aes-128-cbc -d -k "password" -in /tmp/output
Hello World
後來才找到這篇文章:
openssl: recover key and IV by passphrase
http://security.stackexchange.com/questions/29106/openssl-recover-key-and-iv-by-passphrase,講得非常清楚,簡言之:
The encryption format used by OpenSSL is non-standard: it is "what OpenSSL does", and if all versions of OpenSSL tend to agree with each other, there is still no reference document which describes this format except OpenSSL source code.
例如加上 -P 可以印出細部訊息:
$ openssl aes-128-cbc -e -k "password" -P
salt=EDC6E641C29F08F0
key=2CBD7689AEB5880E13803871A1DCDDD5
iv =FFFFBF58B814E180882029109B437D74
每次執行 salt, key 和 iv 都會一直變動,所以,若要用 Openssl 驗證時,需要一口氣填好 key 跟 iv 資訊:
$ openssl aes-128-cbc -e -K '' -iv '' -P
salt=0100000000000000
key=00000000000000000000000000000000
iv =00000000000000000000000000000000
當指定 -K 跟 -iv 時,就不會出現亂跳的情況,同理,在其他程式語言(此例以 C++ 為例),就能夠順利加解密。別忘了 -K 跟 -iv 需要 HEX String 格式的參數,在 Unix-like 系統可以透過 echo, od 跟 sed 來辦事:
$ echo -n "0123456789012345" | od -A n -t x1 | sed 's/ //g'
30313233343536373839303132333435
$ openssl aes-128-cbc -e -K `echo -n "0123456789012345" | od -A n -t x1 | sed 's/ //g'` -iv '' -P
salt=0100000000000000
key=30313233343536373839303132333435
iv =00000000000000000000000000000000
別忘了,若是要用 AES-256 時,Key 要記得手動接好 XD
$ echo -n "01234567890123456789012345678901" | od -A n -t x1 | sed 's/ //g'
30313233343536373839303132333435
36373839303132333435363738393031
$ openssl aes-256-cbc -e -K 3031323334353637383930313233343536373839303132333435363738393031 -iv '' -P
salt=0100000000000000
key=3031323334353637383930313233343536373839303132333435363738393031
iv =00000000000000000000000000000000
接著,來寫一點 C++ 吧:
#include <iostream>
#include <fstream>
#include <cryptopp/aes.h>
#include <cryptopp/modes.h>
#include <cryptopp/filters.h>
// http://www.cryptopp.com/
// sudo apt-get install libcrypto++-dev
// $ g++ main.cpp -lcryptopp
int main(int argc, char **argv) {
byte key[ CryptoPP::AES::DEFAULT_KEYLENGTH ], iv[ CryptoPP::AES::BLOCKSIZE ];
memset( key, 0x00, CryptoPP::AES::DEFAULT_KEYLENGTH );
memset( iv, 0x00, CryptoPP::AES::BLOCKSIZE );
for (int i=0 ; i<CryptoPP::AES::DEFAULT_KEYLENGTH ; ++i)
key[i] = ('0' + (i%10) ) ;
std::cout << "block size: " << CryptoPP::AES::BLOCKSIZE << std::endl;
std::cout << "key(" << CryptoPP::AES::DEFAULT_KEYLENGTH << "):[" << key << "]" << std::endl;
std::string plaintext = "Hello world. To man to be a better man.";
std::string ciphertext;
// enc
CryptoPP::AES::Encryption aesEncryption(key, CryptoPP::AES::DEFAULT_KEYLENGTH);
CryptoPP::CBC_Mode_ExternalCipher::Encryption cbcEncryption( aesEncryption, iv );
CryptoPP::StreamTransformationFilter stfEncryptor(cbcEncryption, new CryptoPP::StringSink( ciphertext ) );
stfEncryptor.Put( reinterpret_cast<const unsigned char*>( plaintext.c_str() ), plaintext.length());
stfEncryptor.MessageEnd();
std::ofstream output ("/tmp/output");
output << ciphertext ;
output.close();
// dec
std::string decryptedtext;
CryptoPP::AES::Decryption aesDecryption(key, CryptoPP::AES::DEFAULT_KEYLENGTH);
CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption( aesDecryption, iv );
CryptoPP::StreamTransformationFilter stfDecryptor(cbcDecryption, new CryptoPP::StringSink( decryptedtext ) );
stfDecryptor.Put( reinterpret_cast<const unsigned char*>( ciphertext.c_str() ), ciphertext.size() );
stfDecryptor.MessageEnd();
std::cout << "dec: [" << decryptedtext << "]" << std::endl;
return 0;
}
執行完的產出在 /tmp/output,可以再用 openssl 把它解回來,而上述範例的 Key 長度為 16 ,因此適用於 aes-128-CBC,若想要用 aes-256-cbc 僅需把 Key 的長度調整為 32 即可搞定 :P
$ g++ t.cpp -lcryptopp
$ ./a.out
block size: 16
key(16):[0123456789012345]
dec: [Hello world. To man to be a better man.]
$ openssl aes-128-cbc -d -K `echo -n '0123456789012345' | od -A n -t x1 | sed 's/ //g'` -iv '' -in /tmp/output
Hello world. To man to be a better man.
沒有留言:
張貼留言