2015-10-25 9 views
5

ich mit wenig Erfolg in den Hafen über die Google-Code bin versucht, ein sicheres Zeichen für ihre Captcha zu erzeugen (https://github.com/google/recaptcha-java/blob/master/appengine/src/main/java/com/google/recaptcha/STokenUtils.java):Porting Java Verschlüsselungsroutine zu C#

Das Original-Dienstprogramm hat die folgenden:

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)); 

Angesichts der Werte, die ich fest codierte, bekomme ich Irez-rWkCEqnsiRLWfol0IXQu1JPs3qL_G_9HfUViMG9u4XhffHqAyju6SRvMhFS86czHX9s1tbzd6B15r1vmY6s5S8odXT-ZE9A-y1lHns" zurück als mein verschlüsseltes Token.

Meine Java und Crypto Fähigkeiten sind mehr als ein bisschen eingerostet, und es gibt nicht immer direkte Analoga in C#. Ich versuchte encrypeAes() und getKey() mit folgenden zu verschmelzen, was nicht korrekt ist:

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()? 
    } 
} 

Die C# Version erzeugt den falschen Wert von: Ye+fySvneVUZJXth67+Si/e8fBUV4Sxs7wEXVDEOJjBMHl1encvt65gGIj8CiFzBGp5uUgKYJZCuQ4rc964vZigjlrJ/430LgYcathLLd9U=

+0

Implementiert SHA1-Hashing, noch keine Freude. –

+0

Sie könnten versuchen, ['AesManaged'] (https://msdn.microsoft.com/en-us/library/system.security.cryptography.aesmanaged%28v=vs.100%29.aspx) anstelle von' RijndaelManaged' zu verwenden (die standardmäßig eine andere Schlüsselgröße hat und daher nicht der * Standard * AES ist), um den Java-Part 'CIPHER_INSTANCE_NAME =" AES/ECB/PKCS5Padding "zu finden und [' Convert.ToBase64String'] zu verwenden (https: // msdn.microsoft.com/en-us/library/dhx0d524%28v=vs.110%29.aspx) anstelle von 'HttpServerUtility.UrlTokenEncode ...'. – pasty

+0

Aktualisiert, um C# -Ausgabe einzuschließen. @pasty Ich habe auf "AESManaged" aktualisiert, bin mir aber nicht sicher, wie ich mich an den Chiffre-Namen anpassen würde. Ich habe mich für "PaddingMode.PKCS7" pro http://blog.zebsadiq.com/post/AESCBCPKCS5Padding-EncryptionDecryption-in-C.aspx entschieden, aber ich gebe zu, dass ich mit mir herumfummle. Der Wechsel zu 'Convert.ToBase64String' hatte keine Auswirkungen auf das Ergebnis. Ich habe mich für 'HttpServerUtility.UrlTokenEncode' entschieden, da es der Basis BaseEncoding.base64Url() von Java am nächsten kam. –

Antwort

4

Ihr Code funktioniert fast wie erwartet. Es ist nur so, dass Sie die Ausgaben der Java-Version (und möglicherweise der C# -Version) irgendwie durcheinander gebracht haben.

Wenn ich Ihr Java-Code (JDK 7 & 8 mit Guava 18.0) ausführen, bekomme ich

 
Ye-fySvneVUZJXth67-Si_e8fBUV4Sxs7wEXVDEOJjBMHl1encvt65gGIj8CiFzBGp5uUgKYJZCuQ4rc964vZigjlrJ_430LgYcathLLd9U 

und wenn ich C# -Code (DEMO) ausführen, bekomme ich

 
Ye-fySvneVUZJXth67-Si_e8fBUV4Sxs7wEXVDEOJjBMHl1encvt65gGIj8CiFzBGp5uUgKYJZCuQ4rc964vZigjlrJ_430LgYcathLLd9U1 

So , die C# Version hat eine zusätzliche "1" am Ende. Es sollte ein Füllzeichen sein, ist es aber nicht. Das bedeutet, dass HttpServerUtility.UrlTokenEncode() keine standardkonforme URL-sichere Base64-Codierung bietet und Sie sie nicht verwenden sollten. Siehe auch this Q&A.

Die URL-safe Base64-Kodierung leicht von der normalen Base64-Kodierung abgeleitet werden kann (Tabellen 1 und 2 in RFC4648 Vergleich), wie in this answer von Marc GRA gesehen:

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

mit:

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

Das ist nicht alles. Wenn wir Ihre Java-Ausgabe von

 
Ye+fySvneVUZJXth67+Si/e8fBUV4Sxs7wEXVDEOJjBMHl1encvt65gGIj8CiFzBGp5uUgKYJZCuQ4rc964vZigjlrJ/430LgYcathLLd9U= 

und decrypt es, dann erhalten wir das folgende Token:

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

, die aus dem Token unterscheidet, die Sie in Ihrem Code haben:

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

Das verbleibende Hauptproblem besteht darin, dass Sie ungültiges JSON verwenden. Zeichenfolgen und Schlüssel in JSON müssen in " und nicht ' verpackt werden.

Was bedeutet, dass die verschlüsselten Token sollen eigentlich gewesen sein (eine gültige Version des Token aus dem Code verwenden):

 
D9rOP07fYgBfza5vbGsvdPe8fBUV4Sxs7wEXVDEOJjBMHl1encvt65gGIj8CiFzBsAWBDgtdSozv4jS_auBU-CgjlrJ_430LgYcathLLd9U 
3

Hier ist eine C# -Implementierung, die das gleiche Ergebnis wie Ihr Java reproduziert Code:

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(); 
    } 
} 

ich weiß nicht, warum Sie sagen, Sie Irez-rWkCEqnsiRLWfol0IXQu1JPs3qL_G_9HfUViMG9u4XhffHqAyju6SRvMhFS86czHX9s1tbzd6B15r1vmY6s5S8odXT-ZE9A-y1lHns aus dem Java-Programm, wenn es darum, weil ich nicht bin, dass Ausgang bekommen. Der Ausgang Ich bin sowohl von der C# -Version und der Java-Version bekommen, ist dies:

Ye-fySvneVUZJXth67-Si_e8fBUV4Sxs7wEXVDEOJjBMHl1encvt65gGIj8CiFzBGp5uUgKYJZCuQ4rc964vZigjlrJ_430LgYcathLLd9U

Wie Sie hier sehen können:

Screenshot of the programs output. Top is the C# version and bottom is the Java version (IntelliJ Idea 14.1.5)

Die Java-Version war copy/aus dem Code eingefügt und wird mit Guave-18.0 und mit JDK8 x64 kompiliert (Ich bin kein Java-Experten so bin ich nur diese Zugabe, falls es einen Unterschied macht) .