原文出处:IT男杂记事
苦逼的折腾了快一星期,总算把新浪微博rsa加密登录折腾ok了,这里需要注意的是httpclient最好用4.0的,否则cookie管理很是问题。
进入正题,最近新浪微博更新了sso登录方式,加密算法变成了rsa,获取nonce和servertime,pubkey,这里涉及到rsa加密,通常用java进行rsa加密一般都是从文件读取公钥信息或者是base64编码的公钥信息转换成key,然后进行加密,但是新浪给的不是base64加密,而是给的一个N(参见RSA加密算法,RSA加密算法),而我先入为主,把新浪给的pubkey当作base64编码的公钥信息,苦逼的折腾了快一天。后来再细看看RSA加密算法感觉pubkey不像是base64编码,看到网上有人分析新浪的sso.js,于是我也看一下,终于有收获,看到他把pubkey解析成biginteger,感觉怪怪的,联想到rsa的百科,发现应该是我搞错了,遂找到以下方法。用pubkey进行加密。
public String rsaCrypt(String modeHex, String exponentHex, String messageg) throws IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, UnsupportedEncodingException { KeyFactory factory = KeyFactory.getInstance("RSA"); BigInteger m = new BigInteger(modeHex, 16); /* public exponent */ BigInteger e = new BigInteger(exponentHex, 16); /* modulus */ RSAPublicKeySpec spec = new RSAPublicKeySpec(m, e); RSAPublicKey pub = (RSAPublicKey) factory.generatePublic(spec); Cipher enc = Cipher.getInstance("RSA"); enc.init(Cipher.ENCRYPT_MODE, pub); byte[] encryptedContentKey = enc.doFinal(messageg.getBytes("GB2312")); return new String(Hex.encodeHex(encryptedContentKey)); }
方法名气得不是很理想,但是不影响我们学习。搞定加密了,接下来更苦逼的是http-client3这托,我看网上有人弄出来的代码,参考着弄下,但是他们的验证方法,参数都有问题,我参照有人用ruby写的(sso 1.4.2版本的),将该替换的都替换了,一直进行到ajaxlogin这里,可以获取到我的个人信息,但是苦逼的是,真正登录的时候不行,我怀疑是cookie问题,因为一直警告我cookie reject,因为新浪sso登录时候用了些技巧解决跨域。好,那我就山寨点,把cookie domain该了,改成.weibo.com最终获取用户主页时retcode=6102,查了下sso.js代码,发现是没登录成功 ,cookie问题。什么办法读想了,抓包,分析,firebug分析,google了无数遍,最后还是没成功。无奈继续google,苦心不负有心人,最终发现这篇,http-client4来做的,我隐约记得官方推荐用hc4,于是就搞了下,竟然可以获得首页一些信息,虽然不是html的,但应该是成功了,之前没成功的话获取个人主页会得到登录的html。下面是主要代码。
import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import java.util.ArrayList; import java.util.Date; import java.util.List; import javax.crypto.BadPaddingException; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import org.apache.commons.codec.binary.Base64; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.NameValuePair; import org.apache.http.HttpException; import org.apache.http.HttpResponse; import org.apache.http.ParseException; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.params.CookiePolicy; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.params.HttpConnectionParams; import org.apache.http.protocol.HTTP; import org.apache.http.util.EntityUtils; import org.json.JSONException; import org.json.JSONObject; public class Main { static String SINA_PK = "EB2A38568661887FA180BDDB5CABD5F21C7BFD59C090CB2D24" + "5A87AC253062882729293E5506350508E7F9AA3BB77F4333231490F915F6D63C55FE2F08A49B353F444AD39" + "93CACC02DB784ABBB8E42A9B1BBFFFB38BE18D78E87A0E41B9B8F73A928EE0CCEE" + "1F6739884B9777E4FE9E88A1BBE495927AC4A799B3181D6442443"; static String username = "用户名"; static String passwd = "密码"; private static final Log logger = LogFactory.getLog(Main.class); public static void main(String[] args) throws HttpException, IOException, JSONException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException { DefaultHttpClient client = new DefaultHttpClient(); client.getParams().setParameter("http.protocol.cookie-policy", CookiePolicy.BROWSER_COMPATIBILITY); client.getParams().setParameter( HttpConnectionParams.CONNECTION_TIMEOUT, 5000); HttpPost post = new HttpPost( "http://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.2)"); PreLoginInfo info = getPreLoginBean(client); long servertime = info.servertime; String nonce = info.nonce; String pwdString = servertime + "\t" + nonce + "\n" + "PASSWORD"; String sp = new BigIntegerRSA().rsaCrypt(SINA_PK, "10001", pwdString); List<NameValuePair> nvps = new ArrayList<NameValuePair>(); nvps.add(new BasicNameValuePair("entry", "weibo")); nvps.add(new BasicNameValuePair("gateway", "1")); nvps.add(new BasicNameValuePair("from", "")); nvps.add(new BasicNameValuePair("savestate", "7")); nvps.add(new BasicNameValuePair("useticket", "1")); nvps.add(new BasicNameValuePair("ssosimplelogin", "1")); nvps.add(new BasicNameValuePair("vsnf", "1")); // new NameValuePair("vsnval", ""), nvps.add(new BasicNameValuePair("su", encodeUserName(username))); nvps.add(new BasicNameValuePair("service", "miniblog")); nvps.add(new BasicNameValuePair("servertime", servertime + "")); nvps.add(new BasicNameValuePair("nonce", nonce)); nvps.add(new BasicNameValuePair("pwencode", "rsa2")); nvps.add(new BasicNameValuePair("rsakv", info.rsakv)); nvps.add(new BasicNameValuePair("sp", sp)); nvps.add(new BasicNameValuePair("encoding", "UTF-8")); nvps.add(new BasicNameValuePair("prelt", "115")); nvps.add(new BasicNameValuePair("returntype", "META")); nvps.add(new BasicNameValuePair( "url", "http://weibo.com/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack")); post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8)); HttpResponse response = client.execute(post); String entity = EntityUtils.toString(response.getEntity()); String url = entity.substring(entity .indexOf("http://weibo.com/ajaxlogin.php?"), entity .indexOf("code=0") + 6); logger.debug("url:" + url); // 获取到实际url进行连接 HttpGet getMethod = new HttpGet(url); response = client.execute(getMethod); entity = EntityUtils.toString(response.getEntity()); entity = entity.substring(entity.indexOf("userdomain") + 13, entity .lastIndexOf("\"")); logger.debug(entity); getMethod = new HttpGet("http://weibo.com/humingchun?wvr=5&lf=reg"); response = client.execute(getMethod); entity = EntityUtils.toString(response.getEntity()); // Document doc = // Jsoup.parse(EntityUtils.toString(response.getEntity())); System.out.println(entity); logger.debug(entity); } private static PreLoginInfo getPreLoginBean(HttpClient client) throws HttpException, IOException, JSONException { String serverTime = getPreLoginInfo(client); System.out.println(""); JSONObject jsonInfo = new JSONObject(serverTime); PreLoginInfo info = new PreLoginInfo(); info.nonce = jsonInfo.getString("nonce"); info.pcid = jsonInfo.getString("pcid"); info.pubkey = jsonInfo.getString("pubkey"); info.retcode = jsonInfo.getInt("retcode"); info.rsakv = jsonInfo.getString("rsakv"); info.servertime = jsonInfo.getLong("servertime"); return info; } public static String getPreLoginInfo(HttpClient client) throws ParseException, IOException { String preloginurl = "http://login.sina.com.cn/sso/prelogin.php?entry=sso&" + "callback=sinaSSOController.preloginCallBack&su=" + "dW5kZWZpbmVk" + "&rsakt=mod&client=ssologin.js(v1.4.2)" + "&_=" + getCurrentTime(); HttpGet get = new HttpGet(preloginurl); HttpResponse response = client.execute(get); String getResp = EntityUtils.toString(response.getEntity()); int firstLeftBracket = getResp.indexOf("("); int lastRightBracket = getResp.lastIndexOf(")"); String jsonBody = getResp.substring(firstLeftBracket + 1, lastRightBracket); System.out.println(jsonBody); return jsonBody; } private static String getCurrentTime() { long servertime = new Date().getTime() / 1000; return String.valueOf(servertime); } private static String encodeUserName(String email) { email = email.replaceFirst("@", "%40");// MzM3MjQwNTUyJTQwcXEuY29t email = Base64.encodeBase64String(email.getBytes()); return email; } }
完整源码到这里下载 http://marspring.mobi/http-client-weibo/ 文章中给出了下载链接了