Javaでパスワード登録&ログイン処理を行う方法(AES暗号化)

ヘッダー広告
スポンサードリンク

前回の記事(JavaでAES(共通鍵暗号)による暗号化)にて、JavaでAES暗号化の方法をご紹介致しましたが、今回はそれの応用編になります。
JavaのWebアプリケーションで、AES暗号化を利用してユーザー登録処理とログイン処理を行うコードをご紹介致します。

まずは画面のイメージからご紹介致します。

パスワード登録&ログイン処理のイメージ

ユーザー登録画面

1.ユーザー登録画面

ユーザー登録画面

2.ユーザーIDとパスワードを入力

ユーザー情報入力

※パスワードには、「Test1234」を入力しています。
 セキュリティ的に入力中のパスワードは表示されないように、input属性に [type=”password”]を設定しています。

●DB状態


3.ユーザー登録完了


●DB状態


ログイン画面

1.ログインを実施。ただし一回失敗させるパターン


※パスワードには、「aaa」を入力しています。

2.ログイン失敗後


3.ログイン成功後


※パスワードに「Test1234」を入力後の画面表示です。

ソースコード

サーバーサイド

●RegistUser


package webapp;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import dao.UserPasswordDao;
import util.CipherUtil;

public class RegistUser extends HttpServlet {
  @Override

    protected void doPost( HttpServletRequest req, HttpServletResponse res ) throws ServletException, IOException{

	  	int SqlResult;
	  	String userName;
	  	String Password;

	  	userName = req.getParameter("userName");
	  	Password = req.getParameter("Password");

	  	res.setContentType("text/html; charset=Windows-31J");

	  	UserPasswordDao UserPassDao = new UserPasswordDao();

	  	UserPassDao.setUserName(userName);
	  	UserPassDao.setPassword(CipherUtil.encode(Password));

	  	SqlResult = UserPassDao.insert001();
	  	String disp;

	  	if (SqlResult == 0) {
	  		req.setAttribute("ErrMsg","データの重複が発生しました。");
	  		disp = "/RegistUser.jsp";
	  	}else{
	  		disp = "/RegistUser_Finish.jsp";
	  	}

	    RequestDispatcher dispatch = req.getRequestDispatcher(disp);
	    dispatch.include(req, res);

    }
}

●LoginUser

package webapp;

import java.io.IOException;
import java.sql.SQLException;
import java.util.List;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import Beans.UserPasswordBean;
import dao.UserPasswordDao;
import util.CipherUtil;

public class LoginUser extends HttpServlet {
  @Override

    protected void doPost( HttpServletRequest req, HttpServletResponse res ) throws ServletException, IOException{

	    int SqlResult;
	    int ErrCode;
	    String ErrMsg;

        String UserName = req.getParameter("userName");
        String Password = req.getParameter("Password");;

        res.setContentType("text/html; charset=Windows-31J");

        SqlResult = 0;
        ErrCode = 0;
        ErrMsg ="";
        try {
        	List<UserPasswordBean> list = UserPasswordDao.getInstance().select001(UserName);

        	if (list.size() == 0) {
        		ErrCode = 10;
        		ErrMsg = "該当のユーザーは存在しません。";
        	} else {
        		if (CipherUtil.passcheck(Password,list.get(0).getPassword())) {
        			ErrCode = 0;
        		} else {
        			ErrCode = 10;
        			ErrMsg = "パスワードが誤っています。";
        		}
        	}

        } catch (SQLException e) {
            throw new ServletException(e);
        }

        String disp;

	  	if (!(ErrCode == 0)) {
	  		req.setAttribute("ErrMsg",ErrMsg);
	  		disp = "/Login.jsp";
	  	}else{
	  		disp = "/Login_Finish.jsp";
	  	}

	  	RequestDispatcher dispatch = req.getRequestDispatcher(disp);
	    dispatch.include(req, res);
    }
}

●UserPasswordDao.java

package dao;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import Beans.UserPasswordBean;

public class UserPasswordDao  {

	private String UserName = "";
	private String password = "";

    public static UserPasswordDao getInstance(){
        return new UserPasswordDao();
    }

	private static final String insert001 = ""
			+ "insert into "
			+ "  user_password "
			+ "values"
			+ "  ("
			+ "    'USER_NAME',"
			+ "    'PASSWORD'"
			+ "  );";

	private static final String select001 = ""
			+ "select "
			+ "  user_name,login_pw "
			+ "from "
			+ "  user_password "
			+ "where "
			+ "  user_name = 'USER_NAME';";

