Skip to content

Instantly share code, notes, and snippets.

@yattom
Last active January 8, 2021 06:39
Show Gist options
  • Save yattom/a11d6d84a3d21eef6aee to your computer and use it in GitHub Desktop.
Save yattom/a11d6d84a3d21eef6aee to your computer and use it in GitHub Desktop.
AES暗号・復号をJavaとRubyで実装したサンプルです。言語間で相互に暗号化・復号ができます。
鍵生成はPKCS#5のPBKDF2、HMAC SHA1です。
暗号化はAES、CBC mode、鍵長128bit、iteration count 65536です。
パスワードは「password」固定、saltは長さ8 octetで0x0001020304050607(Big Endian)です。
ivの長さは128bit固定です(なんで固定なのかよくわかってない)。
送信するメッセージは、iv:encrypted_messageの構成です。先頭16 octetがiv、残りが全部encrypted_message。
Javaでもっと長い鍵を使うにはUnlimited Strength Jurisdiction Policyが必要です。http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html (使っていいのかどうかは調べていません。)
参考資料
http://www.ietf.org/rfc/rfc2898.txt
http://ruby-doc.org/stdlib-2.0/libdoc/openssl/rdoc/OpenSSL/PKCS5.html
http://ruby-doc.org/stdlib-2.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html
http://stackoverflow.com/questions/992019/java-256-bit-aes-password-based-encryption#992413
JavaならSpringを使うといいという話 http://stackoverflow.com/questions/992019/java-256-bit-aes-password-based-encryption#19485874
http://www.trustss.co.jp/Java/JEncrypt100.html
require 'openssl'
require 'base64'
data = 'a secret data'
password = 'password'
salt = "\x00\x01\x02\x03\x04\x05\x06\x07"
cipher = OpenSSL::Cipher.new('AES-128-CBC')
cipher.encrypt
cipher.key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(password, salt, 65536, 128)
iv = cipher.random_iv
encrypted = cipher.update(data) + cipher.final
message = Base64.encode64 iv + encrypted
# message = "r7VzgcVz5OdffaXyRpHW6jCOdziYSK0smgsHLa9Sp0EFYjCERvEqCopyb7Tf9YoyjCTIq5FnKa6C3cdM2Wq/vw=="
p message
decoded = Base64.decode64 message
iv = decoded[0..15]
encrypted = decoded[16..-1]
cipher = OpenSSL::Cipher.new('AES-128-CBC')
cipher.decrypt
cipher.key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(password, salt, 65536, 128)
cipher.iv = iv
print cipher.update(encrypted) + cipher.final #=> 'a secret data'
import java.security.AlgorithmParameters;
import java.security.Key;
import java.security.SecureRandom;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.spec.*;
import org.apache.commons.codec.binary.Base64;
public class Crypto {
static final public byte[] MASTER_KEY = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
static public void dump(byte[] b, String title) {
System.out.println(title + ":");
dump(b);
}
static public void dump(byte[] b) {
for (int i=0; i<b.length;i++) {
if(i % 16 == 0 && i > 0) { System.out.println(); }
if(i % 8 == 0) { System.out.print("| "); }
System.out.print(String.format("%02x ", b[i]));
}
System.out.println();
System.out.println();
}
static class Envelope {
public byte[] encryptedMessage;
public byte[] iv;
public Envelope() {
}
public Envelope(String message) {
Base64 base64 = new Base64();
byte[] buf = base64.decode(message);
iv = new byte[16];
System.arraycopy(buf, 0, iv, 0, iv.length);
encryptedMessage = new byte[buf.length - iv.length];
System.arraycopy(buf, iv.length, encryptedMessage, 0, encryptedMessage.length);
}
public String toString() {
byte[] buf = new byte[iv.length + encryptedMessage.length];
System.arraycopy(iv, 0, buf, 0, iv.length);
System.arraycopy(encryptedMessage, 0, buf, iv.length, encryptedMessage.length);
Base64 base64 = new Base64();
return base64.encodeToString(buf);
}
}
public void cipher(String password, byte[] salt, String text) throws Exception {
String message = encrypt(password, salt, text);
System.out.println("message=" + message);
String plaintext = decrypt(password, salt, message);
System.out.println("decrypted=" + plaintext);
}
public String encrypt(String password, byte[] salt, String text) throws Exception {
/* Derive the key, given password and salt. */
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 128);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
/* Encrypt the message. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
dump(iv, "iv");
byte[] ciphertext = cipher.doFinal(text.getBytes("UTF-8"));
dump(ciphertext, "ciphertext");
Envelope envelope = new Envelope();
envelope.encryptedMessage = ciphertext;
envelope.iv = iv;
return envelope.toString();
}
public String decrypt(String password, byte[] salt, String message) throws Exception {
Envelope envelope = new Envelope(message);
/* Derive the key, given password and salt. */
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 128);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
/* Decrypt the message, given derived key and initialization vector. */
Cipher decipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
decipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(envelope.iv));
String plaintext = new String(decipher.doFinal(envelope.encryptedMessage));
return plaintext;
}
static public void main(String[] args) throws Exception {
Crypto crypto = new Crypto();
String password = "password";
byte[] salt = new byte[] {0, 1, 2, 3, 4, 5, 6, 7};
if(args[0].equals("encrypt")) {
String text = args[1];
System.out.println(crypto.encrypt(password, salt, text));
} else if(args[0].equals("decrypt")) {
String message = args[1];
System.out.println(crypto.decrypt(password, salt, message));
} else {
String text = args[0];
crypto.cipher(password, salt, text);
}
}
}
% ruby cipher.rb
"NbCNPmlJUV0Og4j0lWb9MRg7GuXrLMbBheAE4fECJEw=\n"
a secret data%
% java Crypto decrypt "NbCNPmlJUV0Og4j0lWb9MRg7GuXrLMbBheAE4fECJEw=\n"
a secret data
% java Crypto encrypt "the quick brown fox jumps over a lazy dog"
iv:
| a3 e9 36 6a a1 30 11 01 | 51 00 32 f9 3a f8 af 26
ciphertext:
| 73 a5 b2 8b 5b a0 47 40 | 82 b8 74 35 05 32 14 aa
| 3d 9c 8f 86 9a d0 74 27 | ab d6 f4 45 41 c1 aa 69
| 4b f3 c8 8a 0e 0c c1 35 | 76 e6 ef f5 d3 2c 3a 71
o+k2aqEwEQFRADL5OvivJnOlsotboEdAgrh0NQUyFKo9nI+GmtB0J6vW9EVBwappS/PIig4MwTV25u/10yw6cQ==
% <uncomment cipher.rb:27 and replace message>
% ruby cipher.rb
"o+k2aqEwEQFRADL5OvivJnOlsotboEdAgrh0NQUyFKo9nI+GmtB0J6vW9EVBwappS/PIig4MwTV25u/10yw6cQ=="
the quick brown fox jumps over a lazy dog%
%
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment