One IT Thing

IT業界で飯を食う為の学習系雑記

java security

ブラウザでRSA暗号化したデータをサーバで復号する(Angular + JSEncrypt、Spring MVC)【前編】

投稿日:2019年7月11日 更新日:

セキュリティ的にクリティカルなデータをクライアントブラウザで暗号化保存するようにしてみます。

通信経路はHTTPSで暗号化されていてもスマホに重要なデータが平文で残っていたら珠に傷です。

環境

  • Windows 10
  • Eclipse 2019-03
  • Java 8
  • Maven

鍵ペアを作成

opensslが入っている環境でテキスト、バイナリの2形式で鍵ペアを作っておきます。KeyPairGeneratorで作ってもKeyStoreで作っても勿論OKです。

今回は以下の2ファイルを使います。

  1. サーバ側:private_key.pk8(バイナリ)
  2. クライアント側:public_key.pem(テキスト)

サーバ(Spring MVC側)実装

Spring Bootでサクッと雛型を作ります。EclipseでのSpring Bootアプリ作成が初見の場合は以下を参照(JSPは不要です)。

秘密鍵ファイルを配置

今回は検証なのでプロジェクト直下にprivate_key.pk8ファイルを置きます。

秘密鍵が盗まれることは誰かのクビが飛ぶことと同義です(嫌いな人だったら歓迎ですが)。HTTPからはアクセス出来ない安全な場所で管理してください。

入力パラメータクラスを作成

クライアントからリクエストされたJSONをマッピングするPOJOを作っておきます。

package com.example.demo;

public class EchoParam {

	private String email;

	private String password;

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
}

コントローラを作成

復号しない/echo、復号する/decrypted_echoを作ります。

復号する方は秘密鍵ファイルを読み、送られてきた暗号化データを復号するようにしておきます。

リクエストされたJSONは引数EchoParamとして処理に入ってきます。@RequestMappingアノテーションの引数に

consumes = MediaType.APPLICATION_JSON_VALUE

などを付けてパラメータのタイプをキャストすることが出来ますが、デフォルトがJSONなので今回は指定は不要です。

package com.example.demo;

import java.io.FileInputStream;
import java.io.IOException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.Security;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;

import javax.crypto.Cipher;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class EchoController {

	public EchoController() {
		Security.addProvider(new BouncyCastleProvider());
	}

	// 復号しないエコー
	@RequestMapping(path = "/echo")
	public EchoParam echo(@RequestBody EchoParam param) throws Exception {
		return param;
	}

	// 復号するエコー
	@RequestMapping(path = "/decrypted_echo")
	public EchoParam decryptedEcho(@RequestBody EchoParam param) throws Exception {

		String email = param.getEmail();
		String password = param.getPassword();

		System.out.println(email);
		System.out.println(password);

		byte[] b64DecodedPassword = Base64.getDecoder().decode(password);
        byte[] decrypted = decrypt(b64DecodedPassword);

        System.out.print("DECRYPTED: " + new String(decrypted));

		EchoParam ret = new EchoParam();
		ret.setEmail(email);
		ret.setPassword(new String(decrypted));

		return ret;
	}

    // 秘密鍵で暗号データを復号
    public byte[] decrypt(byte[] source) throws Exception {
        byte[] keyData = readKeyFile("private_key.pk8");
        KeySpec keyspec = new PKCS8EncodedKeySpec(keyData);
        KeyFactory keyfactory = KeyFactory.getInstance("RSA");
        Key privateKey = keyfactory.generatePrivate(keyspec);
        Cipher cipher = Cipher.getInstance("RSA//None/PKCS1PADDING", "BC");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return cipher.doFinal(source);
    }

    // 秘密鍵ファイルprivate_key.derを読み込み
    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;
    }
}

pom.xmlを編集

暗号モードに「RSA/None/PKCS1PADDING」を使うのでBouncyCastleをpom.xmlに追加します。このモードを使う理由は以下の記事の通りです。

		<dependency>
			<groupId>org.bouncycastle</groupId>
			<artifactId>bcprov-jdk15on</artifactId>
			<version>1.62</version>
		</dependency>

実装完了です。

動作確認

Spring Bootアプリを実行してHTTPサーバを起動します。

クライアントがまだ出来ていないのでChrome拡張の「Advanced Rest Client」を使ってREST APIに接続してみます。

ARCを使っても暗号化は出来ないので復号しないエコー(/echo)に対するリクエストです

Method : POST
Request URL : http://localhost:8080/echo
Body content type : application/json
パラメータ : {“email”:”hoge@hogehoge.com”, “password”;”hogehoge”}

200が返ってくればOKです。/decrypted_echoにもアクセスして、暗号化されてないデータを復号しようとして起こる500 Internal Serverエラーが返ってくることを確認しておきます。

次はフロントエンド作成

HTTP POSTされた暗号化されたデータを含むJSONを復号する準備が出来ました。

次回はデータを公開鍵で暗号化してサーバに送信するAngular側を作っていきます。

-java, security
-,

執筆者:

関連記事

HTTPS開発環境用、自己署名証明書の作成

(2020/09/18追記) mkcertを使ってより簡単に完璧なSSL証明書を作ることができました。 ささっと開発用のHTTPSサーバを作りたい時はmkcert使った方が良いですね。  O …

Spring&JSPの検証環境を速攻で作る

2019年現在、オワコン風潮の強いJSPですが使っているプロジェクトもまだまだあり、枯れた技術を好む官公庁系のプロジェクトでは根強いシェアを誇っています。実装検証をする為に環境を作る機会があったりする …

StripeとJavaで単発Web決済を一通り流してみる

会社仕事でも個人ビジネスでも、商品やサービスの対価として利用者に課金方法を提供したい時があるかと思います。 最もメジャーな決済方法であるクレジットカードで課金してもらう仕組みを導入したいところですが、 …

Javascript(暗号化JSライブラリ「Forge」)とp12ファイルで署名値を作成、Javaで検証する

前回、送信データの改ざんを検知する為、簡易的なセキュリティトークンであるPKCS#12形式のファイルを作成しました。  One IT Thing開発用のPKCS#12ファイルをOpenSSL …

CentOS7にOpenJDK11をインストール、alternatives後の再ログインでJAVA_HOMEも自動変更

OpenJDKはCentOS-Baseリポジトリのupdatesに登録されているので新規yumリポジトリ追加は不要です。 [root@spock tmp]# yumdb search from_rep …

 

shingo.nakanishi
 

東京在勤、職歴20年越え中年ITエンジニアです。まだ開発現場で頑張っています。

19歳(1996年)から書き始めた個人日記が5,000日を超え、残りの人生は発信をして行きたいと思い、令和元日からこのサイトを開始しました。勉強と試行錯誤をしながら、自分が経験したIT関連情報を投稿しています。

私と同じく、今後IT業界で生計を立てて行きたいと考えている方や、技術共有したいけどフリーランスで孤独、といった方と一緒に成長、知識共有して行けたら楽しいな、と思っています。