    public int insert001() {

	    String sql = insert001;
	    sql = sql.replaceAll("USER_NAME",UserName);
	    sql = sql.replaceAll("PASSWORD",password);

	    try {
	    	return DBManager.simpleUpdate(sql);
	    }catch (SQLException e) {
	    	return 0;
	    }
    }

    private ResultSetBeanMapping<UserPasswordBean> allMapping=
            new ResultSetBeanMapping<UserPasswordBean>(){

        public UserPasswordBean createFromResultSet(ResultSet rs)
            throws SQLException {

        	UserPasswordBean page = new UserPasswordBean();
            page.setUserName(rs.getString("user_name"));
            page.setPassword(rs.getString("login_pw"));

            return page;
        }

    };

	public List<UserPasswordBean> select001(String UserName) throws SQLException {

	    String sql = select001;
	    sql = sql.replaceAll("USER_NAME",UserName);

	    System.out.println(sql);

	    return DBManager.simpleFind(sql, allMapping);

    }

    public String getUserName() {
    	return UserName;
    }
    public void setUserName(String UserName) {
    	this.UserName = UserName;
    }

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

●CipherUtil

package util;


import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;


public class CipherUtil {

	/*
	 * 暗号化の方法
	 * 暗号化アルゴリズム・・・AES
	 * ブロック暗号のモード・・・CBS
	 * パディング方式・・・PKCS5Padding
	 */

	//暗号化のキーを指定する。
	private static final byte[] KEY = "abcdefghijklmnop".getBytes();

	//public暗号化メソッド
	public static final String encode(String plainText){

		SecureRandom random;
		byte iv[] = null;

		try {
			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
			random = SecureRandom.getInstance("SHA1PRNG");

			iv = new byte[cipher.getBlockSize()];
			random.nextBytes(iv);

		} catch (Exception e){
			System.out.println(e);
			e.printStackTrace();
		}

		return encode(plainText,iv);
	}

	//実際の暗号化処理
	private static final String encode(String plainText,byte[] iv){

		String encryptText = null;

		try {
			//初期化ベクトル (IV)をセットする。
			AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);

			//暗号化方式、ブロック暗号のモード、パディング方式を指定する。
			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
			//暗号方式等をセットして、暗号器を生成する。
			cipher.init(Cipher.ENCRYPT_MODE , new SecretKeySpec(KEY, "AES"), ivSpec);

			//暗号化対象の文字列を暗号化する。
			byte[] cryptogram = cipher.doFinal(plainText.getBytes("MS932"));

			//暗号化の結果を格納する変数を生成
			byte[] byteResult = new byte[iv.length + cryptogram.length];

			//byteResult変数に対して、初期ベクトル + 暗号データを格納する。
			System.arraycopy(iv, 0, byteResult, 0,iv.length);
			System.arraycopy(cryptogram, 0, byteResult, iv.length, cryptogram.length);

			//暗号化されたデータに対してbase64エンコードを行い、文字列化する。
			encryptText = Base64.getEncoder().encodeToString(byteResult);

		} catch (Exception e){
			System.out.println(e);
			e.printStackTrace();
		}

		return encryptText;

	}

	//初期ベクトルを取得するメソッド
	private static final byte[] getIV(byte[] BaseDecode) {
		Cipher cipher = null;
		byte[] iv = null;

		try {
			cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
		} catch (Exception e) {
			System.out.println("err");
		}

		iv = new byte[cipher.getBlockSize()];

		System.arraycopy(BaseDecode, 0, iv, 0, iv.length);
		return iv;

	}

	//暗号化済みの文字列と、比較対象文字列を比較する
	public static final boolean passcheck(String plain, String encoded) {

		byte[] BaseDecode = null;

		BaseDecode = Base64.getDecoder().decode(encoded);
		String encodedFromPlain = encode(plain, getIV(BaseDecode));

		return encoded.equals(encodedFromPlain);

	}

}

jsp参考

JSP側はあまり関係ありませんが、参考として記載しておきます。

●RegistUser.jsp

<%@ page language="java" contentType="text/html;charset=Windows-31J" %>
<%@ page pageEncoding="Windows-31J" %>

<HTML>

<HEAD>

<TITLE>ユーザー登録</TITLE>

<style type="text/css">
    .block1 {
	display: inline-block;
	width: 7em
    }
</style>

</HEAD>

<BODY>

<%

    String ErrMsg = (String)request.getAttribute("ErrMsg");

    if (ErrMsg != null) {
%>
<strong><font color=#ff0000><%= ErrMsg %></font></strong>
<%
    }
%>

<h1>ユーザー登録</h1>

