/* * * $CVSHeader$ * * This file is part of WebScarab, an Open Web Application Security * Project utility. For details, please see http://www.owasp.org/ * * Copyright (c) 2002 - 2004 Rogan Dawes * * Please note that this file was originally released under the * GNU General Public License as published by the Free Software Foundation; * either version 2 of the License, or (at your option) any later version. * * As of October 2014 Rogan Dawes granted the OWASP ZAP Project permission to * redistribute this code under the Apache License, Version 2.0: * * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ch.csnc.extension.util; import java.net.URLDecoder; import java.net.URLEncoder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; /** * Utilities to (de-)code data. * * @since 0.1 * @version 0.2rc<br />CVS $Revision: 1.4 $ $Author: rogan $ * @author <a href="mailto:ingo@ingostruck.de">ingo@ingostruck.de</a> */ public final class Encoding { /** Avoids instantiation */ private Encoding() {} /** Map for base64 encoding */ private static byte[] _base64en; /** Map for base64 decoding */ private static byte[] _base64de; /** invalid char on base64 decoding */ private static final byte B64INV = (byte) 0x80; /** Initializes the base64 de-/encoding maps according to RFC 1341, 5.2 */ static { _base64en = new byte[65]; _base64de = new byte[256]; Arrays.fill( _base64de, B64INV ); for ( byte i = 0; i < 26; i++ ) { _base64en[ i ] = (byte) (65 + i); _base64en[ 26 + i ] = (byte) (97 + i); _base64de[ 65 + i ] = i; _base64de[ 97 + i ] = (byte) (26 + i); } for ( byte i = 48; i < 58; i++ ) { _base64en[ 4 + i ] = i; _base64de[ i ] = (byte) (4 + i); } _base64en[ 62 ] = 43; _base64en[ 63 ] = 47; _base64en[ 64 ] = 61; _base64de[ 43 ] = 62; _base64de[ 47 ] = 63; _base64de[ 61 ] = 0; // sic! } /** * Encodes a byte array to a Base64 encoded String. * <br />(cf. RFC 1341 section 5.2) * This implementation heavily outperforms sun.misc.BASE64Encoder, which is * not "officially" available anyway (about four times faster on 1.3 and * about double speed on 1.4). * @param code the byte code to be encoded * @return the Base64 encoded String representing the plain bytecode */ public static String base64encode( byte[] code ) { if ( null == code ) return null; if ( 0 == code.length ) return new String(); int len = code.length; // remainder of the encoding process int rem = len % 3; // size of the destination byte array byte[] dst = new byte[4 + (((len - 1) / 3) << 2) + (len / 57)]; // actual column of the destination string; // RFC 1341 requires a linefeed every 58 data bytes int column = 0; // position within source int spos = 0; // position within destination int dpos = 0; // adjust length for loop (remainder is treated separately) len -= 2; // using a while loop here since spos may be needed for the remainder while ( spos < len ) { byte b0 = code[ spos ]; byte b1 = code[ spos + 1 ]; byte b2 = code[ spos + 2 ]; dst[ dpos++ ] = _base64en[ 0x3f & (b0 >>> 2) ]; dst[ dpos++ ] = _base64en[ (0x30 & (b0 << 4)) + (0x0f & (b1 >>> 4)) ]; dst[ dpos++ ] = _base64en[ (0x3c & (b1 << 2)) + (0x03 & (b2 >>> 6)) ]; dst[ dpos++ ] = _base64en[ 0x3f & b2 ]; spos += 3; column += 3; if ( 57 == column ) { dst[ dpos++ ] = 10; column = 0; } } // there may be a remainder to be processed if ( 0 != rem ) { byte b0 = code[ spos ]; dst[ dpos++ ] = _base64en[ 0x3f & (b0 >>> 2) ]; if ( 1 == rem ) { // one-byte remainder dst[ dpos++ ] = _base64en[ 0x30 & (b0 << 4) ]; dst[ dpos++ ] = 61; } else { // two-byte remainder byte b1 = code[ spos + 1 ]; dst[ dpos++ ] = _base64en[ (0x30 & (b0 << 4)) + (0x0f & (b1 >>> 4)) ]; dst[ dpos++ ] = _base64en[ 0x3c & (b1 << 2) ]; } dst[ dpos++ ] = 61; } // using any default encoding is possible, since the base64 char subset is // identically represented in all ISO encodings, including US-ASCII return new String( dst ); } /** * Decodes a Base64 encoded String. * <br />(cf. RFC 1341 section 5.2) * NOTE: This decoder silently ignores all legal line breaks in the input and * throws a RuntimeException on any illegal input. * This impl slightly outperforms sun's decoder on 1.3 and heavily outperforms * it on 1.4 (about a third faster). * * @param coded the string to be decoded * @return the plain bytecode represented by the Base64 encoded String */ public static byte[] base64decode( String coded ) { if ( null == coded ) return null; byte[] src = coded.getBytes(); int len = src.length; int dlen = len - (len/77); dlen = (dlen >>> 2) + (dlen >>> 1); int rem = 0; if ( 61 == src[ len - 1 ] ) rem++; if ( 61 == src[ len - 2 ] ) rem++; dlen -= rem; byte[] dst = new byte[ dlen ]; int pos = 0; int dpos = 0; int col = 0; // adjust for remainder len -= 4; while ( pos < len ) { byte b0 = _base64de[ src[ pos++ ] ]; byte b1 = _base64de[ src[ pos++ ] ]; byte b2 = _base64de[ src[ pos++ ] ]; byte b3 = _base64de[ src[ pos++ ] ]; if ( B64INV == b0 || B64INV == b1 || B64INV == b2 || B64INV == b3 ) throw new RuntimeException( "Invalid character at or around position " + pos ); dst[ dpos++ ] = (byte) ((b0 << 2) | ((b1 >>> 4) & 0x03)); dst[ dpos++ ] = (byte) ((b1 << 4) | ((b2 >>> 2) & 0x0f)); dst[ dpos++ ] = (byte) ((b2 << 6) | (b3 & 0x3f)); col += 4; // skip linefeed which is only allowed here; if at that pos is any // other char then the input is goofed and we throw an // exception if ( 76 == col ) { if ( 10 != src[ pos++ ] ) throw new RuntimeException( "No linefeed found at position " + (pos - 1 ) ); col = 0; } } // process the remainder byte b0 = _base64de[ src[ pos++ ] ]; byte b1 = _base64de[ src[ pos++ ] ]; byte b2 = _base64de[ src[ pos++ ] ]; byte b3 = _base64de[ src[ pos++ ] ]; if ( B64INV == b0 || B64INV == b1 || B64INV == b2 || B64INV == b3 ) throw new RuntimeException( "Invalid character at or around position " + pos ); dst[ dpos++ ] = (byte) ((b0 << 2) | ((b1 >>> 4) & 0x03)); if ( 2 == rem ) return dst; dst[ dpos++ ] = (byte) ((b1 << 4) | ((b2 >>> 2) & 0x0f)); if ( 1 == rem ) return dst; dst[ dpos++ ] = (byte) ((b2 << 6) | (b3 & 0x3f)); return dst; } /** * Converts a byte array into a hexadecimal String. * @param b a byte array to be converted * @return a hexadecimal (lower-case-based) String representation of the * byte array */ public static String toHexString( byte[] b ) { if ( null == b ) return null; int len = b.length; byte[] hex = new byte[ len << 1 ]; for ( int i = 0, j = 0; i < len; i++, j+=2 ) { hex[ j ] = (byte) ((b[ i ] & 0xF0) >> 4); hex[ j ] += 10 > hex[ j ] ? 48 : 87; hex[ j + 1 ] = (byte) (b[ i ] & 0x0F); hex[ j + 1 ] += 10 > hex[ j + 1 ] ? 48 : 87; } return new String( hex ); } /** * Returns the MD5 hash of a String. * *@param str Description of the Parameter *@return Description of the Return Value */ public static String hashMD5( String str ) { return hashMD5(str.getBytes()); } public static String hashMD5( byte[] bytes ) { MessageDigest md = null; try { md = MessageDigest.getInstance( "MD5" ); md.update( bytes ); } catch ( NoSuchAlgorithmException e ) { e.printStackTrace(); // it's got to be there } return toHexString( md.digest() ); } /** * Returns the SHA hash of a String. * *@param str Description of the Parameter *@return Description of the Return Value */ public static String hashSHA( String str ) { byte[] b = str.getBytes(); MessageDigest md = null; try { md = MessageDigest.getInstance( "SHA1" ); md.update( b ); } catch ( NoSuchAlgorithmException e ) { e.printStackTrace(); // it's got to be there } return toHexString( md.digest() ); } /** * Description of the Method * *@param input Description of the Parameter *@return Description of the Return Value */ public static synchronized String rot13( String input ) { StringBuffer output = new StringBuffer(); if ( input != null ) { for ( int i = 0; i < input.length(); i++ ) { char inChar = input.charAt( i ); if ( ( inChar >= 'A' ) & ( inChar <= 'Z' ) ) { inChar += 13; if ( inChar > 'Z' ) { inChar -= 26; } } if ( ( inChar >= 'a' ) & ( inChar <= 'z' ) ) { inChar += 13; if ( inChar > 'z' ) { inChar -= 26; } } output.append( inChar ); } } return output.toString(); } /** * Description of the Method * *@param str Description of the Parameter *@return Description of the Return Value */ public static String urlDecode( String str ) { try { return ( URLDecoder.decode( str, "utf-8" ) ); } catch ( Exception e ) { return ( "Decoding error" ); } } /** * Description of the Method * *@param str Description of the Parameter *@return Description of the Return Value */ public static String urlEncode( String str ) { try { return ( URLEncoder.encode( str, "utf-8" ) ); } catch ( Exception e ) { return ( "Encoding error" ); } } } // class Base64