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             * @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 ?ですが?6?数アスキー??に変換
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     復号化を行う暗号化された?6?数アスキー??
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             * バイト?列を?6?数アスキー??に変換します?
176             *
177             * バイト?列を?文字?0??,a???アスキーに変換されます?
178             * これにより、すべての?を、アスキー化できます?
179             * アスキー化で、上位が0F以下?場合でも?0 を?すことで、固定長に変換します?
180             *
181             * よって、?力バイト???のlength()を持ったStringを作?します?
182             *
183             * @param       input バイト??
184             *
185             * @return      ?6?数アスキー??
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             * ?6?数アスキー??をバイト?列に変換します?
205             *
206             * ?文字?0??,a???アスキー??を?バイト?列に変換されます?
207             *
208             * よって、?力Stringの????のlengthを持ったバイト?列を作?します?
209             *
210             * @param       input ?6?数アスキー??
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で?6Byteのバイトに変換されますが、ここでは?6?数で??に変換して?す?
234             *
235             * 変換方法?、各バイト?上?下位を?6???に変換後?連結して?す?
236             * これは、Tomcat等? digest 認証(MD5使用?と同じ変換方式です?
237             * 連結後???長は?2バイ?固?になります?
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で?6Byteのバイトに変換されますが、ここでは?6?数で??に変換して?す?
268             *
269             * 変換方法?、各バイト?上?下位を?6???に変換後?連結して?す?
270             * これは、Tomcat等? digest 認証(MD5使用?と同じ変換方式です?
271             * 連結後???長は?2バイ?固?になります?
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    }