Pythonで作ったデータを暗号化して、Javaで復号したい
セキュリティ上の話で、そんなことが必要になった。
どちらもCryptoというライブラリがあるからすぐできるだろうと思っていたら大ハマりで、復号に至るまでに4時間くらいかかった。
参考
http://nori3tsu.hatenablog.com/entry/2013/10/01/012130
ハマった私に降りた天使のようなページ。
https://qiita.com/pink/items/f795b0680a2a000e1934
http://pentan.info/doc/block_cipher.html#anc_padding
パディングについて説明してくれているページ。
ポイント
暗号化には次の考えておくべき要素がある。(かなりざっくり)
- 暗号化の種類→今回はAESとする
- 鍵
- ECB/CBC/OFB
- 暗号化対象のパディング
鍵
16バイトなのか24バイトなのか32バイトなのか、どの長さか選ぶ必要がある。
Java側でECBを使う前提にしたら、16バイトしか受け付けなかったので、16バイトを使った。
ECB/CBC/OFB
色んなサイト見るとCBCを使う情報が多いが、IV(イニシャルベクタ)という値の生成が必要。
→ってことはこの値も渡す必要が出てくるのか?ということで不採用。
簡単さも必要だったためECBを採用。
OFBはあまり調べてない。
暗号化対象のパディング
最後の難関としてかなり苦労したのがこれ。
AESの暗号化は、暗号化対象のデータ長が16Byteの倍数である必要があった。
そして、python上で暗号化すると
元のデータ長:192Byte
暗号化後のデータ長:192Byte
となった。
この暗号化したデータをJavaでそのまま復号できるかというと、できなかった。
色々試したところ、Java側で暗号化すると
元のデータ長:192Byte
暗号化後のデータ長:208Byte
となり、勝手にさらに16Byteパディングされていた!
この問題の対応方法を参考のページで知ることになる。(本当に感謝)
自己流で16バイトの倍数にパディングして192Byteとかやってたのだけど、やはりパディングにもルールがあるんだなと知る。
プログラム
from Crypto.Cipher import AES
import base64
BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s : s[0:-ord(s[-1])]
key='XXXXXXXXXXXXXXXX' #16バイト必要
text='abcdefg' #暗号化したいデータ
cipher = AES.new(key, AES.MODE_ECB)
raw = pad(text)
enc = cipher.encrypt(raw)
data = base64.b64encode(enc) #dataを何かしらの形でJavaに渡せばよい
import java.security.GeneralSecurityException;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class AESCipher {
private SecretKeySpec key;
public AESCipher(String key) {
this.key = new SecretKeySpec(key.getBytes(), "AES");
}
public String decrypt(String str) throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, this.key);
return new String(cipher.doFinal(Base64.decodeBase64(str)));
}
public static void main(String[] args) throws GeneralSecurityException {
String key = "XXXXXXXXXXXXXXXX";
String text = args[0]; // pythonのdataを渡す
AESCipher cipher = new AESCipher(key);
String dectext = cipher.decrypt(text);
System.out.println(dectext); // abcdefg
}
}