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バイトまでしか暗号化できない。
※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暗号化は輪をかけて遅い。)

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;
	} 
} 

トップ   差分 バックアップ リロード   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2015-10-04 (日) 05:54:58 (3121d)