登録するユーザー、パスワードを入力してください。
<form Name="PostForm" Action="RegistUser" method="post">
    <span class="block1">ユーザー名:</span><input type="text" name ="userName" /><br>
    <span class="block1">パスワード:</span><input type="password" name ="Password" /><br>
    <input type="submit" value="ユーザー登録" />
</form>

</BODY>

</HTML>
[/java]

<p style="margin-top:4.0em;"></p>

<p>
●RegistUser_Finish.jsp
</p>

<%@ page language="java" contentType="text/html;charset=Windows-31J" %>
<%@ page pageEncoding="Windows-31J" %>

<HTML>

<HEAD>

<TITLE>ユーザー登録完了</TITLE>

<style type="text/css">
    .block1 {
	display: inline-block;
	width: 7em
    }
</style>

<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-Control" content="no-cache">
<meta http-equiv="Expires" content="0">

</HEAD>

<BODY>

<%
  String userName = request.getParameter("userName");
%>


<h1>ユーザー登録完了</h1>

以下の内容でユーザーを登録しました。<br>
<span class="block1">ユーザー名:</span><%= userName %><br>
<span class="block1">パスワード:</span>*********<br>


<a href="Login.jsp">ログインはこちらから</a>


</BODY>

</HTML>

●Login.jsp

<%@ page language="java" contentType="text/html;charset=Windows-31J" %>
<%@ page pageEncoding="Windows-31J" %>

<HTML>

<HEAD>

<TITLE>ログイン</TITLE>

<style type="text/css">
    .block1 {
	display: inline-block;
	width: 7em
    }
</style>

</HEAD>

<BODY>

<%

    String ErrMsg = (String)request.getAttribute("ErrMsg");

    if (ErrMsg != null) {
%>
<strong><font color=#ff0000><%= ErrMsg %></font></strong>
<%
    }
%>

<h1>ログイン</h1>

ユーザー、パスワードを入力してください。
<form Name="PostForm" Action="LoginUser" method="post">
    <span class="block1">ユーザー名:</span><input type="text" name ="userName" /><br>
    <span class="block1">パスワード:</span><input type="password" name ="Password" /><br>
    <input type="submit" value="ログイン" />
</form>

</BODY>

</HTML>

●Login_Finish.jsp

<%@ page language="java" contentType="text/html;charset=Windows-31J" %>
<%@ page pageEncoding="Windows-31J" %>

<HTML>

<HEAD>

<TITLE>ログイン成功</TITLE>

<style type="text/css">
    .block1 {
	display: inline-block;
	width: 7em
    }
</style>

<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-Control" content="no-cache">
<meta http-equiv="Expires" content="0">

</HEAD>

<BODY>

<%
  String userName = request.getParameter("userName");
%>


<h1>ログイン成功</h1>

ログインしました。<br>
<span class="block1">ユーザー名:</span><%= userName %><br>

</BODY>

</HTML>

コード解説

ユーザー登録とログイン処理で重要なのは、ユーザー登録時のパスワード暗号化と暗号化されているパスワードとログインパスワードの比較チェックの部分となります。
なのでコード解説もそこを中心に行います。

まず、ユーザー登録時には CipherUtil.encodeメソッドを利用してパスワードを暗号化します。
暗号化した文字列を、user_passwordテーブルにユーザー名と一緒に格納します。

ここで利用した CipherUtil.encodeのポイントとしては、初期化ベクトル (IV)をランダムに作成して、かつ暗号化後のパスワードの先頭に付与している部分ですね。
ランダムな初期ベクトルを利用することで、同じパスワードであっても別の文字列に変換されます。
また、ランダムな初期ベクトルなので、後々パスワード比較する際に、比較できなくなってしまうので、暗号化後のパスワードの先頭に付与しておきます。

次にログインパスワード認証の部分では、CipherUtil.passcheckメソッドが重要になります。
このメソッドは、入力されたログインパスワードと、user_passwordテーブルから取得した暗号化されたパスワードの比較メソッドになります。

前回の記事では、decodeメソッドを用意して、暗号化されたパスワードを平文に戻すことが出来るようにしていましたが、ログインパスワードのチェック処理では平文に戻す必要はありません。
むしろセキュリティ的には暗号化されたもの同士でパスワードチェックをするべきです。
※なので、decodeメソッド自体を用意しない。

CipherUtil.passcheckメソッドの返り値が trueであれば、パスワードがあっているということで、ログイン成功となります。

コードの解説としてはこのくらいになります。

ではまた次回。

フッター広告

スポンサードリンク



シェアする

  • このエントリーをはてなブックマークに追加

フォローする