Javaメモ > RSA公開鍵/秘密鍵による暗号化/復号化(Java) RSA公開鍵/秘密鍵による暗号化/復号化(Java) †秘密鍵の生成 †openssl genrsa -out private_key.pem 1024 Generating RSA private key, 1024 bit long modulus .++++++ ......++++++ e is 65537 (0x10001) PKCS #8 形式かつ DER 形式に変換 openssl pkcs8 -in private_key.pem -topk8 -nocrypt -outform DER -out private_key.pk8 公開鍵の作成 †openssl rsa -in private_key.pem -pubout -outform DER -out public_key.der writing RSA key ※DER形式で出力しておかないと、以下のコードでは読めない 暗号化/復号化(Java) †package example; import java.io.FileInputStream; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.Key; import java.security.KeyFactory; import java.security.spec.KeySpec; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import javax.crypto.Cipher; import org.apache.commons.codec.binary.Base64; /** * 公開鍵/秘密鍵による暗号/復合化 * * (秘密鍵) * openssl genrsa -out private_key.pem 1024 * openssl pkcs8 -in private_key.pem -topk8 -nocrypt -outform DER -out private_key.pk8 * * (公開鍵) * openssl rsa -in private_key.pem -pubout -outform DER -out public_key.der */ public class MyCryptPub3 { private static final String CIPHER_ALGORITHM = "RSA"; private static final String CIPHER_MODE = CIPHER_ALGORITHM + "/ECB/PKCS1PADDING"; // 秘密鍵 private static final String PRIVATE_KEY_FILE = "private_key.pk8"; // 公開鍵 private static final String PUBLIC_KEY_FILE = "public_key.der"; // 公開鍵(キーを文字列として扱う場合のサンプル) private static final String PUBLIC_KEY_TEXT = "30 81 9F 30 0D 06 09 2A 86 48 86 F7 0D 01 01 01 05 00 03 81 8D 00 30 81 89 02 81 81 00 C8 06 F9 09 AD AB 7C AE 9B F0 C4 7B 65 AF 0D 2D AC B3 B3 3B C7 21 EA 3D CE AD 53 DA 5A CC 28 50 5B A8 C6 F4 4E 05 2D 35 AB D9 C0 BF AC 82 CE BB 5B 27 3A 60 92 D8 96 FF B1 C8 C4 6C 95 00 2C B2 C7 31 B2 19 CF 18 58 CE 09 89 C1 04 06 C2 2F B1 D7 54 83 3A AC A5 7E 22 CD 65 B1 B6 D5 C4 71 44 31 55 94 D5 BD DC 74 92 D1 F2 75 CB 0B 1D 74 B8 B3 1E A5 7F 87 B4 F5 98 15 1A C1 7B 38 25 16 05 02 03 01 00 01"; // 秘密鍵(キーを文字列として扱う場合のサンプル) private static final String PRIVATE_KEY_TEXT = "30 82 02 78 02 01 00 30 0D 06 09 2A 86 48 86 F7 0D 01 01 01 05 00 04 82 02 62 30 82 02 5E 02 01 00 02 81 81 00 C8 06 F9 09 AD AB 7C AE 9B F0 C4 7B 65 AF 0D 2D AC B3 B3 3B C7 21 EA 3D CE AD 53 DA 5A CC 28 50 5B A8 C6 F4 4E 05 2D 35 AB D9 C0 BF AC 82 CE BB 5B 27 3A 60 92 D8 96 FF B1 C8 C4 6C 95 00 2C B2 C7 31 B2 19 CF 18 58 CE 09 89 C1 04 06 C2 2F B1 D7 54 83 3A AC A5 7E 22 CD 65 B1 B6 D5 C4 71 44 31 55 94 D5 BD DC 74 92 D1 F2 75 CB 0B 1D 74 B8 B3 1E A5 7F 87 B4 F5 98 15 1A C1 7B 38 25 16 05 02 03 01 00 01 02 81 80 49 7B A6 49 93 EF 87 E2 6D 8F 49 DF 3B 3F CF CB 93 C2 80 79 D9 71 F0 27 BC A1 98 48 83 4A B5 14 B0 57 94 9F 73 7A 1B 5D B8 40 4A BB 1F 98 FE 71 7D CF 2F 77 02 FB 66 9D 90 A6 7C E2 96 EA 75 80 A0 E8 FA 4C BA 60 DB 6C A8 DC B9 5A B6 B3 A8 1E 5C 5A E8 AD 50 3D 92 26 D8 32 6F 98 44 64 94 5A 1F B3 28 3D 8C 97 05 43 07 80 45 A7 1F 99 C0 75 21 40 1E 27 DE 01 E2 02 DC E2 D0 33 12 11 51 11 02 41 00 FA 48 7E 36 E8 F9 6C 95 2D 57 2C 70 DC 4B 56 10 50 EF A9 85 A5 1C 4F 44 94 4C 9D F5 D9 A2 C4 51 5F 54 D7 13 D3 A0 10 41 2B 74 13 5C C8 44 FF BA 94 13 A7 63 50 96 97 68 FB 22 DE EF 2F D4 23 93 02 41 00 CC 98 9C EC 14 1F A0 69 FB 46 14 38 25 97 24 F9 E6 C2 06 EA 91 44 2B B7 5D CA 18 F4 DF C0 F3 2E BF 51 57 09 05 06 66 2B 26 9B 81 B2 B9 6A 57 51 35 E9 70 F2 5C B2 C3 04 F6 C6 CA 9F BE 43 8F 07 02 41 00 DC CD 01 94 3D BA 76 21 B1 2B 5B C8 81 80 70 FF D9 F7 65 2D C3 39 13 71 64 07 A7 BF 51 EE 37 95 B6 2D A9 C5 13 08 FD EE 10 80 C9 E8 2B C2 3B 7D 85 CF 44 F7 E8 0B C2 AD DA 08 AC 76 85 52 78 C3 02 41 00 C5 80 D9 93 28 45 F3 93 FB 86 06 04 C1 7C EB AB F6 2F FD 7F 38 E6 47 11 47 0F CE 11 AD 62 55 1F 1E 7F 05 F6 E6 0B EC 5A E1 75 22 BA 06 35 7A BF 21 BD 0D 54 59 5A 13 DA D9 E2 C3 3D 7B ED 39 C7 02 41 00 F1 12 D6 B7 8B C7 E7 AE 08 F3 EA 12 84 DB E3 62 FE 41 8B B3 F5 CE 71 ED 47 D9 57 DF 93 ED D5 7C 72 FB 75 8D DC BB 34 1F 9B 78 DC 8D 1B 5C 6D FD 37 F7 4E 95 2B D4 B0 28 1A 29 EC EC 9C 0C 53 45"; public static void main(String[] args) throws Exception { MyCryptPub3 app = new MyCryptPub3(); String clearText = "clear text!"; byte[] encrypted = app.encrypt(clearText.getBytes()); System.out.print("ENCRYPTED: "); System.out.println(Base64.encodeBase64String(encrypted)); byte[] decrypted = app.decrypt(encrypted); System.out.print("DECRYPTED: "); System.out.println(new String(decrypted)); } public byte[] encrypt(byte[] source) throws Exception { //byte[] keyData = hexToByte(PUBLIC_KEY_TEXT); //System.out.println("public key length : " + PUBLIC_KEY_TEXT.length()); byte[] keyData = readKeyFile(PUBLIC_KEY_FILE); KeySpec keyspec = new X509EncodedKeySpec(keyData); System.out.println("encrypt key : " + toHexString(keyData)); KeyFactory keyfactory = KeyFactory.getInstance(CIPHER_ALGORITHM); Key publicKey = keyfactory.generatePublic(keyspec); Cipher cipher = Cipher.getInstance(CIPHER_MODE); cipher.init(Cipher.ENCRYPT_MODE, publicKey); return cipher.doFinal(source); } public byte[] decrypt(byte[] source) throws Exception { //byte[] keyData = hexToByte(PRIVATE_KEY_TEXT); //System.out.println("public key length : " + PRIVATE_KEY_TEXT.length()); byte[] keyData = readKeyFile(PRIVATE_KEY_FILE); KeySpec keyspec = new PKCS8EncodedKeySpec(keyData); System.out.println("decript key : " + toHexString(keyData)); KeyFactory keyfactory = KeyFactory.getInstance(CIPHER_ALGORITHM); Key privateKey = keyfactory.generatePrivate(keyspec); Cipher cipher = Cipher.getInstance(CIPHER_MODE); cipher.init(Cipher.DECRYPT_MODE, privateKey); return cipher.doFinal(source); } private static byte[] readKeyFile(String filename) throws IOException { byte[] data = null; FileInputStream in = new FileInputStream(filename); data = new byte[in.available()]; in.read(data); in.close(); return data; } public static String toHexString(byte[] b) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < b.length; i++) { sb.append(String.format("%02X", b[i])).append(" "); //sb.append(Integer.toHexString(b[i] & 0xff)).append(" "); } return sb.toString(); } public static byte[] hexToByte(String hexString) { String[] hexArray = hexString.split(" "); byte[] bytes = new byte[hexArray.length]; for (int i = 0; i < hexArray.length; i++) { String str = hexArray[i]; bytes[i] = (byte) Integer.parseInt(str, 16); } return bytes; } } 長いデータの暗号化/復号化(Java) †この方式では鍵の長さに応じて暗号化できる平文の長さが決まる。 この為、サイズの大きなデータを暗号化する場合は、117バイトずつ分割するか、別の方式で暗号化するしかない。 暗号化できる長さの制限に縛られたくない場合は、S/MIME の利用等も考えられるが、 以下は、117バイトすつ分割して暗号化する場合はのサンプル。 package example; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.IOException; import java.security.Key; import java.security.KeyFactory; import java.security.spec.KeySpec; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.List; import javax.crypto.Cipher; /** * 公開鍵/秘密鍵による暗号/復合化 * (長いデータの暗号化にも対応) * * (秘密鍵) * openssl genrsa -out private_key.pem 1024 * openssl pkcs8 -in private_key.pem -topk8 -nocrypt -outform DER -out private_key.pk8 * * (公開鍵) * openssl rsa -in private_key.pem -pubout -outform DER -out public_key.der */ public class MyCryptPub3b { private static final String CIPHER_ALGORITHM = "RSA"; private static final String CIPHER_MODE = CIPHER_ALGORITHM + "/ECB/PKCS1PADDING"; // 公開鍵(キーを文字列として扱う場合のサンプル) private static final String PUBLIC_KEY_TEXT = "30819F300D06092A864886F70D010101050003818D0030818902818100C806F909ADAB7CAE9BF0C47B65AF0D2DACB3B33BC721EA3DCEAD53DA5ACC28505BA8C6F44E052D35ABD9C0BFAC82CEBB5B273A6092D896FFB1C8C46C95002CB2C731B219CF1858CE0989C10406C22FB1D754833AACA57E22CD65B1B6D5C47144315594D5BDDC7492D1F275CB0B1D74B8B31EA57F87B4F598151AC17B382516050203010001"; // 秘密鍵(キーを文字列として扱う場合のサンプル) private static final String PRIVATE_KEY_TEXT = "30820278020100300D06092A864886F70D0101010500048202623082025E02010002818100C806F909ADAB7CAE9BF0C47B65AF0D2DACB3B33BC721EA3DCEAD53DA5ACC28505BA8C6F44E052D35ABD9C0BFAC82CEBB5B273A6092D896FFB1C8C46C95002CB2C731B219CF1858CE0989C10406C22FB1D754833AACA57E22CD65B1B6D5C47144315594D5BDDC7492D1F275CB0B1D74B8B31EA57F87B4F598151AC17B382516050203010001028180497BA64993EF87E26D8F49DF3B3FCFCB93C28079D971F027BCA19848834AB514B057949F737A1B5DB8404ABB1F98FE717DCF2F7702FB669D90A67CE296EA7580A0E8FA4CBA60DB6CA8DCB95AB6B3A81E5C5AE8AD503D9226D8326F984464945A1FB3283D8C970543078045A71F99C07521401E27DE01E202DCE2D03312115111024100FA487E36E8F96C952D572C70DC4B561050EFA985A51C4F44944C9DF5D9A2C4515F54D713D3A010412B74135CC844FFBA9413A76350969768FB22DEEF2FD42393024100CC989CEC141FA069FB461438259724F9E6C206EA91442BB75DCA18F4DFC0F32EBF5157090506662B269B81B2B96A575135E970F25CB2C304F6C6CA9FBE438F07024100DCCD01943DBA7621B12B5BC8818070FFD9F7652DC33913716407A7BF51EE3795B62DA9C51308FDEE1080C9E82BC23B7D85CF44F7E80BC2ADDA08AC76855278C3024100C580D9932845F393FB860604C17CEBABF62FFD7F38E64711470FCE11AD62551F1E7F05F6E60BEC5AE17522BA06357ABF21BD0D54595A13DAD9E2C33D7BED39C7024100F112D6B78BC7E7AE08F3EA1284DBE362FE418BB3F5CE71ED47D957DF93EDD57C72FB758DDCBB341F9B78DC8D1B5C6DFD37F74E952BD4B0281A29ECEC9C0C5345"; // 暗号化ブロックの区切り文字 private static final byte[] BLOCK_DELIMITER = "__CRLF__".getBytes(); /** * 暗号化/復号化の確認。<br /> */ public static void main(String[] args) throws Exception { // 文字列の暗号/復号化 // (短いデータの暗号/復号化の確認) { String clearText = "clear text!"; byte[] clearBytes = clearText.getBytes(); byte[] encrypted = encrypt(clearBytes); byte[] decrypted = decrypt(encrypted); if (new String(clearBytes).equals(new String(decrypted))) { System.out.println("文字列の暗号/復号化 OK!"); } else { System.err.println("文字列の暗号/復号化 NG!"); } } // 画像ファイルの暗号/復号化 // (長いデータの暗号/復号化の確認) { String imageFile = "sample.png"; byte[] clearBytes = readBinaryFile(imageFile); byte[] encrypted = encrypt(clearBytes); byte[] decrypted = decrypt(encrypted); if (toHexString(clearBytes).equals(toHexString(decrypted))) { System.out.println("画像の暗号/復号化 OK!"); } else { System.err.println("画像の暗号/復号化 NG!"); } } } /** * 暗号化。<br /> * @param sourceInit 暗号化前のデータ * @return 暗号化されたデータ * @throws Exception */ public static byte[] encrypt(byte[] sourceAll) throws Exception { int BLOCK_SIZE = 117; // 暗号化用のオブジェクト生成 byte[] keyData = hexToByte(PUBLIC_KEY_TEXT); KeySpec keyspec = new X509EncodedKeySpec(keyData); KeyFactory keyfactory = KeyFactory.getInstance(CIPHER_ALGORITHM); Key publicKey = keyfactory.generatePublic(keyspec); Cipher cipher = Cipher.getInstance(CIPHER_MODE); cipher.init(Cipher.ENCRYPT_MODE, publicKey); // バイト配列をブロックサイズ毎に分割 List<byte[]> sourceList = new ArrayList<byte[]>(); int listLen = (int)Math.ceil((double)sourceAll.length / (double)BLOCK_SIZE); int sourceLen = sourceAll.length; ByteArrayInputStream is = new ByteArrayInputStream(sourceAll); for (int i = 0; i < listLen; i++) { int readSize = sourceLen < BLOCK_SIZE ? sourceLen : BLOCK_SIZE; byte[] b = new byte[readSize]; is.read(b, 0, readSize); sourceList.add(b); sourceLen -= readSize; } // ブロックサイズ毎に暗号化 ByteArrayOutputStream os = new ByteArrayOutputStream(); for (byte[] source : sourceList) { byte[] encryptedBytes = cipher.doFinal(source); os.write(encryptedBytes); os.write(BLOCK_DELIMITER); // ブロック間にデリミタを挟む } os.flush(); os.close(); byte[] encriptedBytes = os.toByteArray(); return encriptedBytes; } /** * 復号化。<br /> * @param source 暗号化されたデータ * @return 復号化後のデータ * @throws Exception */ public static byte[] decrypt(byte[] source) throws Exception { // 復号化用のオブジェクト準備 byte[] keyData = hexToByte(PRIVATE_KEY_TEXT); KeySpec keyspec = new PKCS8EncodedKeySpec(keyData); KeyFactory keyfactory = KeyFactory.getInstance(CIPHER_ALGORITHM); Key privateKey = keyfactory.generatePrivate(keyspec); Cipher cipher = Cipher.getInstance(CIPHER_MODE); cipher.init(Cipher.DECRYPT_MODE, privateKey); // 暗号化ブロックの区切りを識別する為に、いったん16進文字列に変換 String sourceHex = toHexString(source); // ブロック毎に復号化 ByteArrayOutputStream byteArray = new ByteArrayOutputStream(); String[] sourceTextList = sourceHex.split(toHexString(BLOCK_DELIMITER)); for (String sourceStr : sourceTextList) { if (sourceStr.length() > 0) { byte[] sourceByte = hexToByte(sourceStr); byte[] decryptBytes = cipher.doFinal(sourceByte); byteArray.write(decryptBytes); } } byteArray.flush(); byteArray.close(); byte[] decryprBytes = byteArray.toByteArray(); return decryprBytes; } /** * ファイルを読み込む。<br /> * @param filename ファイルPATH * @return ファイルデータ * @throws IOException */ private static byte[] readBinaryFile(String filename) throws IOException { byte[] data = null; FileInputStream in = new FileInputStream(filename); data = new byte[in.available()]; in.read(data); in.close(); return data; } /** * バイト配列を16進文字列に変換する。<br /> * @param b バイト配列 * @return 16進文字列 */ public static String toHexString(byte[] b) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < b.length; i++) { //sb.append(String.format("%02X", b[i])).append(" "); // Integer.toHexString より数倍〜数十倍遅い String str = Integer.toHexString(b[i] & 0xff); if(str.length() < 2) { sb.append("0"); } sb.append(str); } return sb.toString(); } /** * 16進文字列をバイト配列に変換する。<br /> * @param hexString 16進文字列 * @return バイト配列 */ public static byte[] hexToByte(String hexString) { int size = hexString.length() / 2; byte[] bytes = new byte[size]; for (int i = 0; i < bytes.length; i++) { String str = hexString.substring(i * 2, i * 2 + 2); bytes[i] = (byte) Integer.parseInt(str, 16); } return bytes; } } |