2017-08-26 4 views
0

Es gibt einen Server, der Passwort auf C# verschlüsseln (Methode verschlüsselnCSharp), Android App erhält verschlüsseltes Passwort, Salz, PassPhrase und InitVector. Ich muss dieses Passwort in Java entschlüsseln. Server-guy hat mir eine Verschlüsselungsmethode in C# geschickt und ich muss encryptJava und decryptJava Methoden erstellen, die in Java genauso funktionieren wie in C#. Um PasswordDeriveBytes zu erstellen, die in Java abwesend sind, verwende ich ein Beispiel von hier Encryption Diff Between Java and C#Verschlüsselung/Entschlüsselung zwischen C# und Java

Also, meine Frage ist, was ist los mit meinen Java-Methoden? Beide haben nicht funktioniert

Update: Ich machte einige Änderungen in Schnipsel und jetzt funktioniert alles !!

rufe ich, dass Methoden:

String encryptedText = encryptJava("12345", "100", "@.erf.net34", "@[email protected]");//it works!! 
String decryptedText = decryptJava(encryptedText, "100", "@.erf.net34", "@[email protected]");//it doesn't work!! 

Hier sind Java-Methoden, die ich verwenden und C# Methode vom Server.

C# (was ich nicht ändern kann)

public static String encryptCSharp(String plainText, String saltValue, String passPhrase, String initVector) { 
    String hashAlgorithm = "SHA1"; 
    int passwordIterations = 1; 
    int keySize = 256; 
    // Convert strings into byte arrays. 
    // Let us assume that strings only contain ASCII codes. 
    // If strings include Unicode characters, use Unicode, UTF7, or UTF8 
    // encoding. 
    byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector); 
    byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue); 

    // Convert our plaintext into a byte array. 
    // Let us assume that plaintext contains UTF8-encoded characters. 
    byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText); 

    // First, we must create a password, from which the key will be derived. 
    // This password will be generated from the specified passphrase and 
    // salt value. The password will be created using the specified hash 
    // algorithm. Password creation can be done in several iterations. 
    PasswordDeriveBytes password = new PasswordDeriveBytes(
      passPhrase, 
      saltValueBytes, 
      hashAlgorithm, 
      passwordIterations); 

    // Use the password to generate pseudo-random bytes for the encryption 
    // key. Specify the size of the key in bytes (instead of bits). 
    byte[] keyBytes = password.GetBytes(keySize/8); 

    // Create uninitialized Rijndael encryption object. 
    RijndaelManaged symmetricKey = new RijndaelManaged(); 

    // It is reasonable to set encryption mode to Cipher Block Chaining 
    // (CBC). Use default options for other symmetric key parameters. 
    symmetricKey.Mode = CipherMode.CBC; 

    // Generate encryptor from the existing key bytes and initialization 
    // vector. Key size will be defined based on the number of the key 
    // bytes. 
    ICryptoTransform encryptor = symmetricKey.CreateEncryptor(
      keyBytes, 
      initVectorBytes); 

    // Define memory stream which will be used to hold encrypted data. 
    MemoryStream memoryStream = new MemoryStream(); 

    // Define cryptographic stream (always use Write mode for encryption). 
    CryptoStream cryptoStream = new CryptoStream(memoryStream, 
      encryptor, 
      CryptoStreamMode.Write); 
    // Start encrypting. 
    cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length); 

    // Finish encrypting. 
    cryptoStream.FlushFinalBlock(); 

    // Convert our encrypted data from a memory stream into a byte array. 
    byte[] cipherTextBytes = memoryStream.ToArray(); 

    // Close both streams. 
    memoryStream.Close(); 
    cryptoStream.Close(); 

    // Convert encrypted data into a base64-encoded string. 

    String cipherText = Convert.ToBase64String(cipherTextBytes); 

    // Return encrypted string. 
    return cipherText; 
} 

