Java秘密鍵で暗号

公開鍵暗号方式の機密鍵で暗号

DotNet RSACryptoServiceProviderで作成した秘密鍵 のファイル(privateKey.xml)を読み込んで、希望のファイルを暗号化します。
ここよりダウンロードして実験できます。
(ソース:RsaSEncrypt.java

なお、自身のprivateKey.xmlファイルが実行位置に存在しないと、実行できません。
また、暗号化に使う平文のサイズも128以内のサイズを指定ください。 (これは、下記のプログラム上の制約です。)

以下に実行例を示します。

Z:\security>java RsaSEncrypt
ルート要素のタグ名:RSAKeyValue
D= PQDJvs3JNOW7Cffe4HJrxyFWGjRNPfmYJxciKpcoa3B9wFiHLAyR+A2MwlDWSE58mgvDEp8ertwfv
4FeT27xnM1DsydFwZCZMsZTRYlfx+Cq3bTEnL4N6WdLG1p9ktPdbqq6jlQ3FQpJl38RDA8C5cuRekkZk
NOMOyTlOGzJ8gE=
Modulus= kUeqVlfMc3RsGBo0Q+voiNV9LoH9gmv8i7gobUNG5rIoNsIRU7GUJpU9qoX7htlpZROqvEA
gR7GfFNbL+kf+Z0nnUXrixXv+9QPVGdNooeGvSqTx1tagnXUpZxJrk8ga1SMqsjaUX2hLPl80MCpZzTv
6YQ57Elb4C735/92VHHs=
暗号化するファイルを読み取ります。

privateKey.xmlで、
Z:\:\security\A_MD5.txtのファイルを暗号化し、
Z:\security\A_MD5.txt_private_encrypt.txt のファイル生成終了

Z:\security>

ここでは、次のMD5のハッシュ値のファイル(A_MD5.txt)を選択している例です。 以下がここで使ったA_MD5.txtの内容です。

2dffd34f9d51cc839696c265aef6b5d

上記実行で、次の『A_MD5.txt_private_encrypt.txt』の名前の暗号化された ファイルが作成されます。

dGtUFcsc97xRXJoJmtrrOVr2foKQm9z11MvrwM2s/pgkB+LgrawUM/6TpXyq5wG+09hNYDtXYfY7BWejNWBP+YaqElj5UwoVjFHuhPv5h8UkUCQFSjhkemzwovCEPyE0AUwcHcnXWWLt5ekwFoR2T3JA0oWGFv6vKvLm2j8rxtQ=

公開鍵暗号方式の公開鍵で復号 (実験)

Javaで作成した復号プログラムを、 ここよりダウンロードして実験できます。
(ソース:RsaPDecrypt.java
実行例を示します。

次のように実行します。まず、鍵を選択するダイアログが現れます。 それに対して、 暗号化で使った秘密キーと対になる公開鍵を選ばなければなりません。
以下では、『suzuki_publicKey.xml』のファイルを選んでいます。
次に上記で作成した『A_MD5.txt_private_encrypt.txt』を選択しています。
これによって出来上がる『A_MD5.txt_private_encrypt.txt_public_decrypt.txt』が 復号したファイルで、これは 『A_MD5.txt』と内容が一致するはずです。

Z:\security>java RsaPDecrypt
公開鍵のファイルを読み取ります。

ルート要素のタグ名:RSAKeyValue
e= AQAB
Modulus= kUeqVlfMc3RsGBo0Q+voiNV9LoH9gmv8i7gobUNG5rIoNsIRU7GUJpU9qoX7htlpZROqvEA
gR7GfFNbL+kf+Z0nnUXrixXv+9QPVGdNooeGvSqTx1tagnXUpZxJrk8ga1SMqsjaUX2hLPl80MCpZzTv
6YQ57Elb4C735/92VHHs=
復号化するファイルを読み取ります。

復号対象:dGtUFcsc97xRXJoJmtrrOVr2foKQm9z11MvrwM2s/pgkB+LgrawUM/6TpXyq5wG+09hNYDt
XYfY7BWejNWBP+YaqElj5UwoVjFHuhPv5h8UkUCQFSjhkemzwovCEPyE0AUwcHcnXWWLt5ekwFoR2T3J
A0oWGFv6vKvLm2j8rxtQ=
Z:\work1\suzuki_publicKey.xmlで、
Z:\work1\A_MD5.txt_private_encrypt.txtのファイルを復号し、
Z:\work1\A_MD5.txt_private_encrypt.txt_public_decrypt.txt のファイル生成終了

Z:\security>

以上で作成されたファイル「A_MD5.txt_private_encrypt.txt_public_decrypt.txt」の 内容を以下に示します。

2dffd34f9d51cc839696c265aef6b5d

上記各ソースファイルを以下に示します。

RsaSEncrypt.java

import java.io.*;
import java.math.BigInteger;
import javax.swing.*;

//XMLファイルをパースしてDOMツリー取得に必要なインポート
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;

//ドキュメントと要素操作に必要なインポート
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

// DotNet RSACryptoServiceProviderで作成した privateKey.xml の秘密鍵を読み取り
// その秘密鍵で、ダイアログで指定したファイルを暗号化し、「_private_encrypt.txt」を追加した名前で
// 保存する。なお保存は、バイナリをbase64で変換して行う。


public class RsaSEncrypt {

	static BigInteger m;//Modulus 公開鍵

	static BigInteger d;//D 秘密鍵

	static final char[] B64 = { //変換に使うテーブル 64進(6bit分)を  16桁 4行で表現している 
		'A' ,'B' ,'C' ,'D' ,'E' ,'F' ,'G' ,'H' ,'I' ,'J' ,'K' ,'L' ,'M' ,'N' ,'O' ,'P',//00〜0Fの64進に対応する文字
		'Q' ,'R' ,'S' ,'T' ,'U' ,'V' ,'W' ,'X' ,'Y' ,'Z' ,'a' ,'b' ,'c' ,'d' ,'e' ,'f',//10〜1Fの64進対応に対応する文字
		'g' ,'h' ,'i' ,'j' ,'k' ,'l' ,'m' ,'n' ,'o' ,'p' ,'q' ,'r' ,'s' ,'t' ,'u' ,'v',//20〜2Fの64進対応に対応する文字
		'w' ,'x' ,'y' ,'z' ,'0' ,'1' ,'2' ,'3' ,'4' ,'5' ,'6' ,'7' ,'8' ,'9' ,'+' ,'/' //30〜3Fの64進対応に対応する文字
		};

	//引数のbyte配列を、Base64の文字列に変換して返す。
	public static String encode64(byte a[]){
		if (a.length == 0) return "";
		StringBuffer s = new StringBuffer();//変換した文字列記憶用
		int cnt3 = 0;	//3yteカウント用
		long data = 0;	//変換対象用:3byteを設定し、4文字で取り出す
		int idx = 0;
		do{
			//data に 3byte分を設定
			data <<= 8;
			if (idx < a.length){
				if (a[idx] >= 0)	{//Javaは符号なしがないので、if文で処理を変える
					data += a[idx];
				}else{
					data += 256 + a[idx]; //符号なしのコードに変換して加算
				}
			}
			cnt3++;
			if (cnt3 == 3){//3byteごとに変換

				//dataを4個の64進に対応する文字に変換
				int i = (int)(data / (64 * 64 * 64));
				//System.out.println(data + "," + i + "," + 256 * 256 * 256);
				s.append(B64[i]);//1文字目

				data = data % (64 * 64 * 64);
				i = (int)(data / (64 * 64));
				s.append(B64[i]);//2文字目

				data = data % (64 * 64);
				i = (int)(data / (64));
				s.append(B64[i]);//3文字目

				data = data % 64;
				s.append(B64[(int)data]);//4文字目

				data = 0;	//次の変換データの準備
				cnt3 = 0;
			}
			idx++;
		} while (idx < a.length || cnt3 != 0);
		int len = s.length(); //文字列の長さ
		String rtnval = "";
		if (a.length % 3 == 2){
			return s.substring(0, len - 1) + "=";
		}else if (a.length % 3 == 1){
			return s.substring(0, len - 2) + "==";
		}else{
			return s.toString();
		}
	}

	//Base64の文字列から、バイナリデータを求める。
	public static byte[] decode64(String s){
		int n = s.length() * 3 / 4;
		if (s.endsWith("==")) n -= 2;
		else if (s.endsWith("=")) n -= 1;
		byte[] bi = new byte[n];
		int iset = 0;
		int len = s.length();
		int data = 0;
		int icount = 0;
		int i;
		try{
			for (i = 0; i < len; i++){ //文字を順番に処理する。
				char c = s.charAt(i);
				data <<= 6;// 6ビットシフト
				icount++;
				if (c != '='){
					//Base64の文字から、テーブル内のインデックスを求める。
					if (c >= 'A' && c <= 'Z')	{
						data |= c - 'A';
					}else if (c >= 'a' && c <= 'z'){
						data |= c - 'a' + 0x1a;
					}else if (c >= '0' && c <= '9'){
						data |= c - '0' + 0x34;
					}else if (c == '+'){
						data |= 0x3e;
					}else if (c == '/'){
						data |= 0x3f;
					}
				}
				if (icount == 4){
					icount = 0;
					bi[iset++] = (byte)((data >> 16) & 0x00ff);
					bi[iset++] = (byte)((data >> 8) & 0x00ff);
					bi[iset++] = (byte)(data & 0x00ff);
					data = 0;
				}
			}
		}
		catch (ArrayIndexOutOfBoundsException e){ }
		return bi;
	}
	
	
	// privateKey.xml のファイルを読み取り、mとdのクラス変数へ記憶
	public static void loadPrivateKey(){
		try {
			//DOM オブジェクトツリーをパースするインスタンスを生成するDocumentBuilderオブジェクトを生成
			DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = dbfactory.newDocumentBuilder();
			
			// パースを実行してDocumentオブジェクトを取得
			Document document = builder.parse(new File("privateKey.xml"));
			// ルート要素を取得
			Element rootElement = document.getDocumentElement();
			System.out.println("ルート要素のタグ名:" + rootElement.getTagName());
			
			// nodeListの子孫となる "page" の名前の要素すべてを NodeListのインスタンスとして取得
			NodeList nodeList = rootElement.getElementsByTagName("D");
			String v = nodeList.item(0).getFirstChild().getNodeValue();
			System.out.println("D= " + v);
			d = new BigInteger(1,decode64(v));//秘密鍵

			nodeList = rootElement.getElementsByTagName("Modulus");
			v = nodeList.item(0).getFirstChild().getNodeValue();
			System.out.println("Modulus= " + v);
			m = new BigInteger(1,decode64(v));//公開鍵の一部
		}
		catch (Exception e){
			System.out.println("privateKey.xmlのファイルが見つからない???。" + e.toString());
			System.exit(0);
		}
	}
	
	public static void main(String[] args) throws Exception{
		loadPrivateKey(); //キー情報取得
		
		System.out.println("暗号化するファイルを読み取ります。");
		javax.swing.JFileChooser chooser = new javax.swing.JFileChooser(new File("."));
		chooser.setDialogTitle("暗号化するファイルをファイル選択ください。");
		System.out.println();
		if (chooser.showOpenDialog(null) == JFileChooser.CANCEL_OPTION)
			System.exit(0);
		String srcFile = chooser.getSelectedFile().getPath();//keyファイルパス
		File file = new File(srcFile);
		int  size = (int)file.length();
		byte bi[] = new byte[size];//ファイルサイズのbyte配列を用意。
		FileInputStream is = new FileInputStream(srcFile);
		is.read(bi);//ファイルバイナリーを一括読み取る。
		is.close();	//ファイルを閉じる。

		BigInteger src = new BigInteger(1,bi);//平文
		BigInteger encrypt = src.modPow(d, m); //暗号化
		
		String keyBsae64 = encode64(encrypt.toByteArray());
		
		FileOutputStream out = new FileOutputStream(srcFile + "_private_encrypt.txt");
		out.write(keyBsae64.getBytes());
		out.close();

		System.out.println("privateKey.xmlで、\n" 
		+ srcFile + "のファイルを暗号化し、\n" 
		+ srcFile + "_private_encrypt.txt のファイル生成終了");
	}
}

RsaPDecrypt.java

import java.io.*;
import java.math.BigInteger;
import javax.swing.*;

//XMLファイルをパースしてDOMツリー取得に必要なインポート
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;

//ドキュメントと要素操作に必要なインポート
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

// DotNet RSACryptoServiceProviderで作成した xx_p_publicKey.xml の公開鍵を読み取り
// その公開鍵で、ダイアログで指定したファイルを復号化し、「_public_decrypt.txt」を追加した名前で
// 保存する。

public class RsaPDecrypt {
	
	static BigInteger m;//Modulus 公開鍵

	static BigInteger e;//公開鍵 PUBLIC_EXPONENT=<Exponent>

	static final char[] B64 = { //変換に使うテーブル 64進(6bit分)を  16桁 4行で表現している 
		'A' ,'B' ,'C' ,'D' ,'E' ,'F' ,'G' ,'H' ,'I' ,'J' ,'K' ,'L' ,'M' ,'N' ,'O' ,'P',//00〜0Fの64進に対応する文字
		'Q' ,'R' ,'S' ,'T' ,'U' ,'V' ,'W' ,'X' ,'Y' ,'Z' ,'a' ,'b' ,'c' ,'d' ,'e' ,'f',//10〜1Fの64進対応に対応する文字
		'g' ,'h' ,'i' ,'j' ,'k' ,'l' ,'m' ,'n' ,'o' ,'p' ,'q' ,'r' ,'s' ,'t' ,'u' ,'v',//20〜2Fの64進対応に対応する文字
		'w' ,'x' ,'y' ,'z' ,'0' ,'1' ,'2' ,'3' ,'4' ,'5' ,'6' ,'7' ,'8' ,'9' ,'+' ,'/' //30〜3Fの64進対応に対応する文字
		};

	//引数のbyte配列を、Base64の文字列に変換して返す。
	public static String encode64(byte a[])	{
		if (a.length == 0) return "";
		StringBuffer s = new StringBuffer();//変換した文字列記憶用
		int cnt3 = 0;	//3yteカウント用
		long data = 0;	//変換対象用:3byteを設定し、4文字で取り出す
		int idx = 0;
		do	{
			//data に 3byte分を設定
			data <<= 8;
			if (idx < a.length){
				if (a[idx] >= 0){//Javaは符号なしがないので、if文で処理を変える
					data += a[idx];
				}else{
					data += 256 + a[idx]; //符号なしのコードに変換して加算
				}
			}
			cnt3++;
			if (cnt3 == 3){//3byteごとに変換

				//dataを4個の64進に対応する文字に変換
				int i = (int)(data / (64 * 64 * 64));
				//System.out.println(data + "," + i + "," + 256 * 256 * 256);
				s.append(B64[i]);//1文字目

				data = data % (64 * 64 * 64);
				i = (int)(data / (64 * 64));
				s.append(B64[i]);//2文字目

				data = data % (64 * 64);
				i = (int)(data / (64));
				s.append(B64[i]);//3文字目

				data = data % 64;
				s.append(B64[(int)data]);//4文字目

				data = 0;	//次の変換データの準備
				cnt3 = 0;
			}
			idx++;
		} while (idx < a.length || cnt3 != 0);
		int len = s.length(); //文字列の長さ
		String rtnval = "";
		if (a.length % 3 == 2){
			return s.substring(0, len - 1) + "=";
		}else if (a.length % 3 == 1){
			return s.substring(0, len - 2) + "==";
		}else{
			return s.toString();
		}
	}

	//Base64の文字列から、バイナリデータを求める。
	public static byte[] decode64(String s){
		int n = s.length() * 3 / 4;
		if (s.endsWith("==")) n -= 2;
		else if (s.endsWith("=")) n -= 1;
		byte[] bi = new byte[n];
		int iset = 0;
		int len = s.length();
		int data = 0;
		int icount = 0;
		int i;
		try	{
			for (i = 0; i < len; i++){ //文字を順番に処理する。
				char c = s.charAt(i);
				data <<= 6;// 6ビットシフト
				icount++;
				if (c != '='){
					//Base64の文字から、テーブル内のインデックスを求める。
					if (c >= 'A' && c <= 'Z')	{
						data |= c - 'A';
					}else if (c >= 'a' && c <= 'z'){
						data |= c - 'a' + 0x1a;
					}else if (c >= '0' && c <= '9'){
						data |= c - '0' + 0x34;
					}else if (c == '+'){
						data |= 0x3e;
					}else if (c == '/'){
						data |= 0x3f;
					}
				}
				if (icount == 4){
					icount = 0;
					bi[iset++] = (byte)((data >> 16) & 0x00ff);
					bi[iset++] = (byte)((data >> 8) & 0x00ff);
					bi[iset++] = (byte)(data & 0x00ff);
					data = 0;
				}
			}
		}
		catch (ArrayIndexOutOfBoundsException e){ }
		return bi;
	}
	
	
	// key のファイルを読み取り、公開鍵(eとdのクラス変数)へ記憶
	public static void loadPublicKey(File key){
		try {
			//DOM オブジェクトツリーをパースするインスタンスを生成するDocumentBuilderオブジェクトを生成
			DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = dbfactory.newDocumentBuilder();
			
			// パースを実行してDocumentオブジェクトを取得
			Document document = builder.parse(key);
			// ルート要素を取得
			Element rootElement = document.getDocumentElement();
			System.out.println("ルート要素のタグ名:" + rootElement.getTagName());
			
			// nodeListの子孫となる "page" の名前の要素すべてを NodeListのインスタンスとして取得
			NodeList nodeList = rootElement.getElementsByTagName("Exponent");
			String v = nodeList.item(0).getFirstChild().getNodeValue();
			System.out.println("e= " + v);
			e = new BigInteger(1,decode64(v));//公開鍵の一部

			nodeList = rootElement.getElementsByTagName("Modulus");
			v = nodeList.item(0).getFirstChild().getNodeValue();
			System.out.println("Modulus= " + v);
			m = new BigInteger(1,decode64(v));//公開鍵の一部
		}
		catch (Exception e){
			System.out.println(e.toString());
		}
	}
	
	public static void main(String[] args) throws Exception{
		
		System.out.println("公開鍵のファイルを読み取ります。");
		javax.swing.JFileChooser chooser = new javax.swing.JFileChooser(new File("."));
		chooser.setDialogTitle("公開鍵のxmlファイルをファイル選択ください。");
		System.out.println();
		if (chooser.showOpenDialog(null) == JFileChooser.CANCEL_OPTION)
			System.exit(0);
		String keyfile = chooser.getSelectedFile().getPath();//keyファイルパス

		loadPublicKey(new File(keyfile)); //公開鍵の情報取得
		
		System.out.println("復号化するファイルを読み取ります。");
		javax.swing.JFileChooser chooser2 = new javax.swing.JFileChooser(new File("."));
		chooser2.setDialogTitle("復号化するファイルをファイル選択ください。");
		System.out.println();
		if (chooser2.showOpenDialog(null) == JFileChooser.CANCEL_OPTION)
			System.exit(0);
		String srcFile = chooser2.getSelectedFile().getPath();//keyファイルパス
		File file = new File(srcFile);
		int  size = (int)file.length();
		byte b64[] = new byte[size];//ファイルサイズのbyte配列を用意。
		FileInputStream is = new FileInputStream(srcFile);
		is.read(b64);//base64ファイルを一括読み取る。
		is.close();	//ファイルを閉じる。
		String s64 = new String(b64);
		System.out.println("復号対象:" + s64);
		
		byte bi[] = decode64(s64);
		BigInteger src = new BigInteger(1,bi);//暗号文
		BigInteger encrypt = src.modPow(e, m); //復号化
			
		FileOutputStream out = new FileOutputStream(srcFile + "_public_decrypt.txt");
		out.write(encrypt.toByteArray());
		out.close();

		System.out.println(keyfile + "で、\n" 
				+ srcFile + "のファイルを復号し、\n"
				+ srcFile + "_public_decrypt.txt のファイル生成終了");
	}
}