diff --git a/app/src/main/java/com/monke/monkeybook/ProxyManager.java b/app/src/main/java/com/monke/monkeybook/ProxyManager.java index ad80c47..87e89dc 100644 --- a/app/src/main/java/com/monke/monkeybook/ProxyManager.java +++ b/app/src/main/java/com/monke/monkeybook/ProxyManager.java @@ -2,7 +2,7 @@ package com.monke.monkeybook; import android.content.SharedPreferences; -import com.monke.monkeybook.utils.AESUtil; +import com.monke.monkeybook.utils.aes.AESUtil; import org.jsoup.helper.StringUtil; diff --git a/app/src/main/java/com/monke/monkeybook/utils/AESUtil.java b/app/src/main/java/com/monke/monkeybook/utils/AESUtil.java deleted file mode 100644 index bedc0c8..0000000 --- a/app/src/main/java/com/monke/monkeybook/utils/AESUtil.java +++ /dev/null @@ -1,130 +0,0 @@ -package com.monke.monkeybook.utils; - -import javax.crypto.*; -import javax.crypto.spec.SecretKeySpec; -import java.io.UnsupportedEncodingException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.Scanner; - - -public class AESUtil { - - public static String aesEncode(String content,String password){ - byte[] result = encrypt(content,password); - if(result!=null && result.length>0){ - return ParseSystemUtil.parseByte2HexStr(result); - } - return null; - } - - public static String aesDecode(String content,String password){ - byte[] temp = ParseSystemUtil.parseHexStr2Byte(content); - if(temp!=null && temp.length>0){ - byte[] result = decrypt(temp,password); - if(result!=null && result.length>0){ - return new String(result); - } - } - return null; - } - - /** - * AES加密字符串 - * - * @param content - * 需要被加密的字符串 - * @param keyWord - * 加密需要的密码 - * @return 密文 - */ - private static byte[] encrypt(String content, String keyWord) { - try { - KeyGenerator kgen = KeyGenerator.getInstance("AES");// 创建AES的Key生产者 - - SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); - random.setSeed(keyWord.getBytes()); - kgen.init(128, random);// 利用用户密码作为随机数初始化出 - // 128位的key生产者 - //加密没关系,SecureRandom是生成安全随机数序列,password.getBytes()是种子,只要种子相同,序列就一样,所以解密只要有password就行 - - SecretKey secretKey = kgen.generateKey();// 根据用户密码,生成一个密钥 - - byte[] enCodeFormat = secretKey.getEncoded();// 返回基本编码格式的密钥,如果此密钥不支持编码,则返回 - // null。 - - SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");// 转换为AES专用密钥 - - Cipher cipher = Cipher.getInstance("AES");// 创建密码器 - - byte[] byteContent = content.getBytes("utf-8"); - - cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化为加密模式的密码器 - - byte[] result = cipher.doFinal(byteContent);// 加密 - - return result; - - } catch (NoSuchPaddingException e) { - e.printStackTrace(); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } catch (InvalidKeyException e) { - e.printStackTrace(); - } catch (IllegalBlockSizeException e) { - e.printStackTrace(); - } catch (BadPaddingException e) { - e.printStackTrace(); - } - return null; - } - - /** - * 解密AES加密过的字符串 - * - * @param content - * AES加密过过的内容 - * @param keyWord - * 加密时的密码 - * @return 明文 - */ - private static byte[] decrypt(byte[] content, String keyWord) { - try { - KeyGenerator kgen = KeyGenerator.getInstance("AES");// 创建AES的Key生产者 - SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); - random.setSeed(keyWord.getBytes()); - kgen.init(128, random); - SecretKey secretKey = kgen.generateKey();// 根据用户密码,生成一个密钥 - byte[] enCodeFormat = secretKey.getEncoded();// 返回基本编码格式的密钥 - SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");// 转换为AES专用密钥 - Cipher cipher = Cipher.getInstance("AES");// 创建密码器 - cipher.init(Cipher.DECRYPT_MODE, key);// 初始化为解密模式的密码器 - byte[] result = cipher.doFinal(content); - return result; // 明文 - - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } catch (NoSuchPaddingException e) { - e.printStackTrace(); - } catch (InvalidKeyException e) { - e.printStackTrace(); - } catch (IllegalBlockSizeException e) { - e.printStackTrace(); - } catch (BadPaddingException e) { - e.printStackTrace(); - } - return null; - } - - public static void main(String[] args){ - while (true){ - Scanner sn = new Scanner(System.in); - String content = sn.next(); -// System.out.println(aesDecode(content,ServerConfig.AES_ENCODE_KEY)); - System.out.println(); - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/monke/monkeybook/utils/aes/AESUtil.java b/app/src/main/java/com/monke/monkeybook/utils/aes/AESUtil.java new file mode 100644 index 0000000..9195c1b --- /dev/null +++ b/app/src/main/java/com/monke/monkeybook/utils/aes/AESUtil.java @@ -0,0 +1,47 @@ +package com.monke.monkeybook.utils.aes; + +import com.monke.monkeybook.utils.ParseSystemUtil; + +import javax.crypto.*; +import javax.crypto.spec.SecretKeySpec; + +import java.io.UnsupportedEncodingException; + +public class AESUtil { + public static String aesEncode(String cleartext, String seed) throws Exception { + byte[] rawKey = deriveKeyInsecurely(seed, 32).getEncoded(); + byte[] result = encrypt(rawKey, cleartext.getBytes()); + return ParseSystemUtil.parseByte2HexStr(result); + } + + public static String aesDecode(String encrypted, String seed) throws Exception { + byte[] rawKey = deriveKeyInsecurely(seed, 32).getEncoded(); + byte[] enc = ParseSystemUtil.parseHexStr2Byte(encrypted); + byte[] result = decrypt(rawKey, enc); + return new String(result); + } + + private static SecretKey deriveKeyInsecurely(String password, int keySizeInBytes) throws UnsupportedEncodingException { + byte[] passwordBytes = password.getBytes("UTF-8"); + return new SecretKeySpec( + InsecureSHA1PRNGKeyDerivator.deriveInsecureKey( + passwordBytes, keySizeInBytes), + "AES"); + } + + private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception { + SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, skeySpec); + byte[] encrypted = cipher.doFinal(clear); + return encrypted; + } + + private static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception { + SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, skeySpec); + byte[] decrypted = cipher.doFinal(encrypted); + return decrypted; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/monke/monkeybook/utils/aes/InsecureSHA1PRNGKeyDerivator.java b/app/src/main/java/com/monke/monkeybook/utils/aes/InsecureSHA1PRNGKeyDerivator.java new file mode 100644 index 0000000..3d55d6c --- /dev/null +++ b/app/src/main/java/com/monke/monkeybook/utils/aes/InsecureSHA1PRNGKeyDerivator.java @@ -0,0 +1,609 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.monke.monkeybook.utils.aes; + +/** + * Stripped-down version of the SHA1PRNG provided by the Crypto provider. + * + * The Crypto provider that offers this functionality was deprecated on Android. + * + * Use this class only to retrieve encrypted data that couldn't be retrieved otherwise. + */ +class InsecureSHA1PRNGKeyDerivator { + + /** + * Only public method. Derive a key from the given seed. + * + * Use this method only to retrieve encrypted data that couldn't be retrieved otherwise. + * + * @param seed seed used for the random generator, usually coming from a password + * @param keySizeInBytes length of the array returned + */ + public static byte[] deriveInsecureKey(byte[] seed, int keySizeInBytes) { + InsecureSHA1PRNGKeyDerivator derivator = new InsecureSHA1PRNGKeyDerivator(); + derivator.setSeed(seed); + byte[] key = new byte[keySizeInBytes]; + derivator.nextBytes(key); + return key; + } + + // constants to use in expressions operating on bytes in int and long variables: + // END_FLAGS - final bytes in words to append to message; + // see "ch.5.1 Padding the Message, FIPS 180-2" + // RIGHT1 - shifts to right for left half of long + // RIGHT2 - shifts to right for right half of long + // LEFT - shifts to left for bytes + // MASK - mask to select counter's bytes after shift to right + + private static final int[] END_FLAGS = { 0x80000000, 0x800000, 0x8000, 0x80 }; + + private static final int[] RIGHT1 = { 0, 40, 48, 56 }; + + private static final int[] RIGHT2 = { 0, 8, 16, 24 }; + + private static final int[] LEFT = { 0, 24, 16, 8 }; + + private static final int[] MASK = { 0xFFFFFFFF, 0x00FFFFFF, 0x0000FFFF, + 0x000000FF }; + + // HASHBYTES_TO_USE defines # of bytes returned by "computeHash(byte[])" + // to use to form byte array returning by the "nextBytes(byte[])" method + // Note, that this implementation uses more bytes than it is defined + // in the above specification. + private static final int HASHBYTES_TO_USE = 20; + + // value of 16 defined in the "SECURE HASH STANDARD", FIPS PUB 180-2 + private static final int FRAME_LENGTH = 16; + + // miscellaneous constants defined in this implementation: + // COUNTER_BASE - initial value to set to "counter" before computing "nextBytes(..)"; + // note, that the exact value is not defined in STANDARD + // HASHCOPY_OFFSET - offset for copy of current hash in "copies" array + // EXTRAFRAME_OFFSET - offset for extra frame in "copies" array; + // as the extra frame follows the current hash frame, + // EXTRAFRAME_OFFSET is equal to length of current hash frame + // FRAME_OFFSET - offset for frame in "copies" array + // MAX_BYTES - maximum # of seed bytes processing which doesn't require extra frame + // see (1) comments on usage of "seed" array below and + // (2) comments in "engineNextBytes(byte[])" method + // + // UNDEFINED - three states of engine; initially its state is "UNDEFINED" + // SET_SEED call to "engineSetSeed" sets up "SET_SEED" state, + // NEXT_BYTES call to "engineNextByte" sets up "NEXT_BYTES" state + + private static final int COUNTER_BASE = 0; + + private static final int HASHCOPY_OFFSET = 0; + + private static final int EXTRAFRAME_OFFSET = 5; + + private static final int FRAME_OFFSET = 21; + + private static final int MAX_BYTES = 48; + + private static final int UNDEFINED = 0; + + private static final int SET_SEED = 1; + + private static final int NEXT_BYTES = 2; + + // Structure of "seed" array: + // - 0-79 - words for computing hash + // - 80 - unused + // - 81 - # of seed bytes in current seed frame + // - 82-86 - 5 words, current seed hash + private transient int[] seed; + + // total length of seed bytes, including all processed + private transient long seedLength; + + // Structure of "copies" array + // - 0-4 - 5 words, copy of current seed hash + // - 5-20 - extra 16 words frame; + // is used if final padding exceeds 512-bit length + // - 21-36 - 16 word frame to store a copy of remaining bytes + private transient int[] copies; + + // ready "next" bytes; needed because words are returned + private transient byte[] nextBytes; + + // index of used bytes in "nextBytes" array + private transient int nextBIndex; + + // variable required according to "SECURE HASH STANDARD" + private transient long counter; + + // contains int value corresponding to engine's current state + private transient int state; + + /** + * constant defined in "SECURE HASH STANDARD" + */ + private static final int H0 = 0x67452301; + + + /** + * constant defined in "SECURE HASH STANDARD" + */ + private static final int H1 = 0xEFCDAB89; + + + /** + * constant defined in "SECURE HASH STANDARD" + */ + private static final int H2 = 0x98BADCFE; + + + /** + * constant defined in "SECURE HASH STANDARD" + */ + private static final int H3 = 0x10325476; + + + /** + * constant defined in "SECURE HASH STANDARD" + */ + private static final int H4 = 0xC3D2E1F0; + + + /** + * offset in buffer to store number of bytes in 0-15 word frame + */ + private static final int BYTES_OFFSET = 81; + + + /** + * offset in buffer to store current hash value + */ + private static final int HASH_OFFSET = 82; + + + /** + * # of bytes in H0-H4 words;
+ * in this implementation # is set to 20 (in general # varies from 1 to 20) + */ + private static final int DIGEST_LENGTH = 20; + + // The "seed" array is used to compute both "current seed hash" and "next bytes". + // + // As the "SHA1" algorithm computes a hash of entire seed by splitting it into + // a number of the 512-bit length frames (512 bits = 64 bytes = 16 words), + // "current seed hash" is a hash (5 words, 20 bytes) for all previous full frames; + // remaining bytes are stored in the 0-15 word frame of the "seed" array. + // + // As for calculating "next bytes", + // both remaining bytes and "current seed hash" are used, + // to preserve the latter for following "setSeed(..)" commands, + // the following technique is used: + // - upon getting "nextBytes(byte[])" invoked, single or first in row, + // which requires computing new hash, that is, + // there is no more bytes remaining from previous "next bytes" computation, + // remaining bytes are copied into the 21-36 word frame of the "copies" array; + // - upon getting "setSeed(byte[])" invoked, single or first in row, + // remaining bytes are copied back. + + private InsecureSHA1PRNGKeyDerivator() { + seed = new int[HASH_OFFSET + EXTRAFRAME_OFFSET]; + seed[HASH_OFFSET] = H0; + seed[HASH_OFFSET + 1] = H1; + seed[HASH_OFFSET + 2] = H2; + seed[HASH_OFFSET + 3] = H3; + seed[HASH_OFFSET + 4] = H4; + + seedLength = 0; + copies = new int[2 * FRAME_LENGTH + EXTRAFRAME_OFFSET]; + nextBytes = new byte[DIGEST_LENGTH]; + nextBIndex = HASHBYTES_TO_USE; + counter = COUNTER_BASE; + state = UNDEFINED; + } + + /* + * The method invokes the SHA1Impl's "updateHash(..)" method + * to update current seed frame and + * to compute new intermediate hash value if the frame is full. + * + * After that it computes a length of whole seed. + */ + private void updateSeed(byte[] bytes) { + + // on call: "seed" contains current bytes and current hash; + // on return: "seed" contains new current bytes and possibly new current hash + // if after adding, seed bytes overfill its buffer + updateHash(seed, bytes, 0, bytes.length - 1); + + seedLength += bytes.length; + } + + /** + * Changes current seed by supplementing a seed argument to the current seed, + * if this already set; + * the argument is used as first seed otherwise.
+ * + * The method overrides "engineSetSeed(byte[])" in class SecureRandomSpi. + * + * @param + * seed - byte array + * @throws + * NullPointerException - if null is passed to the "seed" argument + */ + private void setSeed(byte[] seed) { + if (seed == null) { + throw new NullPointerException("seed == null"); + } + + if (state == NEXT_BYTES) { // first setSeed after NextBytes; restoring hash + System.arraycopy(copies, HASHCOPY_OFFSET, this.seed, HASH_OFFSET, + EXTRAFRAME_OFFSET); + } + state = SET_SEED; + + if (seed.length != 0) { + updateSeed(seed); + } + } + + /** + * Writes random bytes into an array supplied. + * Bits in a byte are from left to right.
+ * + * To generate random bytes, the "expansion of source bits" method is used, + * that is, + * the current seed with a 64-bit counter appended is used to compute new bits. + * The counter is incremented by 1 for each 20-byte output.
+ * + * The method overrides engineNextBytes in class SecureRandomSpi. + * + * @param + * bytes - byte array to be filled in with bytes + * @throws + * NullPointerException - if null is passed to the "bytes" argument + */ + protected synchronized void nextBytes(byte[] bytes) { + + int i, n; + + long bits; // number of bits required by Secure Hash Standard + int nextByteToReturn; // index of ready bytes in "bytes" array + int lastWord; // index of last word in frame containing bytes + + // This is a bug since words are 4 bytes. Android used to keep it this way for backward + // compatibility. + final int extrabytes = 7;// # of bytes to add in order to computer # of 8 byte words + + if (bytes == null) { + throw new NullPointerException("bytes == null"); + } + + // This is a bug since extraBytes == 7 instead of 3. Android used to keep it this way for + // backward compatibility. + lastWord = seed[BYTES_OFFSET] == 0 ? 0 + : (seed[BYTES_OFFSET] + extrabytes) >> 3 - 1; + + if (state == UNDEFINED) { + + throw new IllegalStateException("No seed supplied!"); + + } else if (state == SET_SEED) { + + System.arraycopy(seed, HASH_OFFSET, copies, HASHCOPY_OFFSET, + EXTRAFRAME_OFFSET); + + // possible cases for 64-byte frame: + // + // seed bytes < 48 - remaining bytes are enough for all, 8 counter bytes, + // 0x80, and 8 seedLength bytes; no extra frame required + // 48 < seed bytes < 56 - remaining 9 bytes are for 0x80 and 8 counter bytes + // extra frame contains only seedLength value at the end + // seed bytes > 55 - extra frame contains both counter's bytes + // at the beginning and seedLength value at the end; + // note, that beginning extra bytes are not more than 8, + // that is, only 2 extra words may be used + + // no need to set to "0" 3 words after "lastWord" and + // more than two words behind frame + for (i = lastWord + 3; i < FRAME_LENGTH + 2; i++) { + seed[i] = 0; + } + + bits = (seedLength << 3) + 64; // transforming # of bytes into # of bits + + // putting # of bits into two last words (14,15) of 16 word frame in + // seed or copies array depending on total length after padding + if (seed[BYTES_OFFSET] < MAX_BYTES) { + seed[14] = (int) (bits >>> 32); + seed[15] = (int) (bits & 0xFFFFFFFF); + } else { + copies[EXTRAFRAME_OFFSET + 14] = (int) (bits >>> 32); + copies[EXTRAFRAME_OFFSET + 15] = (int) (bits & 0xFFFFFFFF); + } + + nextBIndex = HASHBYTES_TO_USE; // skipping remaining random bits + } + state = NEXT_BYTES; + + if (bytes.length == 0) { + return; + } + + nextByteToReturn = 0; + + // possibly not all of HASHBYTES_TO_USE bytes were used previous time + n = (HASHBYTES_TO_USE - nextBIndex) < (bytes.length - nextByteToReturn) ? HASHBYTES_TO_USE + - nextBIndex + : bytes.length - nextByteToReturn; + if (n > 0) { + System.arraycopy(nextBytes, nextBIndex, bytes, nextByteToReturn, n); + nextBIndex += n; + nextByteToReturn += n; + } + + if (nextByteToReturn >= bytes.length) { + return; // return because "bytes[]" are filled in + } + + n = seed[BYTES_OFFSET] & 0x03; + for (;;) { + if (n == 0) { + + seed[lastWord] = (int) (counter >>> 32); + seed[lastWord + 1] = (int) (counter & 0xFFFFFFFF); + seed[lastWord + 2] = END_FLAGS[0]; + + } else { + + seed[lastWord] |= (int) ((counter >>> RIGHT1[n]) & MASK[n]); + seed[lastWord + 1] = (int) ((counter >>> RIGHT2[n]) & 0xFFFFFFFF); + seed[lastWord + 2] = (int) ((counter << LEFT[n]) | END_FLAGS[n]); + } + if (seed[BYTES_OFFSET] > MAX_BYTES) { + copies[EXTRAFRAME_OFFSET] = seed[FRAME_LENGTH]; + copies[EXTRAFRAME_OFFSET + 1] = seed[FRAME_LENGTH + 1]; + } + + computeHash(seed); + + if (seed[BYTES_OFFSET] > MAX_BYTES) { + + System.arraycopy(seed, 0, copies, FRAME_OFFSET, FRAME_LENGTH); + System.arraycopy(copies, EXTRAFRAME_OFFSET, seed, 0, + FRAME_LENGTH); + + computeHash(seed); + System.arraycopy(copies, FRAME_OFFSET, seed, 0, FRAME_LENGTH); + } + counter++; + + int j = 0; + for (i = 0; i < EXTRAFRAME_OFFSET; i++) { + int k = seed[HASH_OFFSET + i]; + nextBytes[j] = (byte) (k >>> 24); // getting first byte from left + nextBytes[j + 1] = (byte) (k >>> 16); // getting second byte from left + nextBytes[j + 2] = (byte) (k >>> 8); // getting third byte from left + nextBytes[j + 3] = (byte) (k); // getting fourth byte from left + j += 4; + } + + nextBIndex = 0; + j = HASHBYTES_TO_USE < (bytes.length - nextByteToReturn) ? HASHBYTES_TO_USE + : bytes.length - nextByteToReturn; + + if (j > 0) { + System.arraycopy(nextBytes, 0, bytes, nextByteToReturn, j); + nextByteToReturn += j; + nextBIndex += j; + } + + if (nextByteToReturn >= bytes.length) { + break; + } + } + } + + /** + * The method generates a 160 bit hash value using + * a 512 bit message stored in first 16 words of int[] array argument and + * current hash value stored in five words, beginning OFFSET+1, of the array argument. + * Computation is done according to SHA-1 algorithm. + * + * The resulting hash value replaces the previous hash value in the array; + * original bits of the message are not preserved. + * + * No checks on argument supplied, that is, + * a calling method is responsible for such checks. + * In case of incorrect array passed to the method + * either NPE or IndexOutOfBoundException gets thrown by JVM. + * + * @params + * arrW - integer array; arrW.length >= (BYTES_OFFSET+6);
+ * only first (BYTES_OFFSET+6) words are used + */ + private static void computeHash(int[] arrW) { + + int a = arrW[HASH_OFFSET ]; + int b = arrW[HASH_OFFSET +1]; + int c = arrW[HASH_OFFSET +2]; + int d = arrW[HASH_OFFSET +3]; + int e = arrW[HASH_OFFSET +4]; + + int temp; + + // In this implementation the "d. For t = 0 to 79 do" loop + // is split into four loops. The following constants: + // K = 5A827999 0 <= t <= 19 + // K = 6ED9EBA1 20 <= t <= 39 + // K = 8F1BBCDC 40 <= t <= 59 + // K = CA62C1D6 60 <= t <= 79 + // are hex literals in the loops. + + for ( int t = 16; t < 80 ; t++ ) { + + temp = arrW[t-3] ^ arrW[t-8] ^ arrW[t-14] ^ arrW[t-16]; + arrW[t] = ( temp<<1 ) | ( temp>>>31 ); + } + + for ( int t = 0 ; t < 20 ; t++ ) { + + temp = ( ( a<<5 ) | ( a>>>27 ) ) + + ( ( b & c) | ((~b) & d) ) + + ( e + arrW[t] + 0x5A827999 ) ; + e = d; + d = c; + c = ( b<<30 ) | ( b>>>2 ) ; + b = a; + a = temp; + } + for ( int t = 20 ; t < 40 ; t++ ) { + + temp = ((( a<<5 ) | ( a>>>27 ))) + (b ^ c ^ d) + (e + arrW[t] + 0x6ED9EBA1) ; + e = d; + d = c; + c = ( b<<30 ) | ( b>>>2 ) ; + b = a; + a = temp; + } + for ( int t = 40 ; t < 60 ; t++ ) { + + temp = (( a<<5 ) | ( a>>>27 )) + ((b & c) | (b & d) | (c & d)) + + (e + arrW[t] + 0x8F1BBCDC) ; + e = d; + d = c; + c = ( b<<30 ) | ( b>>>2 ) ; + b = a; + a = temp; + } + for ( int t = 60 ; t < 80 ; t++ ) { + + temp = ((( a<<5 ) | ( a>>>27 ))) + (b ^ c ^ d) + (e + arrW[t] + 0xCA62C1D6) ; + e = d; + d = c; + c = ( b<<30 ) | ( b>>>2 ) ; + b = a; + a = temp; + } + + arrW[HASH_OFFSET ] += a; + arrW[HASH_OFFSET +1] += b; + arrW[HASH_OFFSET +2] += c; + arrW[HASH_OFFSET +3] += d; + arrW[HASH_OFFSET +4] += e; + } + + /** + * The method appends new bytes to existing ones + * within limit of a frame of 64 bytes (16 words). + * + * Once a length of accumulated bytes reaches the limit + * the "computeHash(int[])" method is invoked on the array to compute updated hash, + * and the number of bytes in the frame is set to 0. + * Thus, after appending all bytes, the array contain only those bytes + * that were not used in computing final hash value yet. + * + * No checks on arguments passed to the method, that is, + * a calling method is responsible for such checks. + * + * @params + * intArray - int array containing bytes to which to append; + * intArray.length >= (BYTES_OFFSET+6) + * @params + * byteInput - array of bytes to use for the update + * @params + * from - the offset to start in the "byteInput" array + * @params + * to - a number of the last byte in the input array to use, + * that is, for first byte "to"==0, for last byte "to"==input.length-1 + */ + private static void updateHash(int[] intArray, byte[] byteInput, int fromByte, int toByte) { + + // As intArray contains a packed bytes + // the buffer's index is in the intArray[BYTES_OFFSET] element + + int index = intArray[BYTES_OFFSET]; + int i = fromByte; + int maxWord; + int nBytes; + + int wordIndex = index >>2; + int byteIndex = index & 0x03; + + intArray[BYTES_OFFSET] = ( index + toByte - fromByte + 1 ) & 077 ; + + // In general case there are 3 stages : + // - appending bytes to non-full word, + // - writing 4 bytes into empty words, + // - writing less than 4 bytes in last word + + if ( byteIndex != 0 ) { // appending bytes in non-full word (as if) + + for ( ; ( i <= toByte ) && ( byteIndex < 4 ) ; i++ ) { + intArray[wordIndex] |= ( byteInput[i] & 0xFF ) << ((3 - byteIndex)<<3) ; + byteIndex++; + } + if ( byteIndex == 4 ) { + wordIndex++; + if ( wordIndex == 16 ) { // intArray is full, computing hash + + computeHash(intArray); + wordIndex = 0; + } + } + if ( i > toByte ) { // all input bytes appended + return ; + } + } + + // writing full words + + maxWord = (toByte - i + 1) >> 2; // # of remaining full words, may be "0" + for ( int k = 0; k < maxWord ; k++ ) { + + intArray[wordIndex] = ( ((int) byteInput[i ] & 0xFF) <<24 ) | + ( ((int) byteInput[i +1] & 0xFF) <<16 ) | + ( ((int) byteInput[i +2] & 0xFF) <<8 ) | + ( ((int) byteInput[i +3] & 0xFF) ) ; + i += 4; + wordIndex++; + + if ( wordIndex < 16 ) { // buffer is not full yet + continue; + } + computeHash(intArray); // buffer is full, computing hash + wordIndex = 0; + } + + // writing last incomplete word + // after writing free byte positions are set to "0"s + + nBytes = toByte - i +1; + if ( nBytes != 0 ) { + + int w = ((int) byteInput[i] & 0xFF) <<24 ; + + if ( nBytes != 1 ) { + w |= ((int) byteInput[i +1] & 0xFF) <<16 ; + if ( nBytes != 2) { + w |= ((int) byteInput[i +2] & 0xFF) <<8 ; + } + } + intArray[wordIndex] = w; + } + + return ; + } +} diff --git a/app/src/main/java/com/monke/monkeybook/utils/base64/BASE64Decoder.java b/app/src/main/java/com/monke/monkeybook/utils/base64/BASE64Decoder.java new file mode 100644 index 0000000..49c99c2 --- /dev/null +++ b/app/src/main/java/com/monke/monkeybook/utils/base64/BASE64Decoder.java @@ -0,0 +1,91 @@ +package com.monke.monkeybook.utils.base64; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PushbackInputStream; + +public class BASE64Decoder extends CharacterDecoder{ + private static final char[] pem_array = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; + private static final byte[] pem_convert_array = new byte[256]; + byte[] decode_buffer = new byte[4]; + + public BASE64Decoder() { + } + + protected int bytesPerAtom() { + return 4; + } + + protected int bytesPerLine() { + return 72; + } + + protected void decodeAtom(PushbackInputStream var1, OutputStream var2, int var3) throws IOException { + byte var5 = -1; + byte var6 = -1; + byte var7 = -1; + byte var8 = -1; + if (var3 < 2) { + throw new CEFormatException("BASE64Decoder: Not enough bytes for an atom."); + } else { + int var4; + do { + var4 = var1.read(); + if (var4 == -1) { + throw new CEStreamExhausted(); + } + } while(var4 == 10 || var4 == 13); + + this.decode_buffer[0] = (byte)var4; + var4 = this.readFully(var1, this.decode_buffer, 1, var3 - 1); + if (var4 == -1) { + throw new CEStreamExhausted(); + } else { + if (var3 > 3 && this.decode_buffer[3] == 61) { + var3 = 3; + } + + if (var3 > 2 && this.decode_buffer[2] == 61) { + var3 = 2; + } + + switch(var3) { + case 4: + var8 = pem_convert_array[this.decode_buffer[3] & 255]; + case 3: + var7 = pem_convert_array[this.decode_buffer[2] & 255]; + case 2: + var6 = pem_convert_array[this.decode_buffer[1] & 255]; + var5 = pem_convert_array[this.decode_buffer[0] & 255]; + default: + switch(var3) { + case 2: + var2.write((byte)(var5 << 2 & 252 | var6 >>> 4 & 3)); + break; + case 3: + var2.write((byte)(var5 << 2 & 252 | var6 >>> 4 & 3)); + var2.write((byte)(var6 << 4 & 240 | var7 >>> 2 & 15)); + break; + case 4: + var2.write((byte)(var5 << 2 & 252 | var6 >>> 4 & 3)); + var2.write((byte)(var6 << 4 & 240 | var7 >>> 2 & 15)); + var2.write((byte)(var7 << 6 & 192 | var8 & 63)); + } + + } + } + } + } + + static { + int var0; + for(var0 = 0; var0 < 255; ++var0) { + pem_convert_array[var0] = -1; + } + + for(var0 = 0; var0 < pem_array.length; ++var0) { + pem_convert_array[pem_array[var0]] = (byte)var0; + } + + } +} diff --git a/app/src/main/java/com/monke/monkeybook/utils/base64/BASE64Encoder.java b/app/src/main/java/com/monke/monkeybook/utils/base64/BASE64Encoder.java new file mode 100644 index 0000000..4c5fe1d --- /dev/null +++ b/app/src/main/java/com/monke/monkeybook/utils/base64/BASE64Encoder.java @@ -0,0 +1,52 @@ +package com.monke.monkeybook.utils.base64; + +import java.io.IOException; +import java.io.OutputStream; + +public class BASE64Encoder extends CharacterEncoder{ + private static final char[] pem_array = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; + + public BASE64Encoder() { + } + + protected int bytesPerAtom() { + return 3; + } + + protected int bytesPerLine() { + return 57; + } + + protected void encodeAtom(OutputStream var1, byte[] var2, int var3, int var4) throws IOException { + byte var5; + if (var4 == 1) { + var5 = var2[var3]; + byte var6 = 0; + boolean var7 = false; + var1.write(pem_array[var5 >>> 2 & 63]); + var1.write(pem_array[(var5 << 4 & 48) + (var6 >>> 4 & 15)]); + var1.write(61); + var1.write(61); + } else { + byte var8; + if (var4 == 2) { + var5 = var2[var3]; + var8 = var2[var3 + 1]; + byte var9 = 0; + var1.write(pem_array[var5 >>> 2 & 63]); + var1.write(pem_array[(var5 << 4 & 48) + (var8 >>> 4 & 15)]); + var1.write(pem_array[(var8 << 2 & 60) + (var9 >>> 6 & 3)]); + var1.write(61); + } else { + var5 = var2[var3]; + var8 = var2[var3 + 1]; + byte var10 = var2[var3 + 2]; + var1.write(pem_array[var5 >>> 2 & 63]); + var1.write(pem_array[(var5 << 4 & 48) + (var8 >>> 4 & 15)]); + var1.write(pem_array[(var8 << 2 & 60) + (var10 >>> 6 & 3)]); + var1.write(pem_array[var10 & 63]); + } + } + + } +} diff --git a/app/src/main/java/com/monke/monkeybook/utils/base64/CEFormatException.java b/app/src/main/java/com/monke/monkeybook/utils/base64/CEFormatException.java new file mode 100644 index 0000000..fc6450b --- /dev/null +++ b/app/src/main/java/com/monke/monkeybook/utils/base64/CEFormatException.java @@ -0,0 +1,11 @@ +package com.monke.monkeybook.utils.base64; + +import java.io.IOException; + +public class CEFormatException extends IOException { + static final long serialVersionUID = -7139121221067081482L; + + public CEFormatException(String var1) { + super(var1); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/monke/monkeybook/utils/base64/CEStreamExhausted.java b/app/src/main/java/com/monke/monkeybook/utils/base64/CEStreamExhausted.java new file mode 100644 index 0000000..51e1f26 --- /dev/null +++ b/app/src/main/java/com/monke/monkeybook/utils/base64/CEStreamExhausted.java @@ -0,0 +1,10 @@ +package com.monke.monkeybook.utils.base64; + +import java.io.IOException; + +public class CEStreamExhausted extends IOException { + static final long serialVersionUID = -5889118049525891904L; + + public CEStreamExhausted() { + } +} diff --git a/app/src/main/java/com/monke/monkeybook/utils/base64/CharacterDecoder.java b/app/src/main/java/com/monke/monkeybook/utils/base64/CharacterDecoder.java new file mode 100644 index 0000000..21f6864 --- /dev/null +++ b/app/src/main/java/com/monke/monkeybook/utils/base64/CharacterDecoder.java @@ -0,0 +1,102 @@ +package com.monke.monkeybook.utils.base64; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PushbackInputStream; +import java.nio.ByteBuffer; + +public abstract class CharacterDecoder { + public CharacterDecoder() { + } + + protected abstract int bytesPerAtom(); + + protected abstract int bytesPerLine(); + + protected void decodeBufferPrefix(PushbackInputStream var1, OutputStream var2) throws IOException { + } + + protected void decodeBufferSuffix(PushbackInputStream var1, OutputStream var2) throws IOException { + } + + protected int decodeLinePrefix(PushbackInputStream var1, OutputStream var2) throws IOException { + return this.bytesPerLine(); + } + + protected void decodeLineSuffix(PushbackInputStream var1, OutputStream var2) throws IOException { + } + + protected void decodeAtom(PushbackInputStream var1, OutputStream var2, int var3) throws IOException { + throw new CEStreamExhausted(); + } + + protected int readFully(InputStream var1, byte[] var2, int var3, int var4) throws IOException { + for(int var5 = 0; var5 < var4; ++var5) { + int var6 = var1.read(); + if (var6 == -1) { + return var5 == 0 ? -1 : var5; + } + + var2[var5 + var3] = (byte)var6; + } + + return var4; + } + + public void decodeBuffer(InputStream var1, OutputStream var2) throws IOException { + int var4 = 0; + PushbackInputStream var5 = new PushbackInputStream(var1); + this.decodeBufferPrefix(var5, var2); + + while(true) { + try { + int var6 = this.decodeLinePrefix(var5, var2); + + int var3; + for(var3 = 0; var3 + this.bytesPerAtom() < var6; var3 += this.bytesPerAtom()) { + this.decodeAtom(var5, var2, this.bytesPerAtom()); + var4 += this.bytesPerAtom(); + } + + if (var3 + this.bytesPerAtom() == var6) { + this.decodeAtom(var5, var2, this.bytesPerAtom()); + var4 += this.bytesPerAtom(); + } else { + this.decodeAtom(var5, var2, var6 - var3); + var4 += var6 - var3; + } + + this.decodeLineSuffix(var5, var2); + } catch (CEStreamExhausted var8) { + this.decodeBufferSuffix(var5, var2); + return; + } + } + } + + public byte[] decodeBuffer(String var1) throws IOException { + byte[] var2 = new byte[var1.length()]; + var1.getBytes(0, var1.length(), var2, 0); + ByteArrayInputStream var3 = new ByteArrayInputStream(var2); + ByteArrayOutputStream var4 = new ByteArrayOutputStream(); + this.decodeBuffer(var3, var4); + return var4.toByteArray(); + } + + public byte[] decodeBuffer(InputStream var1) throws IOException { + ByteArrayOutputStream var2 = new ByteArrayOutputStream(); + this.decodeBuffer(var1, var2); + return var2.toByteArray(); + } + + public ByteBuffer decodeBufferToByteBuffer(String var1) throws IOException { + return ByteBuffer.wrap(this.decodeBuffer(var1)); + } + + public ByteBuffer decodeBufferToByteBuffer(InputStream var1) throws IOException { + return ByteBuffer.wrap(this.decodeBuffer(var1)); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/monke/monkeybook/utils/base64/CharacterEncoder.java b/app/src/main/java/com/monke/monkeybook/utils/base64/CharacterEncoder.java new file mode 100644 index 0000000..1184f5b --- /dev/null +++ b/app/src/main/java/com/monke/monkeybook/utils/base64/CharacterEncoder.java @@ -0,0 +1,181 @@ +package com.monke.monkeybook.utils.base64; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.nio.ByteBuffer; + +public abstract class CharacterEncoder { + protected PrintStream pStream; + + public CharacterEncoder() { + } + + protected abstract int bytesPerAtom(); + + protected abstract int bytesPerLine(); + + protected void encodeBufferPrefix(OutputStream var1) throws IOException { + this.pStream = new PrintStream(var1); + } + + protected void encodeBufferSuffix(OutputStream var1) throws IOException { + } + + protected void encodeLinePrefix(OutputStream var1, int var2) throws IOException { + } + + protected void encodeLineSuffix(OutputStream var1) throws IOException { + this.pStream.println(); + } + + protected abstract void encodeAtom(OutputStream var1, byte[] var2, int var3, int var4) throws IOException; + + protected int readFully(InputStream var1, byte[] var2) throws IOException { + for(int var3 = 0; var3 < var2.length; ++var3) { + int var4 = var1.read(); + if (var4 == -1) { + return var3; + } + + var2[var3] = (byte)var4; + } + + return var2.length; + } + + public void encode(InputStream var1, OutputStream var2) throws IOException { + byte[] var5 = new byte[this.bytesPerLine()]; + this.encodeBufferPrefix(var2); + + while(true) { + int var4 = this.readFully(var1, var5); + if (var4 == 0) { + break; + } + + this.encodeLinePrefix(var2, var4); + + for(int var3 = 0; var3 < var4; var3 += this.bytesPerAtom()) { + if (var3 + this.bytesPerAtom() <= var4) { + this.encodeAtom(var2, var5, var3, this.bytesPerAtom()); + } else { + this.encodeAtom(var2, var5, var3, var4 - var3); + } + } + + if (var4 < this.bytesPerLine()) { + break; + } + + this.encodeLineSuffix(var2); + } + + this.encodeBufferSuffix(var2); + } + + public void encode(byte[] var1, OutputStream var2) throws IOException { + ByteArrayInputStream var3 = new ByteArrayInputStream(var1); + this.encode((InputStream)var3, var2); + } + + public String encode(byte[] var1) { + ByteArrayOutputStream var2 = new ByteArrayOutputStream(); + ByteArrayInputStream var3 = new ByteArrayInputStream(var1); + String var4 = null; + + try { + this.encode((InputStream)var3, var2); + var4 = var2.toString("8859_1"); + return var4; + } catch (Exception var6) { + throw new Error("CharacterEncoder.encode internal error"); + } + } + + private byte[] getBytes(ByteBuffer var1) { + byte[] var2 = null; + if (var1.hasArray()) { + byte[] var3 = var1.array(); + if (var3.length == var1.capacity() && var3.length == var1.remaining()) { + var2 = var3; + var1.position(var1.limit()); + } + } + + if (var2 == null) { + var2 = new byte[var1.remaining()]; + var1.get(var2); + } + + return var2; + } + + public void encode(ByteBuffer var1, OutputStream var2) throws IOException { + byte[] var3 = this.getBytes(var1); + this.encode(var3, var2); + } + + public String encode(ByteBuffer var1) { + byte[] var2 = this.getBytes(var1); + return this.encode(var2); + } + + public void encodeBuffer(InputStream var1, OutputStream var2) throws IOException { + byte[] var5 = new byte[this.bytesPerLine()]; + this.encodeBufferPrefix(var2); + + int var4; + do { + var4 = this.readFully(var1, var5); + if (var4 == 0) { + break; + } + + this.encodeLinePrefix(var2, var4); + + for(int var3 = 0; var3 < var4; var3 += this.bytesPerAtom()) { + if (var3 + this.bytesPerAtom() <= var4) { + this.encodeAtom(var2, var5, var3, this.bytesPerAtom()); + } else { + this.encodeAtom(var2, var5, var3, var4 - var3); + } + } + + this.encodeLineSuffix(var2); + } while(var4 >= this.bytesPerLine()); + + this.encodeBufferSuffix(var2); + } + + public void encodeBuffer(byte[] var1, OutputStream var2) throws IOException { + ByteArrayInputStream var3 = new ByteArrayInputStream(var1); + this.encodeBuffer((InputStream)var3, var2); + } + + public String encodeBuffer(byte[] var1) { + ByteArrayOutputStream var2 = new ByteArrayOutputStream(); + ByteArrayInputStream var3 = new ByteArrayInputStream(var1); + + try { + this.encodeBuffer((InputStream)var3, var2); + } catch (Exception var5) { + throw new Error("CharacterEncoder.encodeBuffer internal error"); + } + + return var2.toString(); + } + + public void encodeBuffer(ByteBuffer var1, OutputStream var2) throws IOException { + byte[] var3 = this.getBytes(var1); + this.encodeBuffer(var3, var2); + } + + public String encodeBuffer(ByteBuffer var1) { + byte[] var2 = this.getBytes(var1); + return this.encodeBuffer(var2); + } +} diff --git a/app/src/main/java/com/monke/monkeybook/view/impl/MainActivity.java b/app/src/main/java/com/monke/monkeybook/view/impl/MainActivity.java index 423056c..954ed41 100644 --- a/app/src/main/java/com/monke/monkeybook/view/impl/MainActivity.java +++ b/app/src/main/java/com/monke/monkeybook/view/impl/MainActivity.java @@ -30,6 +30,7 @@ import java.util.List; public class MainActivity extends MBaseActivity implements IMainView { private ImageView ivLogo; private ImageButton ibMoney; + private ImageButton ibSettings; private ImageButton ibLibrary; private ImageButton ibAdd; private ImageButton ibDownload; @@ -72,6 +73,7 @@ public class MainActivity extends MBaseActivity implements IMain rfRvShelf = (RefreshRecyclerView) findViewById(R.id.rf_rv_shelf); ibMoney = (ImageButton) findViewById(R.id.ib_money); + ibSettings = findViewById(R.id.ib_settings); ibLibrary = (ImageButton) findViewById(R.id.ib_library); ibAdd = (ImageButton) findViewById(R.id.ib_add); ibDownload = (ImageButton) findViewById(R.id.ib_download); @@ -85,10 +87,10 @@ public class MainActivity extends MBaseActivity implements IMain @Override protected void bindEvent() { bindRvShelfEvent(); - ivLogo.setOnClickListener(new View.OnClickListener() { + ibSettings.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - proxyPop.showAsDropDown(ivLogo); + proxyPop.showAsDropDown(ibSettings); } }); ibDownload.setOnClickListener(new View.OnClickListener() { diff --git a/app/src/main/res/drawable-xxhdpi/icon_setting_main_nor.png b/app/src/main/res/drawable-xxhdpi/icon_setting_main_nor.png new file mode 100644 index 0000000..547b2c0 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_setting_main_nor.png differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_setting_main_pre.png b/app/src/main/res/drawable-xxhdpi/icon_setting_main_pre.png new file mode 100644 index 0000000..5763b19 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_setting_main_pre.png differ diff --git a/app/src/main/res/drawable/selector_icon_setting_main.xml b/app/src/main/res/drawable/selector_icon_setting_main.xml new file mode 100644 index 0000000..841edca --- /dev/null +++ b/app/src/main/res/drawable/selector_icon_setting_main.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 40253e9..e2ea99c 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -35,37 +35,57 @@ android:layout_width="40dp" android:layout_height="40dp" android:layout_gravity="center_vertical" + android:paddingTop="10dp" + android:paddingBottom="10dp" android:layout_marginRight="5dp" android:background="@drawable/bg_ib_pre" android:src="@drawable/selector_iv_money" + android:scaleType="fitCenter" android:visibility="gone" /> - - + android:scaleType="fitCenter" + android:src="@drawable/selector_iv_library"/> + +