Java Nachricht verschlüsseln, es funktioniert und ich habe das gleiche Ergebnis wie Methode in C#

private String encryptJava(String plainText, String saltValue, String passPhrase, String initVector) {//working!!! 
    String result = ""; 

    byte[] initVectorBytes = initVector.getBytes(US_ASCII); 
    byte[] saltValueBytes = saltValue.getBytes(US_ASCII); 
    byte[] plainTextBytes = plainText.getBytes(UTF_8); 

    Cipher cipher; 
    try { 
     final com.gmail.example.PasswordDeriveBytes password = new com.gmail.example.PasswordDeriveBytes(passPhrase, saltValueBytes); 
     final byte[] keyBytes = password.getBytes(256/Byte.SIZE); 
     SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES"); 

     cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
     try { 
      cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(initVectorBytes)); 
     } catch (InvalidAlgorithmParameterException e) { 
      e.printStackTrace(); 
     } 

     final byte[] ct = cipher.doFinal(plainTextBytes); 
     result = Base64.encodeToString(ct, Base64.DEFAULT);//**added this line!** 
     //result = new String(ct, "US-ASCII");**-- deleted this line!** 
    } catch (NoSuchAlgorithmException e) { 
     e.printStackTrace(); 
    } catch (NoSuchPaddingException e) { 
     e.printStackTrace(); 
    } catch (InvalidKeyException e) { 
     e.printStackTrace(); 
    } catch (BadPaddingException e) { 
     e.printStackTrace(); 
    } catch (IllegalBlockSizeException e) { 
     e.printStackTrace(); 
    } 
    return result; 
} 

Java-Methode, die die emulieren gleiche Methode in C#

public class PasswordDeriveBytes { 

    private final MessageDigest hash; 

    private final byte[] firstToLastDigest; 
    private final byte[] outputBuffer; 

    private int position = 0; 

    public PasswordDeriveBytes(String password, byte[] salt) { 
     try { 
      this.hash = MessageDigest.getInstance("SHA-1"); 

      this.hash.update(password.getBytes(UTF_8)); 
      this.hash.update(salt); 
      this.firstToLastDigest = this.hash.digest(); 

      final int iterations = 1;//**changed from 100** 
      for (int i = 1; i < iterations - 1; i++) { 
       hash.update(firstToLastDigest); 
       hash.digest(firstToLastDigest, 0, firstToLastDigest.length); 
      } 

      this.outputBuffer = hash.digest(firstToLastDigest); 

     } catch (NoSuchAlgorithmException | DigestException e) { 
      throw new IllegalStateException("SHA-1 digest should always be available", e); 
     } 
    } 

    public byte[] getBytes(int requested) { 
     if (requested < 1) { 
      throw new IllegalArgumentException(
        "You should at least request 1 byte"); 
     } 

     byte[] result = new byte[requested]; 

     int generated = 0; 

     try { 
      while (generated < requested) { 
       final int outputOffset = position % outputBuffer.length; 
       if (outputOffset == 0 && position != 0) { 
        final String counter = String.valueOf(position/outputBuffer.length); 
        hash.update(counter.getBytes(US_ASCII)); 
        hash.update(firstToLastDigest); 
        hash.digest(outputBuffer, 0, outputBuffer.length); 
       } 

       final int left = outputBuffer.length - outputOffset; 
       final int required = requested - generated; 
       final int copy = Math.min(left, required); 

       System.arraycopy(outputBuffer, outputOffset, result, generated, copy); 

       generated += copy; 
       position += copy; 
      } 
     } catch (final DigestException e) { 
      throw new IllegalStateException(e); 
     } 
     return result; 
    } 
} 

und schließlich Java-Methode, die nicht funktioniert und versuchen zu und erstand, was ich falsch gemacht

