简体   繁体   English

将Java加密例程移植到C#

[英]Porting Java encryption routine to C#

I'm attempting with little success to port over Google's code to generate a secure token for their captcha ( https://github.com/google/recaptcha-java/blob/master/appengine/src/main/java/com/google/recaptcha/STokenUtils.java ): 我试图通过移植Google代码来为其验证码生成安全令牌,但收效甚微( https://github.com/google/recaptcha-java/blob/master/appengine/src/main/java/com/google /recaptcha/STokenUtils.java ):

The original utility has the following: 原始实用程序具有以下内容:

private static final String CIPHER_INSTANCE_NAME = "AES/ECB/PKCS5Padding";

private static String encryptAes(String input, String siteSecret) {
    try {
      SecretKeySpec secretKey = getKey(siteSecret);
      Cipher cipher = Cipher.getInstance(CIPHER_INSTANCE_NAME);
      cipher.init(Cipher.ENCRYPT_MODE, secretKey);
      return BaseEncoding.base64Url().omitPadding().encode(cipher.doFinal(input.getBytes("UTF-8")));
    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }

private static SecretKeySpec getKey(String siteSecret){
    try {
      byte[] key = siteSecret.getBytes("UTF-8");
      key = Arrays.copyOf(MessageDigest.getInstance("SHA").digest(key), 16);
      return new SecretKeySpec(key, "AES");
    } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
      e.printStackTrace();
    }
    return null;
  }

public static void main(String [] args) throws Exception {
    //Hard coded the following to get a repeatable result
    String siteSecret = "12345678";
    String jsonToken = "{'session_id':'abf52ca5-9d87-4061-b109-334abb7e637a','ts_ms':1445705791480}";
    System.out.println(" json token: " + jsonToken);
    System.out.println(" siteSecret: " + siteSecret);
    System.out.println(" Encrypted stoken: " + encryptAes(jsonToken, siteSecret));

Given the values I hardcoded, I get Irez-rWkCEqnsiRLWfol0IXQu1JPs3qL_G_9HfUViMG9u4XhffHqAyju6SRvMhFS86czHX9s1tbzd6B15r1vmY6s5S8odXT-ZE9A-y1lHns" back as my encrypted token. 鉴于我硬编码的值,我得到了Irez-rWkCEqnsiRLWfol0IXQu1JPs3qL_G_9HfUViMG9u4XhffHqAyju6SRvMhFS86czHX9s1tbzd6B15r1vmY6s5S8odXT-ZE9A-y1lHns"作为我的加密令牌。

My Java and crypto skills are more than a little rusty, and there aren't always direct analogs in C#. 我的Java和加密技能不仅有点生疏,而且C#中并不总是直接的类比。 I attempted to merge encrypeAes() and getKey() with the following, which isn't correct: 我试图将encrypeAes()getKey()与以下内容合并,这是不正确的:

public static string EncryptText(string PlainText, string siteSecret)
{
    using (RijndaelManaged aes = new RijndaelManaged())
    {
        aes.Mode = CipherMode.ECB;
        aes.Padding = PaddingMode.PKCS7;
        var bytes = Encoding.UTF8.GetBytes(siteSecret);
        SHA1 sha1 = SHA1.Create();
        var shaKey = sha1.ComputeHash(bytes);

        byte[] targetArray = new byte[16];
        Array.Copy(shaKey, targetArray, 16);

        aes.Key = targetArray;

        ICryptoTransform encrypto = aes.CreateEncryptor();

        byte[] plainTextByte = ASCIIEncoding.UTF8.GetBytes(PlainText);
        byte[] CipherText = encrypto.TransformFinalBlock(plainTextByte, 0, plainTextByte.Length);
        return HttpServerUtility.UrlTokenEncode(CipherText); //Equivalent to java's BaseEncoding.base64Url()?
    }
}

The C# version produces the incorrect value of: Ye+fySvneVUZJXth67+Si/e8fBUV4Sxs7wEXVDEOJjBMHl1encvt65gGIj8CiFzBGp5uUgKYJZCuQ4rc964vZigjlrJ/430LgYcathLLd9U= C#版本产生的值不正确: Ye+fySvneVUZJXth67+Si/e8fBUV4Sxs7wEXVDEOJjBMHl1encvt65gGIj8CiFzBGp5uUgKYJZCuQ4rc964vZigjlrJ/430LgYcathLLd9U=

Your code almost works as expected. 您的代码几乎按预期工作。 It's just that you somehow mixed up the outputs of the Java version (and possibly the C# version). 只是你以某种方式混淆了Java版本的输出(可能还有C#版本)。

If I execute your Java code (JDK 7 & 8 with Guava 18.0), I get 如果我执行你的Java代码(使用Guava 18.0的JDK 7和8),我会得到

Ye-fySvneVUZJXth67-Si_e8fBUV4Sxs7wEXVDEOJjBMHl1encvt65gGIj8CiFzBGp5uUgKYJZCuQ4rc964vZigjlrJ_430LgYcathLLd9U

and if I execute your C# code ( DEMO ), I get 如果我执行你的C#代码( DEMO ),我明白了

Ye-fySvneVUZJXth67-Si_e8fBUV4Sxs7wEXVDEOJjBMHl1encvt65gGIj8CiFzBGp5uUgKYJZCuQ4rc964vZigjlrJ_430LgYcathLLd9U1

So, the C# version has an additional "1" at the end. 因此,C#版本最后还有一个“1”。 It should be a padding character, but isn't. 它应该是填充字符,但不是。 This means that HttpServerUtility.UrlTokenEncode() doesn't provide a standards conform URL-safe Base64 encoding and you shouldn't use it. 这意味着HttpServerUtility.UrlTokenEncode()不提供符合标准的URL安全Base64编码,您不应该使用它。 See also this Q&A . 见此问答

The URL-safe Base64 encoding can be easily derived from the normal Base64 encoding (compare tables 1 and 2 in RFC4648) as seen in this answer by Marc Gravell: URL安全的Base64编码可以很容易地从普通的Base64编码中导出(比较RFC4648中的表1和2),如Marc Gravell的回答所示

 string returnValue = System.Convert.ToBase64String(toEncodeAsBytes) .TrimEnd(padding).Replace('+', '-').Replace('/', '_'); 

with: 有:

 static readonly char[] padding = { '=' }; 

That's not all. 那不是全部。 If we take your Java output of 如果我们把你的Java输出

Ye+fySvneVUZJXth67+Si/e8fBUV4Sxs7wEXVDEOJjBMHl1encvt65gGIj8CiFzBGp5uUgKYJZCuQ4rc964vZigjlrJ/430LgYcathLLd9U=

and decrypt it, then we get the following token: 解密它,然后我们得到以下标记:

{"session_id":"4182e173-3a24-4c10-b76c-b85a36be1173","ts_ms":1445786965574}

which is different from the token that you have in your code: 这与您代码中的令牌不同:

{'session_id':'abf52ca5-9d87-4061-b109-334abb7e637a','ts_ms':1445705791480}

The main remaining problem is that you're using invalid JSON. 剩下的主要问题是你使用的是无效的JSON。 Strings and keys in JSON need to be wrapped in " and not ' . JSON中的字符串和键需要包含在"而不是'

Which means that the encrypted token actually should have been (using a valid version of the token from your code): 这意味着加密令牌实际应该是(使用代码中的有效版本的令牌):

D9rOP07fYgBfza5vbGsvdPe8fBUV4Sxs7wEXVDEOJjBMHl1encvt65gGIj8CiFzBsAWBDgtdSozv4jS_auBU-CgjlrJ_430LgYcathLLd9U

Here's a C# implementation that reproduces the same result as your Java code: 这是一个C#实现,它重现与Java代码相同的结果:

class Program
{
    public static byte[] GetKey(string siteSecret)
    {
        byte[] key = Encoding.UTF8.GetBytes(siteSecret);
        return SHA1.Create().ComputeHash(key).Take(16).ToArray();
    }

    public static string EncryptAes(string input, string siteSecret)
    {
        var key = GetKey(siteSecret);
        using (var aes = AesManaged.Create())
        {
            if (aes == null) return null;

            aes.Mode = CipherMode.ECB;
            aes.Padding = PaddingMode.PKCS7;
            aes.Key = key;
            byte[] inputBytes = Encoding.UTF8.GetBytes(input);

            var enc = aes.CreateEncryptor(key, new byte[16]);
            return UrlSafeBase64(enc.TransformFinalBlock(inputBytes,0,input.Length));
        }
    }

    // http://stackoverflow.com/a/26354677/162671
    public static string UrlSafeBase64(byte[] bytes)
    {
        return Convert.ToBase64String(bytes).TrimEnd('=')
            .Replace('+', '-')
            .Replace('/', '_');
    }
    static void Main(string[] args)
    {
        string siteSecret = "12345678";
        string jsonToken = "{'session_id':'abf52ca5-9d87-4061-b109-334abb7e637a','ts_ms':1445705791480}";

        Console.WriteLine(" json token: " + jsonToken);
        Console.WriteLine(" siteSecret: " + siteSecret);
        Console.WriteLine(EncryptAes(jsonToken, siteSecret));
        Console.ReadLine();
    }
}

I don't know why you said you're getting Irez-rWkCEqnsiRLWfol0IXQu1JPs3qL_G_9HfUViMG9u4XhffHqAyju6SRvMhFS86czHX9s1tbzd6B15r1vmY6s5S8odXT-ZE9A-y1lHns from the Java program because I'm not getting that output. 我不知道为什么你说你从Java程序中得到Irez-rWkCEqnsiRLWfol0IXQu1JPs3qL_G_9HfUViMG9u4XhffHqAyju6SRvMhFS86czHX9s1tbzd6B15r1vmY6s5S8odXT-ZE9A-y1lHns因为我没有得到那个输出。 The output I'm getting from both the C# version and the Java version is this: 我从C#版本和Java版本获得的输出是这样的:

Ye-fySvneVUZJXth67-Si_e8fBUV4Sxs7wEXVDEOJjBMHl1encvt65gGIj8CiFzBGp5uUgKYJZCuQ4rc964vZigjlrJ_430LgYcathLLd9U

As you can see here: 正如你在这里看到的:

程序输出的屏幕截图。 Top是C#版本,底部是Java版本(IntelliJ Idea 14.1.5)

The Java version was copy/pasted from your code and is using guava-18.0 and compiled with JDK8 x64 (I'm not a java expert so I'm just adding these in case it makes any difference). Java版本是从您的代码中复制/粘贴的,并且正在使用guava-18.0并使用JDK8 x64编译(我不是Java专家所以我只是添加它们以防它们有任何区别)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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