001/* 002 * Copyright (c) 2009 The openGion Project. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 013 * either express or implied. See the License for the specific language 014 * governing permissions and limitations under the License. 015 */ 016package org.opengion.fukurou.security; 017 018import javax.crypto.spec.SecretKeySpec; 019import javax.crypto.Cipher; 020 021import java.security.MessageDigest; 022import java.security.NoSuchAlgorithmException; 023import java.security.GeneralSecurityException; // 5.7.2.1 (2014/01/17) 024 025import java.nio.charset.Charset; // 5.5.2.6 (2012/05/25) 026import java.nio.channels.FileChannel; // 5.7.2.1 (2014/01/17) 027import java.nio.ByteBuffer; // 5.5.2.6 (2012/05/25) 028import java.nio.channels.FileChannel.MapMode; // 5.5.2.6 (2012/05/25) 029 030import java.io.File; 031import java.io.FileInputStream; 032import java.io.IOException; 033 034import org.opengion.fukurou.util.Closer; // 5.5.2.6 (2012/05/25) 035 036/** 037 * HybsCryptography は、セキュリティ強化の為の Hybs独自の暗号化クラスです。 038 * 039 * このクラスは、暗号化キーを受け取り、それに基づいて暗号化/復号化を行います。 040 * ここでの暗号化は、秘密キー方式でバイト文字列に変換されたものを、16進アスキー文字に 041 * 直して、扱っています。よって、暗号化/復号化共に、文字列として扱うことが可能です。 042 * 043 * @og.rev 4.0.0.0 (2005/08/31) 新規追加 044 * @og.group ライセンス管理 045 * 046 * @version 4.0 047 * @author Kazuhiko Hasegawa 048 * @since JDK5.0, 049 */ 050public final class HybsCryptography { 051 private final SecretKeySpec sksSpec ; 052 private static final String CIPHER_TYPE = "Blowfish" ; 053 054 /** 055 * プラットフォーム依存のデフォルトの Charset です。 056 * プラットフォーム依存性を考慮する場合、エンコード指定で作成しておく事をお勧めします。 057 * 058 * @og.rev 5.5.2.6 (2012/05/25) findbugs対応 059 */ 060 private static final Charset DEFAULT_CHARSET = Charset.defaultCharset() ; 061 062 // 注意:秘密キーは、8の倍数でないといけない。 063 private static final String HYBS_CRYPT_KEY = "2a5a88891d37ae59" ; 064 065 /** 066 * 内部設定の秘密鍵を使用して,暗号化を行うオブジェクトを構築します。 067 * ここでの暗号化は、Java標準のセキュリティパッケージを使用しています。 068 */ 069 public HybsCryptography() { 070// sksSpec = new SecretKeySpec( HYBS_CRYPT_KEY.getBytes(), CIPHER_TYPE ); 071 sksSpec = new SecretKeySpec( HYBS_CRYPT_KEY.getBytes( DEFAULT_CHARSET ), CIPHER_TYPE ); // 5.5.2.6 (2012/05/25) findbugs対応 072 } 073 074 /** 075 * 秘密鍵の文字列を受け取って,暗号化を行うオブジェクトを構築します。 076 * ここでの暗号化は、Java標準のセキュリティパッケージを使用しています。 077 * 秘密鍵のサイズを、8 の倍数 (32 以上 448 以下) にする必要があります。 078 * 079 * @og.rev 5.8.8.0 (2015/06/05) null時の挙動はデフォルトキーを利用する 080 * 081 * @param cryptKey 暗号化を行う秘密鍵 082 */ 083 public HybsCryptography( final String cryptKey ) { 084// sksSpec = new SecretKeySpec( cryptKey.getBytes(), CIPHER_TYPE ); 085// sksSpec = new SecretKeySpec( cryptKey.getBytes( DEFAULT_CHARSET ), CIPHER_TYPE ); // 5.5.2.6 (2012/05/25) findbugs対応 086 // 5.8.8.0 (2015/06/05) null時はデフォルトキーを利用 087 final String useKey; 088 if( cryptKey == null || cryptKey.length() == 0 ){ 089 useKey = HYBS_CRYPT_KEY; 090 } 091 else{ 092 useKey = cryptKey; 093 } 094 sksSpec = new SecretKeySpec( useKey.getBytes( DEFAULT_CHARSET ), CIPHER_TYPE ); 095 } 096 097 /** 098 * セキュリティカラムのDBTyepに対してHybs独自の暗号化を行います。 099 * 暗号化されたデータは、通常 byte 文字ですが、16進数アスキー文字列に変換 100 * したものを返します。 101 * この暗号化では、引数が null の場合は、ゼロ文字列を返します。 102 * 103 * @og.rev 5.7.2.1 (2014/01/17) Exceptionをまとめます。 104 * 105 * @param org 暗号化を行う元の文字列 106 * 107 * @return 暗号化された文字列(HEXADECIMAL化) 108 */ 109 public String encrypt( final String org ) { 110 if( org == null || org.length() == 0 ) { return ""; } 111 112 try { 113 Cipher cipher = Cipher.getInstance( CIPHER_TYPE ); 114 cipher.init( Cipher.ENCRYPT_MODE, sksSpec ); 115// byte[] encrypted = cipher.doFinal( org.getBytes() ); 116 byte[] encrypted = cipher.doFinal( org.getBytes( DEFAULT_CHARSET ) ); // 5.5.2.6 (2012/05/25) findbugs対応 117 118 return byte2hexa( encrypted ); 119 } 120 // 5.7.2.1 (2014/01/17) Exceptionをまとめます。 121 catch( GeneralSecurityException ex ) { 122 String errMsg = "暗号化処理に失敗しました。[" + org + "]" 123 + ex.getMessage() ; 124 throw new RuntimeException( errMsg,ex ); 125 } 126// catch( javax.crypto.IllegalBlockSizeException ex ) { throw new RuntimeException( ex ); } 127// catch( java.security.InvalidKeyException ex ) { throw new RuntimeException( ex ); } 128// catch( java.security.NoSuchAlgorithmException ex ) { throw new RuntimeException( ex ); } 129// catch( javax.crypto.BadPaddingException ex ) { throw new RuntimeException( ex ); } 130// catch( javax.crypto.NoSuchPaddingException ex ) { throw new RuntimeException( ex ); } 131 } 132 133 /** 134 * セキュリティカラムのDBTyepに対してHybs独自の復号化を行います。 135 * ここでの復号化は、encrypt で暗号化された文字を戻す場合に使用します。 136 * この復号化では、null は復号化できないため、ゼロ文字列を返します。 137 * 138 * @og.rev 5.7.2.1 (2014/01/17) Exceptionをまとめます。 139 * 140 * @param hex 復号化を行う暗号化された16進数アスキー文字列 141 * 142 * @return 復号化された元の文字列 143 */ 144 public String decrypt( final String hex ) { 145 if( hex == null || hex.length() == 0 ) { return ""; } 146 147 try { 148 Cipher cipher = Cipher.getInstance( CIPHER_TYPE ); 149 cipher.init( Cipher.DECRYPT_MODE, sksSpec ); 150 byte[] encrypted = hexa2byte( hex ); 151 byte[] decrypted = cipher.doFinal( encrypted ); 152// return new String( decrypted ); 153 return new String( decrypted,DEFAULT_CHARSET ); // 5.5.2.6 (2012/05/25) findbugs対応 154 } 155 // 5.7.2.1 (2014/01/17) Exceptionをまとめます。 156 catch( GeneralSecurityException ex ) { 157 String errMsg = "復号化処理に失敗しました。[" + hex + "]" 158 + ex.getMessage() ; 159 throw new RuntimeException( errMsg,ex ); 160 } 161// catch( javax.crypto.IllegalBlockSizeException ex ) { throw new RuntimeException( ex ); } 162// catch( java.security.InvalidKeyException ex ) { throw new RuntimeException( ex ); } 163// catch( java.security.NoSuchAlgorithmException ex ) { throw new RuntimeException( ex ); } 164// catch( javax.crypto.BadPaddingException ex ) { throw new RuntimeException( ex ); } 165// catch( javax.crypto.NoSuchPaddingException ex ) { throw new RuntimeException( ex ); } 166 } 167 /** 168 * 数字から16進文字に変換するテーブルです。 169 */ 170 private static final char[] hexadecimal = 171 { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 172 'a', 'b', 'c', 'd', 'e', 'f' }; 173 174 /** 175 * バイト配列を16進数アスキー文字列に変換します。 176 * 177 * バイト配列を、2文字の0〜9,a〜fのアスキーに変換されます。 178 * これにより、すべての文字を、アスキー化できます。 179 * アスキー化で、上位が0F以下の場合でも、0 を出すことで、固定長に変換します。 180 * 181 * よって、入力バイトの2倍のlength()を持ったStringを作成します。 182 * 183 * @param input バイト配列 184 * 185 * @return 16進数アスキー文字列 186 */ 187 public static String byte2hexa( final byte[] input ) { 188 String rtn = null; 189 if( input != null ) { 190 int len = input.length ; 191 char[] ch = new char[len*2]; 192 for( int i=0; i<len; i++ ) { 193 int high = ((input[i] & 0xf0) >> 4); 194 int low = (input[i] & 0x0f); 195 ch[i*2] = hexadecimal[high]; 196 ch[i*2+1] = hexadecimal[low]; 197 } 198 rtn = new String(ch); 199 } 200 return rtn; 201 } 202 203 /** 204 * 16進数アスキー文字列をバイト配列に変換します。 205 * 206 * 2文字の0〜9,a〜fのアスキー文字列を、バイト配列に変換されます。 207 * 208 * よって、入力Stringの1/2倍のlengthを持ったバイト配列を作成します。 209 * 210 * @param input 16進数アスキー文字列 211 * 212 * @return バイト配列 213 */ 214 public static byte[] hexa2byte( final String input ) { 215 byte[] rtn = null; 216 if( input != null ) { 217 int len = input.length() ; 218 rtn = new byte[len/2]; 219 for( int i=0; i<len/2; i++ ) { 220 char ch = input.charAt( i*2 ); 221 int high = ( ch < 'a' ) ? ch-'0' : ch-'a'+10 ; 222 ch = input.charAt( i*2+1 ); 223 int low = ( ch < 'a' ) ? ch-'0' : ch-'a'+10 ; 224 rtn[i] = (byte)(high << 4 | low); 225 } 226 } 227 return rtn; 228 } 229 230 /** 231 * MessageDigestにより、MD5 でハッシュした文字に変換します。 232 * 233 * MD5で、16Byteのバイトに変換されますが、ここでは、16進数で文字列に変換しています。 234 * 235 * 変換方法は、各バイトの上位/下位を16進文字列に変換後、連結しています。 236 * これは、Tomcat等の digest 認証(MD5使用時)と同じ変換方式です。 237 * 連結後の文字列長は、32バイト(固定)になります。 238 * 239 * @og.rev 5.2.2.0 (2010/11/01) util.StringUtil から移動 240 * 241 * @param input 変換前の文字列 242 * 243 * @return MD5でハッシュした文字列。32バイト(固定) 244 */ 245 public static String getMD5( final String input ) { 246 String rtn = null; 247 if( input != null ) { 248 try { 249 MessageDigest md5 = MessageDigest.getInstance( "MD5" ); 250// md5.update( input.getBytes() ); 251 md5.update( input.getBytes( DEFAULT_CHARSET ) ); // 5.5.2.6 (2012/05/25) findbugs対応 252 byte[] out = md5.digest(); 253 rtn = byte2hexa( out ); 254 } 255 catch( NoSuchAlgorithmException ex ) { 256 String errMsg = "MessageDigestで失敗しました。[" + input + "]" 257 + ex.getMessage() ; 258 throw new RuntimeException( errMsg,ex ); 259 } 260 } 261 return rtn; 262 } 263 264 /** 265 * MessageDigestにより、MD5 でハッシュした文字に変換します。 266 * 267 * MD5で、16Byteのバイトに変換されますが、ここでは、16進数で文字列に変換しています。 268 * 269 * 変換方法は、各バイトの上位/下位を16進文字列に変換後、連結しています。 270 * これは、Tomcat等の digest 認証(MD5使用時)と同じ変換方式です。 271 * 連結後の文字列長は、32バイト(固定)になります。 272 * 273 * @og.rev 5.7.2.1 (2014/01/17) Exceptionをまとめます。 274 * 275 * @param input 変換前のFile 276 * 277 * @return MD5でハッシュした文字列。32バイト(固定) 278 */ 279 public static String getMD5( final File input ) { 280 String rtn = null; 281 if( input != null ) { 282 FileInputStream fis = null; 283 FileChannel fc = null; 284 try { 285 MessageDigest md5 = MessageDigest.getInstance( "MD5" ); 286 fis = new FileInputStream( input ); 287 fc =fis.getChannel(); 288 ByteBuffer bb = fc.map( FileChannel.MapMode.READ_ONLY , 0L , fc.size() ); 289 md5.update( bb ); 290 byte[] out = md5.digest(); 291 rtn = byte2hexa( out ); 292 } 293 catch( NoSuchAlgorithmException ex ) { 294 String errMsg = "MessageDigestで MD5 インスタンスの作成に失敗しました。[" + input + "]" 295 + ex.getMessage() ; 296 throw new RuntimeException( errMsg,ex ); 297 } 298 catch( IOException ex ) { 299 String errMsg = "ファイルの読み取りを失敗しました。[" + input + "]" 300 + ex.getMessage() ; 301 throw new RuntimeException( errMsg,ex ); 302 } 303 finally { 304 Closer.ioClose( fc ); 305 Closer.ioClose( fis ); 306 } 307 } 308 return rtn; 309 } 310 311 /** 312 * 暗号化のテストを行う為のメインメソッド 313 * 314 * java HybsCryptography KEY TEXT で起動します。 315 * KEY : 秘密鍵(8 の倍数 (32 以上 448 以下)文字) 316 * TEXT : 変換する文字列 317 * 318 * @og.rev 5.2.2.0 (2010/11/01) 循環参照の解消(LogWriter 削除) 319 * 320 * @param args 引数配列 321 * @throws Exception なんらかのエラーが発生した場合。 322 */ 323 public static void main( final String[] args ) throws Exception { 324 if( args.length != 2 ) { 325// LogWriter.log( "java HybsCryptography KEY TEXT" ); 326// LogWriter.log( " KEY : 秘密鍵(8 の倍数 (32 以上 448 以下)文字)" ); 327// LogWriter.log( " TEXT : 変換する文字列" ); 328 System.out.println( "java HybsCryptography KEY TEXT" ); 329 System.out.println( " KEY : 秘密鍵(8 の倍数 (32 以上 448 以下)文字)" ); 330 System.out.println( " TEXT : 変換する文字列" ); 331 return; 332 } 333 334 HybsCryptography cript = new HybsCryptography( args[0] ); 335 336 System.out.println( "IN TEXT : " + args[1] ); 337 338 String hexa = cript.encrypt( args[1] ); 339 System.out.println( "HEXA TEXT : " + hexa ); 340 341 String data = cript.decrypt( hexa ); 342 System.out.println( "OUT DATA : " + data ); 343 } 344}