本文主要對消息摘要算法和加密算法作了整理,包括MD五、SHA、DES、AES、RSA等,而且提供了相應算法的Java實現和測試。java
消息摘要算法最經常使用的場景就是數字簽名以及數據(密碼)加密了。(通常平時作項目用的比較多的就是使用MD5對用戶密碼進行加密)git
數字簽名主要用到了非對稱密鑰加密技術與數字摘要技術。數字簽名技術是將摘要信息用發送者的私鑰加密,與原文一塊兒傳送給接收者。接收者只有用發送者的公鑰才能解密被加密的摘要信息,而後用HASH函數對收到的原文產生一個摘要信息,與解密的摘要信息對比。 若是相同,則說明收到的信息是完整的,在傳輸過程當中沒有被修改,不然說明信息被修改過.算法
所以數字簽名可以驗證信息的完整性。 數字簽名是個加密的過程,數字簽名驗證是個解密的過程。apache
MD5:數組
簡介:安全
MD5的做用是讓大容量信息在用數字簽名軟件簽署私人密鑰前被」壓縮」成一種保密的格式 (也就是把一個任意長度的字節串變換成必定長的十六進制數字串)。網絡
特色:app
代碼實現:dom
利用JDK提供java.security.MessageDigest類實現MD5算法:ide
package com.snailclimb.ks.securityAlgorithm; import java.security.MessageDigest; public class MD5Demo { // test public static void main(String[] args) { System.out.println(getMD5Code("你若安好,即是晴天")); } private MD5Demo() { } // md5加密 public static String getMD5Code(String message) { String md5Str = ""; try { //建立MD5算法消息摘要 MessageDigest md = MessageDigest.getInstance("MD5"); //生成的哈希值的字節數組 byte[] md5Bytes = md.digest(message.getBytes()); md5Str = bytes2Hex(md5Bytes); }catch(Exception e) { e.printStackTrace(); } return md5Str; } // 2進制轉16進制 public static String bytes2Hex(byte[] bytes) { StringBuffer result = new StringBuffer(); int temp; try { for (int i = 0; i < bytes.length; i++) { temp = bytes[i]; if(temp < 0) { temp += 256; } if (temp < 16) { result.append("0"); } result.append(Integer.toHexString(temp)); } } catch (Exception e) { e.printStackTrace(); } return result.toString(); } }
結果:
6bab82679914f7cb480a120b532ffa80
注意MessageDigest類的幾個方法:
static MessageDigest getInstance(String algorithm)//返回實現指定摘要算法的MessageDigest對象
byte[] digest(byte[] input)//使用指定的字節數組對摘要執行最終更新,而後完成摘要計算。
不利用Java提供的java.security.MessageDigest類實現MD5算法:
package com.snailclimb.ks.securityAlgorithm; public class MD5{ /* *四個連接變量 */ private final int A=0x67452301; private final int B=0xefcdab89; private final int C=0x98badcfe; private final int D=0x10325476; /* *ABCD的臨時變量 */ private int Atemp,Btemp,Ctemp,Dtemp; /* *常量ti *公式:floor(abs(sin(i+1))×(2pow32) */ private final int K[]={ 0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee, 0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,0x698098d8, 0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193, 0xa679438e,0x49b40821,0xf61e2562,0xc040b340,0x265e5a51, 0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8, 0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905, 0xfcefa3f8,0x676f02d9,0x8d2a4c8a,0xfffa3942,0x8771f681, 0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60, 0xbebfbc70,0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05, 0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,0xf4292244, 0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92, 0xffeff47d,0x85845dd1,0x6fa87e4f,0xfe2ce6e0,0xa3014314, 0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391}; /* *向左位移數,計算方法未知 */ private final int s[]={7,12,17,22,7,12,17,22,7,12,17,22,7, 12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20, 4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10, 15,21,6,10,15,21,6,10,15,21,6,10,15,21}; /* *初始化函數 */ private void init(){ Atemp=A; Btemp=B; Ctemp=C; Dtemp=D; } /* *移動必定位數 */ private int shift(int a,int s){ return(a<<s)|(a>>>(32-s));//右移的時候,高位必定要補零,而不是補充符號位 } /* *主循環 */ private void MainLoop(int M[]){ int F,g; int a=Atemp; int b=Btemp; int c=Ctemp; int d=Dtemp; for(int i = 0; i < 64; i ++){ if(i<16){ F=(b&c)|((~b)&d); g=i; }else if(i<32){ F=(d&b)|((~d)&c); g=(5*i+1)%16; }else if(i<48){ F=b^c^d; g=(3*i+5)%16; }else{ F=c^(b|(~d)); g=(7*i)%16; } int tmp=d; d=c; c=b; b=b+shift(a+F+K[i]+M[g],s[i]); a=tmp; } Atemp=a+Atemp; Btemp=b+Btemp; Ctemp=c+Ctemp; Dtemp=d+Dtemp; } /* *填充函數 *處理後應知足bits≡448(mod512),字節就是bytes≡56(mode64) *填充方式爲先加一個0,其它位補零 *最後加上64位的原來長度 */ private int[] add(String str){ int num=((str.length()+8)/64)+1;//以512位,64個字節爲一組 int strByte[]=new int[num*16];//64/4=16,因此有16個整數 for(int i=0;i<num*16;i++){//所有初始化0 strByte[i]=0; } int i; for(i=0;i<str.length();i++){ strByte[i>>2]|=str.charAt(i)<<((i%4)*8);//一個整數存儲四個字節,小端序 } strByte[i>>2]|=0x80<<((i%4)*8);//尾部添加1 /* *添加原長度,長度指位的長度,因此要乘8,而後是小端序,因此放在倒數第二個,這裏長度只用了32位 */ strByte[num*16-2]=str.length()*8; return strByte; } /* *調用函數 */ public String getMD5(String source){ init(); int strByte[]=add(source); for(int i=0;i<strByte.length/16;i++){ int num[]=new int[16]; for(int j=0;j<16;j++){ num[j]=strByte[i*16+j]; } MainLoop(num); } return changeHex(Atemp)+changeHex(Btemp)+changeHex(Ctemp)+changeHex(Dtemp); } /* *整數變成16進制字符串 */ private String changeHex(int a){ String str=""; for(int i=0;i<4;i++){ str+=String.format("%2s", Integer.toHexString(((a>>i*8)%(1<<8))&0xff)).replace(' ', '0'); } return str; } /* *單例 */ private static MD5 instance; public static MD5 getInstance(){ if(instance==null){ instance=new MD5(); } return instance; } private MD5(){}; public static void main(String[] args){ String str=MD5.getInstance().getMD5("你若安好,即是晴天"); System.out.println(str); } }
SHA1:
對於長度小於2^64位的消息,SHA1會產生一個160位(40個字符)的消息摘要。當接收到消息的時候,這個消息摘要能夠用來驗證數據的完整性。在傳輸的過程當中,數據極可能會發生變化,那麼這時候就會產生不一樣的消息摘要。
SHA1有以下特性:
代碼實現:
*利用JDK提供java.security.MessageDigest類實現SHA1算法:
package com.snailclimb.ks.securityAlgorithm; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class SHA1Demo { public static void main(String[] args) { // TODO Auto-generated method stub System.out.println(getSha1("你若安好,即是晴天")); } public static String getSha1(String str) { if (null == str || 0 == str.length()) { return null; } char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; try { //建立SHA1算法消息摘要對象 MessageDigest mdTemp = MessageDigest.getInstance("SHA1"); //使用指定的字節數組更新摘要。 mdTemp.update(str.getBytes("UTF-8")); //生成的哈希值的字節數組 byte[] md = mdTemp.digest(); //SHA1算法生成信息摘要關鍵過程 int j = md.length; char[] buf = new char[j * 2]; int k = 0; for (int i = 0; i < j; i++) { byte byte0 = md[i]; buf[k++] = hexDigits[byte0 >>> 4 & 0xf]; buf[k++] = hexDigits[byte0 & 0xf]; } return new String(buf); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return "0"; } }
結果:
8ce764110a42da9b08504b20e26b19c9e3382414
密鑰加密技術的密碼體制分爲對稱密鑰體制和非對稱密鑰體制兩種。相應地,對數據加密的技術分爲兩類,即對稱加密(私人密鑰加密)和非對稱加密(公開密鑰加密)。
對稱加密以數據加密標準(DES,Data Encryption Standard)算法爲典型表明,非對稱加密一般以RSA(Rivest Shamir Adleman)算法爲表明。
對稱加密的加密密鑰和解密密鑰相同。非對稱加密的加密密鑰和解密密鑰不一樣,加密密鑰能夠公開而解密密鑰須要保密
常被用在電子商務或者其餘須要保證網絡傳輸安全的範圍。
加密密鑰和解密密鑰相同的加密算法。
對稱加密算法使用起來簡單快捷,密鑰較短,且破譯困難,除了數據加密標準(DES), 另外一個對稱密鑰加密系統是國際數據加密算法(IDEA),它比DES的加密性好,並且對計算機功能要求也沒有那麼高。IDEA加密標準由PGP(Pretty Good Privacy)系統使用。
DES:
DES全稱爲Data Encryption Standard,即數據加密標準,是一種使用密鑰加密的塊算法,如今已通過時。
代碼實現:
DES算法實現 :
package com.snailclimb.ks.securityAlgorithm; import java.io.UnsupportedEncodingException; import java.security.SecureRandom; import javax.crypto.spec.DESKeySpec; import javax.crypto.SecretKeyFactory; import javax.crypto.SecretKey; import javax.crypto.Cipher; /** * DES加密介紹 DES是一種對稱加密算法,所謂對稱加密算法即:加密和解密使用相同密鑰的算法。DES加密算法出自IBM的研究, * 後來被美國政府正式採用,以後開始普遍流傳,可是近些年使用愈來愈少,由於DES使用56位密鑰,以現代計算能力, * 24小時內便可被破解。雖然如此,在某些簡單應用中,咱們仍是可使用DES加密算法,本文簡單講解DES的JAVA實現 。 * 注意:DES加密和解密過程當中,密鑰長度都必須是8的倍數 */ public class DesDemo { public DesDemo() { } // 測試 public static void main(String args[]) { // 待加密內容 String str = "cryptology"; // 密碼,長度要是8的倍數 String password = "95880288"; byte[] result; try { result = DesDemo.encrypt(str.getBytes(), password); System.out.println("加密後:" + result); byte[] decryResult = DesDemo.decrypt(result, password); System.out.println("解密後:" + new String(decryResult)); } catch (UnsupportedEncodingException e2) { // TODO Auto-generated catch block e2.printStackTrace(); } catch (Exception e1) { e1.printStackTrace(); } } // 直接將如上內容解密 /** * 加密 * * @param datasource * byte[] * @param password * String * @return byte[] */ public static byte[] encrypt(byte[] datasource, String password) { try { SecureRandom random = new SecureRandom(); DESKeySpec desKey = new DESKeySpec(password.getBytes()); // 建立一個密匙工廠,而後用它把DESKeySpec轉換成 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); SecretKey securekey = keyFactory.generateSecret(desKey); // Cipher對象實際完成加密操做 Cipher cipher = Cipher.getInstance("DES"); // 用密匙初始化Cipher對象,ENCRYPT_MODE用於將 Cipher 初始化爲加密模式的常量 cipher.init(Cipher.ENCRYPT_MODE, securekey, random); // 如今,獲取數據並加密 // 正式執行加密操做 return cipher.doFinal(datasource); // 按單部分操做加密或解密數據,或者結束一個多部分操做 } catch (Throwable e) { e.printStackTrace(); } return null; } /** * 解密 * * @param src * byte[] * @param password * String * @return byte[] * @throws Exception */ public static byte[] decrypt(byte[] src, String password) throws Exception { // DES算法要求有一個可信任的隨機數源 SecureRandom random = new SecureRandom(); // 建立一個DESKeySpec對象 DESKeySpec desKey = new DESKeySpec(password.getBytes()); // 建立一個密匙工廠 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");// 返回實現指定轉換的 // Cipher // 對象 // 將DESKeySpec對象轉換成SecretKey對象 SecretKey securekey = keyFactory.generateSecret(desKey); // Cipher對象實際完成解密操做 Cipher cipher = Cipher.getInstance("DES"); // 用密匙初始化Cipher對象 cipher.init(Cipher.DECRYPT_MODE, securekey, random); // 真正開始解密操做 return cipher.doFinal(src); } }
結果:
加密後:[B@50cbc42f 解密後:cryptology
IDEA:
代碼實現:
IDEA算法實現
package com.snailclimb.ks.securityAlgorithm; import java.security.Key; import java.security.Security; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Base64; import org.bouncycastle.jce.provider.BouncyCastleProvider; public class IDEADemo { public static void main(String args[]) { bcIDEA(); } public static void bcIDEA() { String src = "www.xttblog.com security idea"; try { Security.addProvider(new BouncyCastleProvider()); //生成key KeyGenerator keyGenerator = KeyGenerator.getInstance("IDEA"); keyGenerator.init(128); SecretKey secretKey = keyGenerator.generateKey(); byte[] keyBytes = secretKey.getEncoded(); //轉換密鑰 Key key = new SecretKeySpec(keyBytes, "IDEA"); //加密 Cipher cipher = Cipher.getInstance("IDEA/ECB/ISO10126Padding"); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] result = cipher.doFinal(src.getBytes()); System.out.println("bc idea encrypt : " + Base64.encodeBase64String(result)); //解密 cipher.init(Cipher.DECRYPT_MODE, key); result = cipher.doFinal(result); System.out.println("bc idea decrypt : " + new String(result)); } catch (Exception e) { e.printStackTrace(); } } }
RSA是目前最有影響力和最經常使用的公鑰加密算法。它可以抵抗到目前爲止已知的絕大多數密碼***,已被ISO推薦爲公鑰數據加密標準。
代碼實現:
RAS算法實現:
package com.snailclimb.ks.securityAlgorithm; import org.apache.commons.codec.binary.Base64; import java.security.*; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.HashMap; import java.util.Map; import javax.crypto.Cipher; /** * Created by humf.須要依賴 commons-codec 包 */ public class RSADemo { public static void main(String[] args) throws Exception { Map<String, Key> keyMap = initKey(); String publicKey = getPublicKey(keyMap); String privateKey = getPrivateKey(keyMap); System.out.println(keyMap); System.out.println("-----------------------------------"); System.out.println(publicKey); System.out.println("-----------------------------------"); System.out.println(privateKey); System.out.println("-----------------------------------"); byte[] encryptByPrivateKey = encryptByPrivateKey("123456".getBytes(), privateKey); byte[] encryptByPublicKey = encryptByPublicKey("123456", publicKey); System.out.println(encryptByPrivateKey); System.out.println("-----------------------------------"); System.out.println(encryptByPublicKey); System.out.println("-----------------------------------"); String sign = sign(encryptByPrivateKey, privateKey); System.out.println(sign); System.out.println("-----------------------------------"); boolean verify = verify(encryptByPrivateKey, publicKey, sign); System.out.println(verify); System.out.println("-----------------------------------"); byte[] decryptByPublicKey = decryptByPublicKey(encryptByPrivateKey, publicKey); byte[] decryptByPrivateKey = decryptByPrivateKey(encryptByPublicKey, privateKey); System.out.println(decryptByPublicKey); System.out.println("-----------------------------------"); System.out.println(decryptByPrivateKey); } public static final String KEY_ALGORITHM = "RSA"; public static final String SIGNATURE_ALGORITHM = "MD5withRSA"; private static final String PUBLIC_KEY = "RSAPublicKey"; private static final String PRIVATE_KEY = "RSAPrivateKey"; public static byte[] decryptBASE64(String key) { return Base64.decodeBase64(key); } public static String encryptBASE64(byte[] bytes) { return Base64.encodeBase64String(bytes); } /** * 用私鑰對信息生成數字簽名 * * @param data * 加密數據 * @param privateKey * 私鑰 * @return * @throws Exception */ public static String sign(byte[] data, String privateKey) throws Exception { // 解密由base64編碼的私鑰 byte[] keyBytes = decryptBASE64(privateKey); // 構造PKCS8EncodedKeySpec對象 PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); // KEY_ALGORITHM 指定的加密算法 KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); // 取私鑰匙對象 PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec); // 用私鑰對信息生成數字簽名 Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); signature.initSign(priKey); signature.update(data); return encryptBASE64(signature.sign()); } /** * 校驗數字簽名 * * @param data * 加密數據 * @param publicKey * 公鑰 * @param sign * 數字簽名 * @return 校驗成功返回true 失敗返回false * @throws Exception */ public static boolean verify(byte[] data, String publicKey, String sign) throws Exception { // 解密由base64編碼的公鑰 byte[] keyBytes = decryptBASE64(publicKey); // 構造X509EncodedKeySpec對象 X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); // KEY_ALGORITHM 指定的加密算法 KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); // 取公鑰匙對象 PublicKey pubKey = keyFactory.generatePublic(keySpec); Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); signature.initVerify(pubKey); signature.update(data); // 驗證簽名是否正常 return signature.verify(decryptBASE64(sign)); } public static byte[] decryptByPrivateKey(byte[] data, String key) throws Exception { // 對密鑰解密 byte[] keyBytes = decryptBASE64(key); // 取得私鑰 PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec); // 對數據解密 Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.DECRYPT_MODE, privateKey); return cipher.doFinal(data); } /** * 解密<br> * 用私鑰解密 * * @param data * @param key * @return * @throws Exception */ public static byte[] decryptByPrivateKey(String data, String key) throws Exception { return decryptByPrivateKey(decryptBASE64(data), key); } /** * 解密<br> * 用公鑰解密 * * @param data * @param key * @return * @throws Exception */ public static byte[] decryptByPublicKey(byte[] data, String key) throws Exception { // 對密鑰解密 byte[] keyBytes = decryptBASE64(key); // 取得公鑰 X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key publicKey = keyFactory.generatePublic(x509KeySpec); // 對數據解密 Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.DECRYPT_MODE, publicKey); return cipher.doFinal(data); } /** * 加密<br> * 用公鑰加密 * * @param data * @param key * @return * @throws Exception */ public static byte[] encryptByPublicKey(String data, String key) throws Exception { // 對公鑰解密 byte[] keyBytes = decryptBASE64(key); // 取得公鑰 X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key publicKey = keyFactory.generatePublic(x509KeySpec); // 對數據加密 Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, publicKey); return cipher.doFinal(data.getBytes()); } /** * 加密<br> * 用私鑰加密 * * @param data * @param key * @return * @throws Exception */ public static byte[] encryptByPrivateKey(byte[] data, String key) throws Exception { // 對密鑰解密 byte[] keyBytes = decryptBASE64(key); // 取得私鑰 PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec); // 對數據加密 Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, privateKey); return cipher.doFinal(data); } /** * 取得私鑰 * * @param keyMap * @return * @throws Exception */ public static String getPrivateKey(Map<String, Key> keyMap) throws Exception { Key key = (Key) keyMap.get(PRIVATE_KEY); return encryptBASE64(key.getEncoded()); } /** * 取得公鑰 * * @param keyMap * @return * @throws Exception */ public static String getPublicKey(Map<String, Key> keyMap) throws Exception { Key key = keyMap.get(PUBLIC_KEY); return encryptBASE64(key.getEncoded()); } /** * 初始化密鑰 * * @return * @throws Exception */ public static Map<String, Key> initKey() throws Exception { KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM); keyPairGen.initialize(1024); KeyPair keyPair = keyPairGen.generateKeyPair(); Map<String, Key> keyMap = new HashMap(2); keyMap.put(PUBLIC_KEY, keyPair.getPublic());// 公鑰 keyMap.put(PRIVATE_KEY, keyPair.getPrivate());// 私鑰 return keyMap; } }
結果:
{RSAPublicKey=Sun RSA public key, 1024 bits modulus: 115328826086047873902606456571034976538836553998745367981848911677968062571831626674499650854318207280419960767020601253071739555161388135589487284843845439403614883967713749605268831336418001722701924537624573180276356615050309809260289965219855862692230362893996010057188170525719351126759886050891484226169 public exponent: 65537, RSAPrivateKey=sun.security.rsa.RSAPrivateCrtKeyImpl@93479} ----------------------------------- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCkO9PBTOFJQTkzznALN62PU7ixd9YFjXrt2dPOGj3wwhymbOU8HLoCztjwpLXHgbpBUJlGmbURV955M1BkZ1kr5dkZYR5x1gO4xOnu8rEipy4AAMcpFttfiarIZrtzL9pKEvEOxABltVN4yzFDr3IjBqY46aHna7YjwhXI0xHieQIDAQAB ----------------------------------- MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKQ708FM4UlBOTPOcAs3rY9TuLF31gWNeu3Z084aPfDCHKZs5TwcugLO2PCktceBukFQmUaZtRFX3nkzUGRnWSvl2RlhHnHWA7jE6e7ysSKnLgAAxykW21+Jqshmu3Mv2koS8Q7EAGW1U3jLMUOvciMGpjjpoedrtiPCFcjTEeJ5AgMBAAECgYAK4sxOa8IjEOexv2U92Rrv/SSo3sCY7Z/QVDft2V9xrewoO9+V9HF/7iYDDWffKYInAiimvVl7JM/iSLxza0ZFv29VMpyDcr4TigYmWwBlk7ZbxSTkqLdNwxxldMmEoTn1py53MUm+1V1K3rzNvJjuZaZFAevU7vUnwQwD+JGQYQJBAM9HBaC+dF3PJ2mkXekHpDS1ZPaSFdrdzd/GvHFi/cJAMM+Uz6PmpkosNXRtOpSYWwlOMRamLZtrHhfQoqSk3S8CQQDK1qL1jGvVdqw5OjqxktR7MmOsWUVZdWiBN+6ojxBgA0yVn0n7vkdAAgEZBj89WG0VHPEu3hd4AgXFZHDfXeDXAkBvSn7nE9t/Et7ihfI2UHgGJO8UxNMfNMB5Skebyb7eMYEDs67ZHdpjMOFypcMyTatzj5wjwQ3zyMvblZX+ONbZAkAX4ysRy9WvL+icXLUo0Gfhkk+WrnSyUldaUGH0y9Rb2kecn0OxN/lgGlxSvB+ac910zRHCOTl+Uo6nbmq0g3PFAkAyqA4eT7G9GXfncakgW1Kdkn72w/ODpozgfhTLNX0SGw1ITML3c4THTtH5h3zLi3AF9zJO2O+K6ajRbV0szHHI ----------------------------------- [B@387c703b ----------------------------------- [B@224aed64 ----------------------------------- la4Hc4n/UbeBu0z9iLRuwKVv014SiOJMXkO5qdJvKBsw0MlnsrM+89a3p73yMrb1dAnCU/2kgO0PtFpvmG8pzxTe1u/5nX/25iIyUXALlwVRptJyjzFE83g2IX0XEv/Dxqr1RCRcrMHOLQM0oBoxZCaChmyw1Ub4wsSs6Ndxb9M= ----------------------------------- true ----------------------------------- [B@c39f790 ----------------------------------- [B@71e7a66b