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     */
016    package org.opengion.fukurou.security;
017    
018    import javax.crypto.spec.SecretKeySpec;
019    import javax.crypto.Cipher;
020    
021    import java.security.MessageDigest;
022    import java.security.NoSuchAlgorithmException;
023    import java.security.GeneralSecurityException;  // 5.7.2.1 (2014/01/17)
024    
025    import java.nio.charset.Charset;                                // 5.5.2.6 (2012/05/25)
026    import java.nio.channels.FileChannel;                   // 5.7.2.1 (2014/01/17)
027    import java.nio.ByteBuffer;                                             // 5.5.2.6 (2012/05/25)
028    import java.nio.channels.FileChannel.MapMode;   // 5.5.2.6 (2012/05/25)
029    
030    import java.io.File;
031    import java.io.FileInputStream;
032    import java.io.IOException;
033    
034    import org.opengion.fukurou.util.Closer;                // 5.5.2.6 (2012/05/25)
035    
036    /**
037     * HybsCryptography は、セキュリãƒ?‚£強化ã?為の Hybs独自の暗号化クラスですã?
038     *
039     * こã?クラスは、暗号化キーを受け取りã?それに基づã�?�¦暗号åŒ?復号化を行いますã?
040     * ここでの暗号化ã?、秘å¯?‚­ー方式でバイト文字å?に変換されたものをã??‘6é?アスキーæ–?­—に
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     */
050    public 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            // 注意:秘å¯?‚­ーはã€?¼˜ã?倍数でなã�?�¨ã�?�‘なã�??
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             * @param       cryptKey        暗号化を行う秘å¯?�µ
080             */
081            public HybsCryptography( final String cryptKey ) {
082    //              sksSpec = new SecretKeySpec( cryptKey.getBytes(), CIPHER_TYPE );
083                    sksSpec = new SecretKeySpec( cryptKey.getBytes( DEFAULT_CHARSET ), CIPHER_TYPE );               // 5.5.2.6 (2012/05/25) findbugs対å¿?
084            }
085    
086            /**
087             * セキュリãƒ?‚£カラãƒ??DBTyepに対してHybs独自の暗号化を行いますã?
088             * 暗号化されたãƒ??タはã€??常 byte æ–?­—ですがã€?¼‘6é?数アスキーæ–?­—å?に変換
089             * したもã?を返しますã?
090             * こã?暗号化では、引数ã�?null の場合ã?、ゼロæ–?­—å?を返しますã?
091             *
092             * @og.rev 5.7.2.1 (2014/01/17) Exceptionをまとめますã?
093             *
094             * @param       org     暗号化を行うå…??æ–?­—å?
095             *
096             * @return      暗号化されたæ–?­—å?(HEXADECIMALåŒ?
097             */
098            public String encrypt( final String org ) {
099                    if( org == null || org.length() == 0 ) { return ""; }
100    
101                    try {
102                            Cipher cipher = Cipher.getInstance( CIPHER_TYPE );
103                            cipher.init( Cipher.ENCRYPT_MODE, sksSpec );
104    //                      byte[] encrypted = cipher.doFinal( org.getBytes() );
105                            byte[] encrypted = cipher.doFinal( org.getBytes( DEFAULT_CHARSET ) );           // 5.5.2.6 (2012/05/25) findbugs対å¿?
106    
107                            return byte2hexa( encrypted );
108                    }
109                    // 5.7.2.1 (2014/01/17) Exceptionをまとめますã?
110                    catch( GeneralSecurityException ex ) {
111                            String errMsg = "暗号化å?ç�?�«失敗しましたã€?" + org + "]"
112                                                            + ex.getMessage() ;
113                            throw new RuntimeException( errMsg,ex );
114                    }
115    //              catch( javax.crypto.IllegalBlockSizeException   ex ) { throw new RuntimeException( ex ); }
116    //              catch( java.security.InvalidKeyException                ex ) { throw new RuntimeException( ex ); }
117    //              catch( java.security.NoSuchAlgorithmException   ex ) { throw new RuntimeException( ex ); }
118    //              catch( javax.crypto.BadPaddingException                 ex ) { throw new RuntimeException( ex ); }
119    //              catch( javax.crypto.NoSuchPaddingException              ex ) { throw new RuntimeException( ex ); }
120            }
121    
122            /**
123             * セキュリãƒ?‚£カラãƒ??DBTyepに対してHybs独自の復号化を行いますã?
124             * ここでの復号化ã?、encrypt で暗号化されたæ–?­—を戻すå?合に使用しますã?
125             * こã?復号化では、null は復号化できなã�?�Ÿめã?ゼロæ–?­—å?を返しますã?
126             *
127             * @og.rev 5.7.2.1 (2014/01/17) Exceptionをまとめますã?
128             *
129             * @param       hex     復号化を行う暗号化された?‘6é?数アスキーæ–?­—å?
130             *
131             * @return      復号化されたå…??æ–?­—å?
132             */
133            public String decrypt( final String hex ) {
134                    if( hex == null || hex.length() == 0 ) { return ""; }
135    
136                    try {
137                            Cipher cipher = Cipher.getInstance( CIPHER_TYPE );
138                            cipher.init( Cipher.DECRYPT_MODE, sksSpec );
139                            byte[] encrypted = hexa2byte( hex );
140                            byte[] decrypted = cipher.doFinal( encrypted );
141    //                      return new String( decrypted );
142                            return new String( decrypted,DEFAULT_CHARSET );         // 5.5.2.6 (2012/05/25) findbugs対å¿?
143                    }
144                    // 5.7.2.1 (2014/01/17) Exceptionをまとめますã?
145                    catch( GeneralSecurityException ex ) {
146                            String errMsg = "復号化å?ç�?�«失敗しましたã€?" + hex + "]"
147                                                            + ex.getMessage() ;
148                            throw new RuntimeException( errMsg,ex );
149                    }
150    //              catch( javax.crypto.IllegalBlockSizeException   ex ) { throw new RuntimeException( ex ); }
151    //              catch( java.security.InvalidKeyException                ex ) { throw new RuntimeException( ex ); }
152    //              catch( java.security.NoSuchAlgorithmException   ex ) { throw new RuntimeException( ex ); }
153    //              catch( javax.crypto.BadPaddingException                 ex ) { throw new RuntimeException( ex ); }
154    //              catch( javax.crypto.NoSuchPaddingException              ex ) { throw new RuntimeException( ex ); }
155            }
156            /**
157             * 数字から16é?æ–?­—に変換するãƒ??ブルですã?
158             */
159            private static final char[] hexadecimal =
160                    { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
161                      'a', 'b', 'c', 'd', 'e', 'f' };
162    
163            /**
164             * バイトé?列を?‘6é?数アスキーæ–?­—å?に変換しますã?
165             *
166             * バイトé?列をã€?¼’文字ã?0??,a?žï½??アスキーに変換されますã?
167             * これにより、すべてのæ–?­—を、アスキー化できますã?
168             * アスキー化で、上位が0F以下ã?場合でもã?0 をå?すことで、固定長に変換しますã?
169             *
170             * よって、å?力バイトã??’å?のlength()を持ったStringを作æ?しますã?
171             *
172             * @param       input バイトé?åˆ?
173             *
174             * @return      ?‘6é?数アスキーæ–?­—å?
175             */
176            public static String byte2hexa( final byte[] input ) {
177                    String rtn = null;
178                    if( input != null ) {
179                            int len = input.length ;
180                            char[] ch = new char[len*2];
181                            for( int i=0; i<len; i++ ) {
182                                    int high = ((input[i] & 0xf0) >> 4);
183                                    int low  = (input[i] & 0x0f);
184                                    ch[i*2]   = hexadecimal[high];
185                                    ch[i*2+1] = hexadecimal[low];
186                            }
187                            rtn =  new String(ch);
188                    }
189                    return rtn;
190            }
191    
192            /**
193             * ?‘6é?数アスキーæ–?­—å?をバイトé?列に変換しますã?
194             *
195             * ?’文字ã?0??,a?žï½??アスキーæ–?­—å?をã?バイトé?列に変換されますã?
196             *
197             * よって、å?力Stringの???’å?のlengthを持ったバイトé?列を作æ?しますã?
198             *
199             * @param       input ?‘6é?数アスキーæ–?­—å?
200             *
201             * @return      バイトé?åˆ?
202             */
203            public static byte[] hexa2byte( final String input ) {
204                    byte[] rtn = null;
205                    if( input != null ) {
206                            int len = input.length() ;
207                            rtn = new byte[len/2];
208                            for( int i=0; i<len/2; i++ ) {
209                                    char ch = input.charAt( i*2 );
210                                    int high = ( ch < 'a' ) ? ch-'0' : ch-'a'+10 ;
211                                    ch = input.charAt( i*2+1 );
212                                    int low  = ( ch < 'a' ) ? ch-'0' : ch-'a'+10 ;
213                                    rtn[i] = (byte)(high << 4 | low);
214                            }
215                    }
216                    return rtn;
217            }
218    
219            /**
220             * MessageDigestにより、MD5 でハッシュしたæ–?­—に変換しますã?
221             *
222             * MD5でã€?¼‘6Byteのバイトに変換されますが、ここではã€?¼‘6é?数でæ–?­—å?に変換してã�?�¾すã?
223             *
224             * 変換方法ã?、各バイトã?上ä½?下位を?‘6é?æ–?­—å?に変換後ã?連結してã�?�¾すã?
225             * これは、Tomcat等ã? digest 認証(MD5使用æ™?と同じ変換方式ですã?
226             * 連結後ã?æ–?­—å?長はã€?¼“2バイãƒ?固å®?になりますã?
227             *
228             * @og.rev 5.2.2.0 (2010/11/01) util.StringUtil から移å‹?
229             *
230             * @param       input 変換前ã?æ–?­—å?
231             *
232             * @return      MD5でハッシュしたæ–?­—å?。32バイãƒ?固å®?
233             */
234            public static String getMD5( final String input ) {
235                    String rtn = null;
236                    if( input != null ) {
237                            try {
238                                    MessageDigest md5 = MessageDigest.getInstance( "MD5" );
239    //                              md5.update( input.getBytes() );
240                                    md5.update( input.getBytes( DEFAULT_CHARSET ) );        // 5.5.2.6 (2012/05/25) findbugs対å¿?
241                                    byte[] out = md5.digest();
242                                    rtn = byte2hexa( out );
243                            }
244                            catch( NoSuchAlgorithmException ex ) {
245                                    String errMsg = "MessageDigestで失敗しましたã€?" + input + "]"
246                                                            + ex.getMessage() ;
247                                    throw new RuntimeException( errMsg,ex );
248                            }
249                    }
250                    return rtn;
251            }
252    
253            /**
254             * MessageDigestにより、MD5 でハッシュしたæ–?­—に変換しますã?
255             *
256             * MD5でã€?¼‘6Byteのバイトに変換されますが、ここではã€?¼‘6é?数でæ–?­—å?に変換してã�?�¾すã?
257             *
258             * 変換方法ã?、各バイトã?上ä½?下位を?‘6é?æ–?­—å?に変換後ã?連結してã�?�¾すã?
259             * これは、Tomcat等ã? digest 認証(MD5使用æ™?と同じ変換方式ですã?
260             * 連結後ã?æ–?­—å?長はã€?¼“2バイãƒ?固å®?になりますã?
261             *
262             * @og.rev 5.7.2.1 (2014/01/17) Exceptionをまとめますã?
263             *
264             * @param       input 変換前ã?File
265             *
266             * @return      MD5でハッシュしたæ–?­—å?。32バイãƒ?固å®?
267             */
268            public static String getMD5( final File input ) {
269                    String rtn = null;
270                    if( input != null ) {
271                            FileInputStream fis     = null;
272                            FileChannel             fc      = null;
273                            try {
274                                    MessageDigest md5 = MessageDigest.getInstance( "MD5" );
275                                    fis = new FileInputStream( input );
276                                    fc  =fis.getChannel();
277                                    ByteBuffer bb = fc.map( FileChannel.MapMode.READ_ONLY , 0L , fc.size() );
278                                    md5.update( bb );
279                                    byte[] out = md5.digest();
280                                    rtn = byte2hexa( out );
281                            }
282                            catch( NoSuchAlgorithmException ex ) {
283                                    String errMsg = "MessageDigestで MD5 インスタンスの作æ?に失敗しましたã€?" + input + "]"
284                                                            + ex.getMessage() ;
285                                    throw new RuntimeException( errMsg,ex );
286                            }
287                            catch( IOException ex ) {
288                                    String errMsg = "ファイルの読み取りを失敗しましたã€?" + input + "]"
289                                                            + ex.getMessage() ;
290                                    throw new RuntimeException( errMsg,ex );
291                            }
292                            finally {
293                                    Closer.ioClose( fc );
294                                    Closer.ioClose( fis );
295                            }
296                    }
297                    return rtn;
298            }
299    
300            /**
301             * 暗号化ã?ãƒ?‚¹トを行う為のメインメソãƒ?ƒ‰
302             *
303             * java HybsCryptography KEY TEXT で起動しますã?
304             *   KEY  : 秘å¯?�µ(8 の倍数 (32 以ä¸?448 以ä¸?æ–?­?
305             *   TEXT : 変換するæ–?­—å?
306             *
307             * @og.rev 5.2.2.0 (2010/11/01) 循環参ç?の解æ¶?LogWriter 削除)
308             *
309             * @param       args    引数配å?
310             * @throws Exception なんらかã?エラーが発生したå?合ã?
311             */
312            public static void main( final String[] args ) throws Exception {
313                    if( args.length != 2 ) {
314    //                      LogWriter.log( "java HybsCryptography KEY TEXT" );
315    //                      LogWriter.log( "  KEY  : 秘å¯?�µ(8 の倍数 (32 以ä¸?448 以ä¸?æ–?­?" );
316    //                      LogWriter.log( "  TEXT : 変換するæ–?­—å?" );
317                            System.out.println( "java HybsCryptography KEY TEXT" );
318                            System.out.println( "  KEY  : 秘å¯?�µ(8 の倍数 (32 以ä¸?448 以ä¸?æ–?­?" );
319                            System.out.println( "  TEXT : 変換するæ–?­—å?" );
320                            return;
321                    }
322    
323                    HybsCryptography cript = new HybsCryptography( args[0] );
324    
325                    System.out.println( "IN   TEXT : " + args[1] );
326    
327                    String hexa = cript.encrypt( args[1] );
328                    System.out.println( "HEXA TEXT : " + hexa );
329    
330                    String data = cript.decrypt( hexa );
331                    System.out.println( "OUT  DATA : " + data );
332            }
333    }