private String decryptJava(String encryptedText, String saltValue, String passPhrase, String initVector) { 
    String result = ""; 
    byte[] initVectorBytes = initVector.getBytes(US_ASCII); 
    byte[] saltValueBytes = saltValue.getBytes(US_ASCII); 
    byte[] encryptedTexttBytes = Base64.decode(encryptedText, Base64.DEFAULT); 
    Cipher cipher; 
    try { 
     final PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, saltValueBytes); 
     final byte[] keyBytes = password.getBytes(256/Byte.SIZE); 
     SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES"); 

     cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
     try { 
      cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(initVectorBytes)); 
     } catch (InvalidAlgorithmParameterException e) { 
      e.printStackTrace(); 
     } 
     final byte[] ct = cipher.doFinal(encryptedTexttBytes); 
     //result = Base64.encodeToString(ct, Base64.DEFAULT); - **deleted this line** 
     try { 
      result = new String(ct, "US-ASCII");//** added this line** 
     } catch (UnsupportedEncodingException e) { 
      e.printStackTrace(); 
     } 

    } catch (NoSuchAlgorithmException e) { 
     e.printStackTrace(); 
    } catch (NoSuchPaddingException e) { 
     e.printStackTrace(); 
    } catch (InvalidKeyException e) { 
     e.printStackTrace(); 
    } catch (BadPaddingException e) { 
     e.printStackTrace(); 
    } catch (IllegalBlockSizeException e) { 
     e.printStackTrace(); 
    } 
    return result; 
} 

Antwort

1

Mit PasswordDeriveBytes nie jemals fordern mehr Bytes als die zugrunde liegende Hash-Funktion. Diese Funktion implementiert PBKDF1, die nicht mehr als diese Anzahl von Bits ausgeben kann (160 für SHA-1 in Ihrem Beispiel).

Die Microsoft-Implementierung ermöglicht mehr Ausgabe, aber die Implementierung ist bis zum Extrem gebrochen (es kann sogar die Ausgabe wiederholen!). Verwenden Sie stattdessen Rfc2898DeriveBytes, das PBKDF2 implementiert, ebenfalls in Java verfügbar. Verwenden Sie einen größeren Hash, PBKDF2 kann mehr als die Ausgabe des Hashs als Byte generieren, aber nur auf Kosten der Sicherheit.

+0

Ich bin nicht in Kryptographie erfahren, also versuche ich, Ihre Antwort zu verstehen. Meinst du, dass der Typ, der mit Microsoft Server arbeitet, die Methode von 'PasswordDeriveBytes' auf' Rfc2898DeriveBytes' umstellen muss? – LumisD

+0

Ja, das wäre sicherlich ratsam. 'PasswordDeriveBytes' wurde von' Rfc2898DeriveBytes' ersetzt. Soweit ich weiß, hat niemand den erweiterten Algorithmus, den der frühere verwendet, vollständig beschrieben (er enthält wahrscheinlich einige schreckliche Pufferüberläufe). –

0

Schließlich fand ich eine Lösung für das Kodieren und deconding Methode: (so daß nicht die Menge an Code hier zu erhöhen, I vorgenommene Änderungen in Schnipseln oben)

in encryptJava Methode geändert I eine Zeile,

in PasswordDeriveBytes Ich änderte Iterationen von 100 zu 1

in decryptJava Methode Ich habe eine Zeile hinzugefügt und löschte eine Zeile.

+0

Froh, dass es funktioniert, aber bitte beachten Sie, dass Sie mit 'PasswordDeriveBytes' und 1 als Iterationszähler tatsächlich nicht * secure * gemacht haben, was ich hoffe, was Sie suchen. –

+0

@ MaartenBodewes, Server Encrypt-Methode ist außerhalb meiner Reichweite, aber ich werde Ihre Notizen an den Server Kerl zeigen, um das zu beheben. Obwohl ich die Antwort auf diese spezielle Frage gefunden habe, wäre es unpassierbar, es ohne Ihre Methode für Java zu lösen - 'PasswordDeriveBytes'. Vielen Dank für Ihre Antworten! – LumisD

Verwandte Themen