ksnctf 21 Perfect Cipher
ksnctfの21問目を解いていきます。メルセンヌツイスタの問題です。
ksnctf.sweetduet.info
大まかな解法
1. encrypt.keyを復元する
2.encrypt.keyからメルセンヌツイスタの乱数を予測し、flag.keyを生成する
3.flag.keyとflag.encからflag_dec.jpgを復元する
1. encrypt.keyを復元する
encrypt.zipには以下のファイルがあります。
↓encrypt.cpp
// g++ -O2 -o encrypt.exe encrypt.cpp mt19937ar.cpp #include <stdio.h> #include <stdlib.h> #include "mt19937ar.h" typedef unsigned long dword; void initialize(const char *seed); void encrypt(const char *plain, const char *crypt, const char *key); void decrypt(const char *plain, const char *crypt, const char *key); int min(int a, int b) { return a<b ? a : b; } int main() { initialize("seed"); encrypt("encrypt.cpp", "encrypt.enc", "encrypt.key"); encrypt("flag.jpg", "flag.enc", "flag.key"); // decrypt("encrypt_dec.cpp", "encrypt.enc", "encrypt.key"); // decrypt("flag_dec.jpg", "flag.enc", "flag.key"); } void initialize(const char *seed) { const int N = 1024; FILE *f = fopen(seed, "rb"); if (f==NULL) { printf("Failed to open %s\n", seed); exit(-1); } dword buf[N]; fread(buf, sizeof (dword), N, f); fclose(f); init_by_array(buf, N); } void encrypt(const char *plain, const char *crypt, const char *key) { FILE *fp = fopen(plain, "rb"); if (fp==NULL) { printf("Failed to open %s\n", plain); exit(-1); } FILE *fc = fopen(crypt, "wb"); if (fc==NULL) { printf("Failed to open %s\n", crypt); exit(-1); } FILE *fk = fopen(key, "wb"); if (fk==NULL) { printf("Failed to open %s\n", key); exit(-1); } fseek(fp, 0, SEEK_END); dword length = (dword)ftell(fp); fseek(fp, 0, SEEK_SET); fwrite(&length, 4, 1, fc); for (int i=0; i<length; i+=4) { dword p; fread(&p, 4, 1, fp); dword k = genrand_int32(); dword c = p^k; fwrite(&c, 4, 1, fc); fwrite(&k, 4, 1, fk); } fclose(fp); fclose(fc); fclose(fk); } void decrypt(const char *plain, const char *crypt, const char *key) { FILE *fp = fopen(plain, "wb"); if (fp==NULL) { printf("Failed to open %s\n", plain); exit(-1); } FILE *fc = fopen(crypt, "rb"); if (fc==NULL) { printf("Failed to open %s\n", crypt); exit(-1); } FILE *fk = fopen(key, "rb"); if (fk==NULL) { printf("Failed to open %s\n", key); exit(-1); } dword length; fread(&length, 4, 1, fc); for (int i=0; i<length; i+=4) { dword c; fread(&c, 4, 1, fc); dword k; fread(&k, 4, 1, fk); dword p = c^k; fwrite(&p, min(4,length-i), 1, fp); } fclose(fp); fclose(fc); fclose(fk); }
encrypt関数において、
①引数で指定したファイルを開く
②第一引数で指定したファイル(fp)のサイズを取得して.encファイルの先頭4バイトに書き込む
③genrand_int32関数でメルセンヌツイスタの疑似乱数を生成
④fpと生成した疑似乱数を4バイト単位でXORしたものを.encファイルに出力(生成した乱数は.keyファイルに記録する)
というような操作が行われています。
暗号化と言っても単にXORしているだけなのでXORの性質を利用すればencrypt.keyファイルの復元が可能です。
いま、encrypt関数で
encrypt .cpp (XOR) encrypt.key = encrypt.enc
が計算されているので、
encrypt.cpp (XOR) encrypt.enc(先頭4バイト除く) = encrypt.key
となり、生成された乱数を得ることができます。
2.encrypt.keyからメルセンヌツイスタの乱数を予測し、flag.keyを生成する
メルセンヌ・ツイスタをわかった気になる | 成瀬順のメモ帳
(メルセンヌツイスタのしくみについて書かれています)
メルセンヌツイスタは連続した624個の乱数があれば次に生成される乱数の予測が可能になります。先ほど復元したencrypt.keyは2600バイト(=650個の乱数)なので次に生成されたflag.keyを予測できます。これを一から実装するのは大変なので、
Mersenne Twisterの出力を推測してみる - ももいろテクノロジー
こちらのpredict_mt.pyをお借りして、改造します。
import random from struct import * def untemper(x): x = unBitshiftRightXor(x, 18) x = unBitshiftLeftXor(x, 15, 0xefc60000) x = unBitshiftLeftXor(x, 7, 0x9d2c5680) x = unBitshiftRightXor(x, 11) return x def unBitshiftRightXor(x, shift): i = 1 y = x while i * shift < 32: z = y >> shift y = x ^ z i += 1 return y def unBitshiftLeftXor(x, shift, mask): i = 1 y = x while i * shift < 32: z = y << shift y = x ^ (z & mask) i += 1 return y #encrypt.keyから乱数を624読み込みメルセンヌツイスタの内部状態を復元 with open("encrypt.key", "rb") as f: values1 = [int.from_bytes(f.read(4), 'little') for i in range(624)] mt_state = tuple([untemper(x) for x in values1] + [624]) random.setstate((3, mt_state, None)) #650個まで分かってるのでいらない waste=[random.getrandbits(32) for i in range(26)] #バイト数が78556 → 19639個の乱数が復元に必要 pre = [random.getrandbits(32) for i in range(19639)] #生成された乱数を4バイト単位でflag.keyに書き込み with open("flag.key", "wb") as f: for x in pre: f.write(pack('<L', x))
3.flag.keyとflag.encからflag_dec.jpgを復元する
flag.keyが分かったのであとはdecrypt関数にかければflag_dec.jpgが復元されます。flag_dec.jpgをバイナリエディタで開くとちょっと下にフラグが出てきます。
ksnctf 20 G00913
first 10-digit prime found in consecutive digits of π
円周率の中で、最初の10桁の素数を見つけよ。
素数判定いろいろ - シンプルな判定と、素数の分布 - Qiita
上のリンクのsimple_prime_test.pyで素数判定処理がありますのでそれを拝借させていただきます。
import math def is_prime(n): if n == 1: return False for k in range(2, int(math.sqrt(n)) + 1): if n % k == 0: return False return True #円周率 とりあえず100桁 pi = "31415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679" x = 0 while(x < 90): num = int(pi[x:x+10]) #10桁の円周率を抽出 if(is_prime(num)): print(num) break x += 1
ksnctf 19 ZIP de kure
flag.zipにはパスワードがかかっています。
It is known that the encryption system of ZIP is weak against known-plaintext attacks
zipは既知平文攻撃に弱いことが知られています。
既知平文攻撃とは、平文の一部と暗号文から鍵を推測し、平文全体を割り出す攻撃手法です。ここでは平文が「flag.html」「Standard-lock-key.jpg」で鍵がパスワード、暗号文がzipファイルです。ここで平文の一部を知りたいのですが「Standard-lock-key.jpg」はネットで出回っているようでした。
...出回っているのですがサイズがいろいろあり、正しい画像を使わないと復号できないみたいです。250kBぐらいのこれを使ってください。
既知平文攻撃はpkcrackを使って行います。
パスワードクラッキングツール pkcrack
引数は以下の通り。
-C [暗号化されたzipファイル]
-c [暗号化されたzipファイルの中で平文がわかるファイル]
-P [平文のファイルが入っている暗号化されていないzip]
-p [平文のファイル]
-d [出力先(復号したzipファイルの名前)]
./pkcrack -C flag.zip -c Standard-lock-key.jpg -p flag.zip -p Stadard-lock-key.jpg -d flag_dec.zip
うまくいくとflag_dec.zipが作成され、unzipしてflag.htmlをブラウザで開くとフラグが表示されます。
ksnctf 18 USB flash drive
ksnctfの18問目を解いていきます。ジャンルはディジタルフォレンジックスです。
zipファイルを解凍してimgファイルに探りを入れます。
unzip drive.zip fls drive.img
イメージファイルは.jpgの集まりみたいですが「Liberty Leading the People.jpg」が消去されています。
icat drive.img 36-128-4 > 0.jpg
試しにLiberty Leading the People.jpg:00を復元するとこれは画像ファイルでなく「FLA」と書かれた3バイトのファイルでした。00~06のファイルを復元してcatコマンドでつなげて出力するとフラグが出てきました。
おまけ:FTK Imagerで開く
FTK Imagerでファイルを開くと自動で画像ファイルを復元してくれるのでツールを使うだけの問題になります。
ksnctf 16 Math I
ksnctfの16問目を解いていきます。
一見すると単なる数学の問題のようですが、これはRSAに使われる暗号化の技術と関連しています。
RSAとは
RSA暗号 - Wikipedia
大きな素数を掛け算する(=暗号化する)ことは簡単でも、その積を素因数分解する(=復号する)のが難しいということを利用した暗号です。今の問題でいうと、pとqが大きな素数、nがその積、cが暗号文、eが秘密鍵(の一部)に当たります。
RSAによる暗号化、復号の流れ
RSAの処理の流れについて超ざっくり説明します。間違ってたらすいません。
①巨大な素数p,qを用意しn=pqを計算しておく
※ここのpqは秘密にしておかなければなりません。この問題では明らかになっているので復号が可能になります。
②p-1とq-1の最小公倍数をもとめる
※最小公倍数L = lcm(p-1, q-1)
③ed≡1(mod L) (ed-1がLの倍数である)となるようなedの組を求める
※ここで、(e,n)が公開鍵、(d, n)が秘密鍵になります。
④送信者は平文mを公開鍵(e,n)を使って
c= me mod n (mのe乗をnで割った余り)を計算して暗号化する
受信者は秘密鍵を使って
m = cd mod n を計算し復号する
mを求める
要するにmを求めるためにはm = cd mod n を計算すればいいわけです。いまc、とnは分かっているので①~③の流れに沿ってdを求めます。
①巨大な素数p,qを用意しn=pqを計算しておく
いまpもqもnもわかっているのでOKです。
②p-1とq-1の最大公倍数をもとめる
a,bの最小公倍数lcm(a,b)と最大公約数gcd(a,b)の間には次の式が成り立ちます。
lcm(a,b) = ab/gcd(a,b)
ですので、まずgcd(p-1,q-1)を求める必要があります。最大公約数を求めるアルゴリズムは、ユークリッドの互除法が有名です。でもpython3.5からmathモジュールでgcd関数が利用可能になったので詳しい説明は飛ばします。
import math p = 34111525225922333955113751419357677129436029651245533697825114748126342624744832960936498161825269430327019858323450578875242014583535842110912370431931233957939950911741013017595977471949767235426490850284286661592357779825212265055931705799916913817655743434497422993498931394618832741336247426815710164342599150990608143637331068220244525541794855651643135012846039439355101027994945120698530177329829213208761057392236875366458197098507252851244132455996468628957560178868724310000317011912994632328371761486669358065577269198065792981537378448324923622959249447066754504943097391628716371245206444816309511381323 q = 44481453884385518268018625442920628989497457642625668259648790876723318635861137128631112417617317160816537010595885992856520476731882382742220627466006460645416066646852266992087386855491152795237153901319521506429873434336969666536995399866125781057768075533560120399184566956433129854995464893265403724034960689938351450709950699740508459206785093693277541785285699733873530541918483842122691276322286810422297015782658645129421043160749040846216892671031156465364652681036828461619272427318758098538927727392459501761203842363017121432657534770898181975532066012149902177196510416802134121754859407938165610800223 lcm = (p-1)*(q-1)//math.gcd(p-1, q-1) print(lcm)
Lが「84296124236828755295256160493851228647282615601483997160948130049067122531755132909036158809504235996655857568141379623584785421268598355704500296885003303538029386515098788842365280739235449990183062094279553798371687347273163100041038785672404220512403565798591569237642461494493666961096181434014181929065329176973463601725920953714877046441949558852569160310401566464294319909521197662482014455011640926123889364512056036070109707528066878526609938532349593490438006567548470577849626240317548045332059115332702394504908613775263079301368965937265158080043479607415842506120690972879040939308376545429013819648390485935794450615898958353067616834620525918090857785857386064201656062162479977365475744402936160462725848773696706919076088977774264353849497982111223048654302053359923581516772918609667237350631341932314473210047462494239592712774736257925119172802057115385038544143487469511024761330324631420828143510952462371722743318909328271983585955272776528260618472853022289420098014703890951951042220157282038147741762284049660911709073315391605377178884477278893384365501359415452208704330546728410396192866583877887324203642431037251063767954409221007463068318828757916045833958167479029226438074023231872818141833569638」だとわかりました。
③ed≡1(mod L) (ed-1がLの倍数である)となるようなedの組を求める
このedの組を求めるには拡張ユークリッド互除法を使います。
Python で RSA 公開鍵暗号をなぞってみる - CAMPHOR- Tech Blog
拡張ユークリッド互除法はax + by = gcd(a,b)(x,yは正の整数)となるa,b,gcd(x,y)を同時に求めるアルゴリズムです。x=e、y=L、gcd(x,y) = gcd(e, L) = 1を代入して求めたaが秘密鍵dに対応します。
def ex_euclid(x, y): c0, c1 = x, y a0, a1 = 1, 0 b0, b1 = 0, 1 while c1 != 0: m = c0 % c1 q = c0 // c1 c0, c1 = c1, m a0, a1 = a1, (a0 - q * a1) b0, b1 = b1, (b0 - q * b1) return a0 e = 65537 L = 84296124236828755295256160493851228647282615601483997160948130049067122531755132909036158809504235996655857568141379623584785421268598355704500296885003303538029386515098788842365280739235449990183062094279553798371687347273163100041038785672404220512403565798591569237642461494493666961096181434014181929065329176973463601725920953714877046441949558852569160310401566464294319909521197662482014455011640926123889364512056036070109707528066878526609938532349593490438006567548470577849626240317548045332059115332702394504908613775263079301368965937265158080043479607415842506120690972879040939308376545429013819648390485935794450615898958353067616834620525918090857785857386064201656062162479977365475744402936160462725848773696706919076088977774264353849497982111223048654302053359923581516772918609667237350631341932314473210047462494239592712774736257925119172802057115385038544143487469511024761330324631420828143510952462371722743318909328271983585955272776528260618472853022289420098014703890951951042220157282038147741762284049660911709073315391605377178884477278893384365501359415452208704330546728410396192866583877887324203642431037251063767954409221007463068318828757916045833958167479029226438074023231872818141833569638 print(ex_euclid(e, L))
dは「10136834995658138463492588932237385491695291111208864940803396751708164741638497374858690015986433371830337267414166239124032133070140892035143000743865465846517380947029823685348440995253285645858564053359433045225861238443319016607770063778998392692040412317602272871230911378886805763467949492370199548086788520129512590524466836080793231350367036533822078404661103579735165405907144952897153606664124725556286862104147483410417544364690099785895187841577233414683948452917428271419700389092308102983993131939774899233916486643618846269650561065529192835021784072906056956997378054492267293936086722836352868038649395298228421583287298637113781349064564517150221252274929575231903374062018473558712091515320198980831322980690354261397968433615194125039106056075477193744671780559524508993907065803481811763741483524857261750894670978486995592861707073084026797089476358795024013403036830297639289928502806357134849001477277811794054352447082046958247110998439840383629616621979472098506072201677900915912594977791777814705476731626338978671272819912434837992993096501746475459427746365460409490804111246572200930710614577133985413566473885050820659402302501957059621914654156295472133564617207107883082204882388427753479344345367」でした。
④受信者は秘密鍵を使って
m = cd mod n を計算し復号する
c = 225549592628492616152632265482125315868911125659971085929712296366214355608049224179339757637982541542745010822022226409126123627804953064072055667012172681551500780763483172914389813057444669314726404135978565446282309019729994976815925850916487257699707478206132474710963752590399332920672607440793116387051071191919835316845827838287954541558777355864714782464299278036910958484272003656702623646042688124964364376687297742060363382322519436200343894901785951095760714894439233966409337996138592489997024933882003852590408577812535049335652212448474376457015077047529818315877549614859586475504070051201054704954654093482056493092930700787890579346065916834434739980791402216175555075896066616519150164831990626727591876115821219941268309678240872298029611746575376322733311657394502859852213595389607239431585120943268774679785316133478171225719729917877009624611286702010936951705160870997184123775488592130586606070277173392647225589257616518666852404878425355285270687131724258281902727717116041282358028398978152480549468694659695121115046850718180640407034795656480263573773381753855724693739080045739160297875306923958599742379878734638341856117533253251168244471273520476474579680250862738227337561115160603373096699944163 d = 10136834995658138463492588932237385491695291111208864940803396751708164741638497374858690015986433371830337267414166239124032133070140892035143000743865465846517380947029823685348440995253285645858564053359433045225861238443319016607770063778998392692040412317602272871230911378886805763467949492370199548086788520129512590524466836080793231350367036533822078404661103579735165405907144952897153606664124725556286862104147483410417544364690099785895187841577233414683948452917428271419700389092308102983993131939774899233916486643618846269650561065529192835021784072906056956997378054492267293936086722836352868038649395298228421583287298637113781349064564517150221252274929575231903374062018473558712091515320198980831322980690354261397968433615194125039106056075477193744671780559524508993907065803481811763741483524857261750894670978486995592861707073084026797089476358795024013403036830297639289928502806357134849001477277811794054352447082046958247110998439840383629616621979472098506072201677900915912594977791777814705476731626338978671272819912434837992993096501746475459427746365460409490804111246572200930710614577133985413566473885050820659402302501957059621914654156295472133564617207107883082204882388427753479344345367 n = 1517330236262917595314610888889322115651087080826711948897066340883208205571592392362650858571076247939805436226544833224526137582834770402681005343930059463684528957271778199162575053306238099823295117697031968370690372250916935800738698142103275969223264184374648246277564306900886005299731265812255274723175925185522344831066577166867786835955092059346244885587228196357297758371381557924676260190209536670230008561217008649261974735505203813478978893582292682827884118215872470401293272325715864815977064075988643101088355047954735427424641386870772845440782632933485165110172437511822736907550777817722248753671107339823410418938404382732079381329288400012929311347390423061254658780185245562668131009832293474920208834795460061115101364091252176594144096675899952570380792978037217747311595899301451192342027799533264325948876556110474850761538179748318187805312451895898751337975457949549497666542175077894987697085521882531938339334715190663665300179658557458036053188152532948734992896239950564081581184284728802682982779186068791931259198917308153082917381616147108543673346682338045309449569430550618884202465809290850964525390539782080230737593560891353558335337408957948041667929154230334506735825418239563481028126435029 m = pow(c,d,n) print ("%0512x"%m).decode("hex")
実行するとフラグが出力されます。
ksnctf 15 Jewel
ksnctfの15問目を解いていきます。Javaと暗号の問題です。
アプリのインストールと実行
androidでapkファイルをダウンロードして実行すると「Your device is not supported」と表示されてアプリが終了します。でも機種の問題ではなさそう。
apkファイルの解析
androidファイルを解析するのは比較的簡単で逆コンパイラを使えばJavaのコードにまで復元してくれます。詳しいやり方は以下から↓
Android apk の解析 - Qiita
dex2jarは.apkを.jarに、jadは.classを.javaに変換します。kali linuxには2つともデフォルトでインストールされているのでそれを利用します。
>dex2jar Jewel.apk >unzip Jewel_dex2jar.jar -d ./Classes >jad -p JewelActivity.class > JewelActivity.java
ここからはソースコードを読んで解析していきます。アセンブリ読むより100倍マシですね。
// Decompiled by Jad v1.5.8e. Copyright 2001 Pavel Kouznetsov. // Jad home page: http://www.geocities.com/kpdus/jad.html // Decompiler options: packimports(3) package info.sweetduet.ksnctf.jewel; import android.app.Activity; import android.content.res.Resources; import android.graphics.BitmapFactory; import android.os.Bundle; import android.telephony.TelephonyManager; import android.widget.ImageView; import android.widget.Toast; import java.io.InputStream; import java.math.BigInteger; import java.security.MessageDigest; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; // Referenced classes of package info.sweetduet.ksnctf.jewel: // b, a public class JewelActivity extends Activity { public JewelActivity() { } public void onCreate(Bundle bundle) { String s; super.onCreate(bundle); setContentView(0x7f030000); s = ((TelephonyManager)getSystemService("phone")).getDeviceId(); String s1; try { MessageDigest messagedigest = MessageDigest.getInstance("SHA-256"); messagedigest.update(s.getBytes("ASCII")); s1 = (new BigInteger(messagedigest.digest())).toString(16); if(!s.substring(0, 8).equals("99999991")) { (new android.app.AlertDialog.Builder(this)).setMessage("Your device is not supported").setCancelable(false).setPositiveButton("OK", new b(this)).show(); return; } } catch(Exception exception) { Toast.makeText(this, exception.toString(), 1).show(); return; } if(!s1.equals("356280a58d3c437a45268a0b226d8cccad7b5dd28f5d1b37abf1873cc426a8a5")) { (new android.app.AlertDialog.Builder(this)).setMessage("You are not a valid user").setCancelable(false).setPositiveButton("OK", new a(this)).show(); return; } InputStream inputstream = getResources().openRawResource(0x7f040000); byte abyte0[] = new byte[inputstream.available()]; inputstream.read(abyte0); SecretKeySpec secretkeyspec = new SecretKeySpec((new StringBuilder("!")).append(s).toString().getBytes("ASCII"), "AES"); IvParameterSpec ivparameterspec = new IvParameterSpec("kLwC29iMc4nRMuE5".getBytes()); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(2, secretkeyspec, ivparameterspec); byte abyte1[] = cipher.doFinal(abyte0); ImageView imageview = new ImageView(this); imageview.setImageBitmap(BitmapFactory.decodeByteArray(abyte1, 0, abyte1.length)); setContentView(imageview); return; } }
前半部分(~58行目、 InputStream inputstream = getResources().openRawResource(0x7f040000);以前)はアンドロイド端末のデバイスID(IMEI)前半が「99999991」で、ID全体をSHA256でハッシュ化した結果が「356280a58d3c437a45268a0b226d8cccad7b5dd28f5d1b37abf1873cc426a8a5」でなければブログラムを終了するというもの。
SHA256は不可逆関数で逆算できませんが、IMEIは15桁の数字なので、総当たりで求めます。
#!/usr/bin/env python import hashlib deviceid = 999999910000000 for x in range(0, 10000000): text = str((deviceid+x)) hash=hashlib.sha256(text).hexdigest() if hash == "356280a58d3c437a45268a0b226d8cccad7b5dd28f5d1b37abf1873cc426a8a5": print text break
プログラムからデバイスIDは「999999913371337」であることが分かりました。後半はこのデバイスIDを使って暗号化処理を行っています。
InputStream inputstream = getResources().openRawResource(0x7f040000); byte abyte0[] = new byte[inputstream.available()]; inputstream.read(abyte0);
リソースID(0x7f040000)から何らかのストリームを取得して内容をabyte0に格納しています。リソースIDとはアプリに置かれたリソースファイルを参照するために割り振られる数字のことです。リソースIDが何を指すのかを調べるには.apkの中にある.arscファイルを解析します。
unzip Jewel.apk aapt dump --values resources Jewel.apk resource.arsc > v.txt
v.txtにリソースIDの詳細が出力されます。
resource 0x7f040000 info.sweetduet.ksnctf.jewel:raw/jewel_c: t=0x03 d=0x00000004 (s=0x0008 r=0x00) (string16) "res/raw/jewel_c.png"
ID0x7f04000はjewel_c.pngを指しているようです。jewel_c.pngは/res/raw内にあります。jewel_c.pngはそのままでは画像として表示できないので、次の処理で復号処理を行っていると思われます。
SecretKeySpec secretkeyspec = new SecretKeySpec((new StringBuilder("!")).append(s).toString().getBytes("ASCII"), "AES"); IvParameterSpec ivparameterspec = new IvParameterSpec("kLwC29iMc4nRMuE5".getBytes());
AES暗号の暗号鍵を作成し、初期化ベクトルを指定しています。初期ベクトルは「kLwC29iMc4nRMuE5」暗号鍵は「!」と「デバイスID(999999913371337)」を組み合わせたものです。
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(2, secretkeyspec, ivparameterspec); byte abyte1[] = cipher.doFinal(abyte0);
AES暗号のCBCモード、PKCS5でjewel_c.pngの復号を実行しています。
jewel_c.pngの復号
メカニズムが分かったのでコードをかいてjewel_c.pngを復号します。コードは適当です。
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; public class Main { public static void main(String[] args) throws NoSuchAlgorithmException, IOException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { //jewel_c.pngの読み込み FileInputStream fs = new FileInputStream("jewel_c.png"); byte abyte0[] = new byte[fs.available()]; fs.read(abyte0); //jewel_c.pngの復号 String s = "999999913371337"; SecretKeySpec secretkeyspec = new SecretKeySpec((new StringBuilder("!")).append(s).toString().getBytes("ASCII"), "AES"); IvParameterSpec ivparameterspec = new IvParameterSpec("kLwC29iMc4nRMuE5".getBytes()); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(2, secretkeyspec, ivparameterspec); byte abyte1[] = cipher.doFinal(abyte0); //復号したデータの書き込み FileOutputStream fileOutStm = new FileOutputStream("jewel.png"); fileOutStm.write(abyte1); } }
↑で出力したファイルを開くと、
ちゃんと復元されていました。