求助,C# RSA加密密码,发送到服务器返回密码错误

EveKovi 发布于 2016/02/11 19:48
阅读 447
收藏 2

用C#写一个客户端登录应用,分析登录页面的代码,判断出登录逻辑如下:

1、从 "/login?act=getkey&_=" + (new Date).getTime() 获得公钥和hash值,返回结果像这样:

{
  "hash" : "52c17e7b60a06c5b",
  "key" : "-----BEGIN PUBLIC KEY-----\nMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANe7Rmtt4U3mjPwixdxLrw9Yczkht8VE\nxECb05iKTGrlXcc3vXuDla1Vjs7EY2xD4se+pAaICeSqS+Rq7yVZKkcCAwEAAQ==\n-----END PUBLIC KEY-----\n"
}

其中的hash值会和密码字符串连接,然后加密。

页面RSA加密采用的是jsencrypt  http://travistidwell.com/jsencrypt/ ,关键代码为:

var s = new JSEncrypt;
s.setPublicKey(i.key), // i为上述json
a = s.encrypt(i.hash + a), // a为密码

最后得到的a为加密的密码。

2、POST登录数据到 /ajax/miniLogin/login ,POST的数据格式为:

userid=用户名&pwd=加密的密码&captcha=验证码&keep=1

由Cookie中的sid值确定客户端。

返回的JSON数据里包含了登录成功或失败的信息:

ERROR_MAP = {
    "-105": "验证码错误",
    "-618": "昵称重复或含有非法字符",
    "-619": "昵称不能小于3个字符或者大于30个字符",
    "-620": "该昵称已被使用",
    "-622": "Email已存在",
    "-625": "密码错误次数过多",
    "-626": "用户不存在",
    "-627": "密码错误",
    "-628": "密码不能小于6个字符或大于16个字符",
    "-645": "昵称或密码过短",
    "-646": "请输入正确的手机号",
    "-647": "该手机已绑定另外一个账号",
    "-648": "验证码发送失败",
    "-652": "历史遗留问题,昵称与手机号重复,请联系管理员",
    "-662": "加密后的密码已过期"
}

附上具体的登录函数,供参考:

function init_login() {
    $("#login-submit").click(function() {
        var e = $(this),
            t = $("#login-username").val(),
            a = $("#login-passwd").val(),
            n = $("#login-captcha").val(),
            r = $("#keep-login").attr("checked") ? 1 : 0;
        if (!e.hasClass("disabled") && !e.hasClass("loading")) {
            if ($("#login .input").removeClass("ok error"), $("#login .message").text(""), "" == t) return $("#login .uname-row .input").error(), void $("#login .message[for=username]").text("手机号/邮箱不能为空");
            if ("" == a) return $("#login .passwd-row .input").error(), void $("#login .message[for=passwd]").text("请输入密码");
            e.addClass("loading"), $.getJSON("/login?act=getkey&_=" + (new Date).getTime(), function(i) {
                if (i && i.error) $("#login .message[for=passwd]").text("服务端出现异常,请稍后重试");
                else {
                    var s = new JSEncrypt;
                    s.setPublicKey(i.key), a = s.encrypt(i.hash + a), $.post("/ajax/miniLogin/login", {
                        userid: t,
                        pwd: a,
                        captcha: n,
                        keep: r
                    }, function(t) {
                        if (!t.status) {
                            var a = t.message.code;
                            if (-105 == a) {
                                var n = $("#login .captcha-row");
                                n.is(":visible") ? n.children(".input").addClass("error").next().text(ERROR_MAP[a]) : n.slideDown(), refresh_captcha(n.find(".captcha-img"))
                            } else -626 == a || -652 == a ? $("#login .uname-row").children(".input").addClass("error").next().text(ERROR_MAP[a]) : $("#login .passwd-row").children(".input").addClass("error").next().text(ERROR_MAP[a]);
                            return void e.removeClass("loading")
                        }
                        window.location.href = t.data.crossDomain
                    }, "json")
                }
            })
        }
    }), $("#login").on("keyup", "input", function(e) {
        $(this).closest(".input").removeClass("error ok").next().text(""), 13 == e.keyCode && $("#login-submit").click()
    })
}

对密码学不是很懂,C#采用的加密函数为:

public static string PemToXml(string pem)
{
    if (pem.StartsWith("-----BEGIN PUBLIC KEY-----"))
    {
        //pem = pem.Replace("-----BEGIN PUBLIC KEY-----", "").Replace("-----END PUBLIC KEY-----", "");
        return GetXmlRsaKey(pem, obj =>
        {
            var publicKey = (RsaKeyParameters)obj;
            return DotNetUtilities.ToRSA(publicKey);
        }, rsa => rsa.ToXmlString(false));
    }


    throw new InvalidKeyException("Unsupported PEM format...");
}


private static string GetXmlRsaKey(string pem, Func<object, RSA> getRsa, Func<RSA, string> getKey)
{
    using (var ms = new MemoryStream())
    using (var sw = new StreamWriter(ms))
    using (var sr = new StreamReader(ms))
    {
        sw.Write(pem);
        sw.Flush();
        ms.Position = 0;
        var pr = new Org.BouncyCastle.OpenSsl.PemReader(sr);
        object keyPair = pr.ReadObject();
        using (RSA rsa = getRsa(keyPair))
        {
            var xml = getKey(rsa);
            return xml;
        }
    }
}

/// <summary>
/// RSA加密
/// </summary>
/// <param name="publickey"></param>
/// <param name="content"></param>
/// <returns></returns>
public static string RSAEncrypt(string publickey, string content)
{
    RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
    byte[] cipherbytes;
    rsa.FromXmlString(publickey);
    cipherbytes = rsa.Encrypt(Encoding.UTF8.GetBytes(content), false);

    return Convert.ToBase64String(cipherbytes);
}
大概逻辑为把从服务器上获取的公钥用 Org.BouncyCastle.OpenSsl.PemReader 和 DotNetUtilities.ToRSA 转换成 RSACryptoServiceProvider 可用的 xml 格式公钥,然后对 (hash+密码 ) 字符串进行加密,再POST到服务器,可是这样有时候可以成功登录,大部分时候返回 
"-627": "密码错误"
因为对RSA加密算法不太了解,只能判断出加密的密码在服务器解密出来的结果不对,希望能得到了解这方面的大神指点,C#端应该怎么修改,非常感谢。




加载中
0
永恒刹那
永恒刹那

建议,,,使用Microsoft.JScript.Eval.JScriptEvaluate,,


public object Eval(string expression)
{
try
{
var ve = Microsoft.JScript.Vsa.VsaEngine.CreateEngine();
ve.SetOption("fast", false);
return Microsoft.JScript.Eval.JScriptEvaluate(expression, ve).ToString();
}
catch (Exception) { return ""; }
}


Eval("function a(){ return 1;} return a(1);");

EveKovi
EveKovi
谢谢~本来想着没办法了就运行js吧,后来想干脆用WebBrower,弹出浏览器窗口登录完成拿cookie,看起来不涉及密码让人觉得更安全些,还是谢谢你
返回顶部
顶部