简体   繁体   中英

How to port MD5 signature code from Java to C#

I'm trying to port the following Java code to C#, but so far it still says that the signature is invalid.

private static String generateSignStr(Map<String, String> params, String key) {
    StringBuilder sb = new StringBuilder();
    params.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(entry -> {
        if (sb.length() > 0) {
            sb.append('&');
        }
        sb.append(entry.getKey()).append('=');
        sb.append(entry.getValue());
    });
    sb.append('&').append("api_secret")
            .append('=').append(key);
    return sb.toString();
}

private static String sign(String target) {
    MessageDigest md;
    try {
        md = MessageDigest.getInstance("MD5");
    } catch (NoSuchAlgorithmException e) {
        log.error("Fail to get MD5 instance");
        return null;
    }
    md.update(target.getBytes());
    byte[] dg = md.digest();
    StringBuilder output = new StringBuilder(dg.length * 2);
    for (byte dgByte : dg) {
        int current = dgByte & 0xff;
        if (current < 16) {
            output.append("0");
        }
        output.append(Integer.toString(current, 16));
    }
    return output.toString();
}
private static string GenerateSign(Dictionary<string, object> query, string apiSecret)
{
    var sb = new StringBuilder();

    var queryParameterString = string.Join("&",
        query.Where(kvp => !string.IsNullOrWhiteSpace(kvp.Value.ToString()))
            .Select(kvp => $"{kvp.Key}={HttpUtility.UrlEncode(kvp.Value.ToString())}"));
    sb.Append(queryParameterString);

    if (sb.Length > 0)
    {
        sb.Append('&');
    }

    sb.Append("api_secret=").Append(apiSecret);

    return sb.ToString();
}

private static string Sign(string source)
{
    using var md5 = MD5.Create();
    var sourceBytes = Encoding.UTF8.GetBytes(source);
    var hash = md5.ComputeHash(sourceBytes);
    return BitConverter.ToString(hash).Replace("-", string.Empty).ToLowerInvariant();
}

Edit:

This fixed it. However, it would be nice if someone knows a way to lexicographically sort the dictionary inside that method just like the Java code.

var @params = new Dictionary<string, object>
        {
            { "api_key", _apiKey },
            { "req_time", now },
            { "op", "sub.personal" }
        };

        var javaSorted = @params.OrderBy(item => item.Key, StringComparer.Ordinal)
            .ToDictionary(i => i.Key, i => i.Value);

var signature = Sign(GenerateSign(javaSorted, _apiSecret));

In GenerateSign method you can just create instance of SortedDictionary based on dictionary passed as parameter:

private static string GenerateSign(Dictionary<string, object> query, string apiSecret)
{
    var sortedDict = new SortedDictionary<string, object>(query, StringComparer.Ordinal);
    // rest of the method
}

Or you can do even better (note the important change from Dictionary to IDictionary ):

private static string GenerateSign(IDictionary<string, object> query, string apiSecret)
{
    query = new SortedDictionary<string, object>(query, StringComparer.Ordinal);
    // rest of the method
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM