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
-,

執筆者:

関連記事

SpringBootアプリにBootstrap4を追加(WebJars使用)

SpringBootにCSSやJSを追加する場合は概ね以下のパターンがあるんじゃないかと思います。(bowerはもう使わない方向で) CDNで外部から読み込む<script src=&#8221 …

JUnitコードの自動生成も出来る古の神Eclipseプラグイン「CodeProAnalytix」は今でも使える

デスマーチPM「各社のPG進捗が遅れていてユニットテストを書く暇が全く無いんですよ、なにかいい手は有りませんかね・・・? 因みに本プロジェクトはカバレッジ100%で請け負っています!(キリッ)」 何故 …

主要ブラウザに保存させたパスワードの確認方法を比較してみる(Firefoxをメインブラウザにしない理由)

アクセスしたサイトのパスワードをブラウザに覚えてもらったけどなんて入れたっけ? なんてこと結構あるんじゃないかと思います。 各種ブラウザとも覚えさせたパスワードは後から確認が出来るのでそれぞれの方法を …

JavaとPerlでUDPソケット通信

目次1 はじめに2 対象読者3 実装3.1 UDPサーバ(Perl)3.2 UDPクライアント(Java)3.3 実行4 まとめ はじめに 本棚を整理していたらラクダ本が出てきました。うわー懐かしい、 …

pgcryptoの有効化と共通鍵暗号の動作確認

PostgreSQLで「pgcrypto」拡張機能を有効にすると、PostgreSQLに保存するデータを暗号化出来るようになります。 INSERTする際、共通鍵暗号はpgp_sym_encrypt関数 …


shingo nakanishi。東京で消耗中の職歴20年越え中年ITエンジニアです。「生涯現役プログラマを楽しむ」ことができる働き方探しをライフワークにしています。

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