/* * 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. */ /** * Project : MiniQQ2 * Package : net.solosky.miniqq * File : Encryptor.java * Author : solosky < solosky772@qq.com > * Created : 2010-4-10 * License : Apache License 2.0 */ package iqq.im.util; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import javax.script.Invocable; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; /** * * * @author solosky <solosky772@qq.com> */ public class QQEncryptor { /** * 获取好友列表时计算的HASH参数 基本上照抄eqq.all.js代码即可 * * @param uin * 当前登录用户UIN * @param ptwebqq * Cookie中的ptwebqq的值 * @return */ public static String hash(String uin, String ptwebqq) { String a = uin; String e = ptwebqq; byte[] c = new byte[a.length()]; for (int i = 0; i < a.length(); i++) { c[i] = (byte) (a.charAt(i) - '0'); } int k = -1; for (int b = 0, d = 0; d < c.length; d++) { b += c[d]; b %= e.length(); int f = 0; if (b + 4 > e.length()) { for (int g = 4 + b - e.length(), h = 0; h < 4; h++) { if (h < g) { f |= (e.charAt(b + h) & 255) << (3 - h) * 8; } else { f |= (e.charAt(h - g) & 255) << (3 - h) * 8; } } } else { for (int h = 0; h < 4; h++) { f |= (e.charAt(b + h) & 255) << (3 - h) * 8; } } k ^= f; } return Integer.toHexString(k).toUpperCase(); } /** * 获取好友列表时计算的HASH参数 v2012.04.19更新 * * @param uin * 当前登录用户UIN * @param ptwebqq * Cookie中的ptwebqq的值 * @return */ public static String hash2(String uin, String ptwebqq) { String i = ptwebqq; String b = uin; String a = i + "password error"; StringBuffer s = new StringBuffer(); while (s.length() < a.length()) { s.append(b); } String ss = s.substring(0, a.length()); byte[] j = new byte[a.length()]; for (int d = 0; d < a.length(); d++) { j[d] = (byte) (ss.charAt(d) ^ a.charAt(d)); } return byte2HexString(j); } /** * 获取好友列表时计算的HASH参数 v2012.07.20更新 感谢IQQ群里面的@枫叶(953884102)提供 * * @param uin * 当前登录用户UIN * @param ptwebqq * Cookie中的ptwebqq的值 * @return */ public static String hash3(String uin, String ptwebqq) { String i = ptwebqq; String b = uin; int[] a = new int[i.length()]; char[] iString = i.toCharArray(); for (int s = 0; s < i.length(); s++) { a[s % 4] ^= (int) iString[s]; } String[] j = { "EC", "OK" }; int[] d = new int[4]; d[0] = Integer.parseInt(b) >> 24 & 255 ^ (int) j[0].toCharArray()[0]; d[1] = Integer.parseInt(b) >> 16 & 255 ^ (int) j[0].toCharArray()[1]; d[2] = Integer.parseInt(b) >> 8 & 255 ^ (int) j[1].toCharArray()[0]; d[3] = Integer.parseInt(b) & 255 ^ (int) j[1].toCharArray()[1]; int[] k = new int[8]; for (int s = 0; s < 8; s++) k[s] = s % 2 == 0 ? a[s >> 1] : d[s >> 1]; String[] aa = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" }; String dd = ""; for (int s = 0; s < k.length; s++) { dd += aa[k[s] >> 4 & 15]; dd += aa[k[s] & 15]; } return dd; } /** * 获取好友列表时计算的Hash参数 v2013.08.17更新 * * @param uin * 当前登录用户UIN * @param ptwebqq * Cookie中的ptwebqq的值 * @return hash */ public static String hashP(String uin, String ptwebqq) { String s = ""; try { String js = "function getHash(b,i){for(var a=[],s=0;s<i.length;s++)a[s%4]^=i.charCodeAt(s);var j=['EC','OK'],d=[];d[0]=b>>24&255^j[0].charCodeAt(0);d[1]=b>>16&255^j[0].charCodeAt(1);d[2]=b>>8&255^j[1].charCodeAt(0);d[3]=b&255^j[1].charCodeAt(1);j=[];for(s=0;s<8;s++)j[s]=s%2==0?a[s>>1]:d[s>>1];a=['0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'];d='';for(s=0;s<j.length;s++)d+=a[j[s]>>4&15],d+=a[j[s]&15];return d}"; ScriptEngineManager mgr = new ScriptEngineManager(); ScriptEngine engine = mgr .getEngineByMimeType("application/javascript"); engine.eval(js); Invocable inv = (Invocable) engine; s = (String) inv.invokeFunction("getHash", uin, ptwebqq); } catch (Exception e) { e.printStackTrace(); } return s; } /*** * 计算登录时密码HASH值 * * @param uin * @param plain * @param verify * @return */ public static String encrypt(long uin, String plain, String verify) { byte[] data = concat(md5(plain.getBytes()), long2bytes(uin)); String code = byte2HexString(md5(data)); data = md5((code + verify.toUpperCase()).getBytes()); return byte2HexString(data); } private static byte[] concat(byte[] bytes1, byte[] bytes2) { byte[] big = new byte[bytes1.length + bytes2.length]; System.arraycopy(bytes1, 0, big, 0, bytes1.length); System.arraycopy(bytes2, 0, big, bytes1.length, bytes2.length); return big; } /** * 计算一个字节数组的Md5值 * * @param bytes * @return */ private static byte[] md5(byte[] bytes) { MessageDigest dist = null; byte[] result = null; try { dist = MessageDigest.getInstance("MD5"); result = dist.digest(bytes); } catch (NoSuchAlgorithmException e) { throw new IllegalArgumentException(e); } return result; } /** * 把字节数组转换为16进制表示的字符串 * * @param b * @return */ private static String byte2HexString(byte[] b) { StringBuffer sb = new StringBuffer(); char[] hex = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; if (b == null) return "null"; int offset = 0; int len = b.length; // 检查索引范围 int end = offset + len; if (end > b.length) end = b.length; sb.delete(0, sb.length()); for (int i = offset; i < end; i++) { sb.append(hex[(b[i] & 0xF0) >>> 4]).append(hex[b[i] & 0xF]); } return sb.toString(); } /** * 计算GTK(gtk啥东东?) * * @param skey * @return */ public static String gtk(String skey) { int hash = 5381; for (int i = 0; i < skey.length(); i++) { hash += (hash << 5) + skey.charAt(i); } return Integer.toString(hash & 0x7fffffff); } /** * 把整形数转换为字节数组 * * @param i * @return */ public static byte[] long2bytes(long i) { byte[] b = new byte[8]; for (int m = 0; m < 8; m++, i >>= 8) { b[7 - m] = (byte) (i & 0x000000FF); // 奇怪, 在C# 整型数是低字节在前 byte[] // bytes = // BitConverter.GetBytes(i); // 而在JAVA里,是高字节在前 } return b; } /** * 把一个16进制字符串转换为字节数组,字符串没有空格,所以每两个字符 一个字节 * * @param s * @return */ public static byte[] hexString2Byte(String s) { int len = s.length(); byte[] ret = new byte[len >>> 1]; for (int i = 0; i <= len - 2; i += 2) { ret[i >>> 1] = (byte) (Integer.parseInt(s.substring(i, i + 2) .trim(), 16) & 0xFF); } return ret; } }