[[Javaメモ]] > RSA公開鍵/秘密鍵による暗号化/復号化(Java)
* RSA公開鍵/秘密鍵による暗号化/復号化(Java) [#a06166dc]
#setlinebreak(on)
#contents
- 関連
-- [[共通鍵方式による暗号化/復号化]]
-- [[キーストアを使用した暗号化/復号化]]
** 秘密鍵の生成 [#j03a9d19]
#myterm(){{
openssl genrsa -out private_key.pem 1024
Generating RSA private key, 1024 bit long modulus
.++++++
......++++++
e is 65537 (0x10001)
}}
PKCS #8 形式かつ DER 形式に変換
#myterm(){{
openssl pkcs8 -in private_key.pem -topk8 -nocrypt -outform DER -out private_key.pk8
}}
&br;
** 公開鍵の作成 [#qac67d63]
#myterm(){{
openssl rsa -in private_key.pem -pubout -outform DER -out public_key.der
writing RSA key
}}
※DER形式で出力しておかないと、以下のコードでは読めない
&br;
** 暗号化/復号化(Java) [#ed8ec341]
#mycode(){{
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) [#m56ada4b]
この方式では鍵の長さに応じて暗号化できる平文の長さが決まる。
上記の例の場合、117バイトまでしか暗号化できない。
※PKCS#1のパディングで11バイト 使うので、 (鍵1024ビット - (11 * 8)) / 8 = 117バイト
この為、サイズの大きなデータを暗号化する場合は、117バイトずつ分割するか、別の方式で暗号化するしかない。
暗号化できる長さの制限に縛られたくない場合は、S/MIME の利用等も考えられるが、
現在、JAVAで利用できる S/MIME のいいカンジの実装が見当たらない。
※ http://otndnld.oracle.co.jp/document/products/as10g/1013/doc_cd/security.1013/B28615-01/smime.htm 等は使えるかも。
以下は、117バイトすつ分割して暗号化する場合はのサンプル。
※ただしめちゃくちゃ遅い!(暗号化はサイズが大きくなればなるほど時間がかかるが、RSA暗号化は輪をかけて遅い。)
#mycode(){{
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;
}
}
}}