简体   繁体   English

C#和Java之间的加密/解密

[英]Encryption/Decryption between C# and Java

There is a server which encrypt password on C# (method encryptCSharp), android app receives encrypted password, salt, passPhrase and initVector.在 C# 上有一个加密密码的服务器(方法 encryptCSharp),android 应用程序接收加密的密码、salt、passPhrase 和 initVector。 I have to decrypt that password in Java.我必须用 Java 解密那个密码。 Server-guy sent me an encrypt method in C# and I need to created encryptJava and decryptJava methods which works the same in Java as in C#.服务器家伙在 C# 中给我发送了一个加密方法,我需要创建 encryptJava 和decryptJava 方法,它们在 Java 中的工作方式与在 C# 中的工作方式相同。 To create PasswordDeriveBytes which absent in Java I use an example from here Encryption Diff Between Java and C#要创建 Java 中不存在的PasswordDeriveBytes ,我使用这里的示例Encryption DiffBetween Java and C#

So, my question is, what's going wrong with my Java methods?所以,我的问题是,我的 Java 方法出了什么问题? Both didn't work两个都没用

Update: I made some changes in snippets and now all works!!更新:我对片段进行了一些更改,现在一切正常!!

I invoke that methods:我调用这些方法:

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

Here are Java methods that I used and C# method from server.这是我使用的 Java 方法和来自服务器的 C# 方法。

C# (which I can't change) C#(我无法改变)

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 to encrypt message, it works and I got the same result as method in C# Java 加密消息,它有效,我得到了与 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 method that emulate the same method in C#在 C# 中模拟相同方法的 Java 方法

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

and finally Java method that doesn't work and try to understand what I do wrong最后是不起作用的 Java 方法,并尝试了解我做错了什么

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

With PasswordDeriveBytes never ever ever ask for more bytes than the underlying hash function.随着PasswordDeriveBytes永远不要求比底层散列函数多个字节。 This function implements PBKDF1 which cannot output more than that number of bits (160 for SHA-1 in your example).此函数实现了 PBKDF1,它不能输出超过该位数(在您的示例中 SHA-1 为 160)。

The Microsoft implementation allows more output, but the implementation is broken to the extreme (it may even repeat output!).微软的实现允许更多的输出,但是实现被破坏到了极致(它甚至可能重复输出!)。 Use Rfc2898DeriveBytes instead which implements PBKDF2, also available in Java.使用Rfc2898DeriveBytes代替它实现 PBKDF2,也可在 Java 中使用。 Use a larger hash, PBKDF2 can generate more than the output of the hash as bytes, but only at the cost of security.使用更大的散列,PBKDF2 可以生成比散列输出更多的字节,但只会以安全为代价。

Finally I found a solution for encoding and deconding method: (so that to not increase the amount of code here, I made changes in snippets above)最后我找到了一个编码和解码方法的解决方案:(为了不增加这里的代码量,我对上面的片段进行了更改)

in encryptJava method I changed one line,encryptJava方法中,我更改了一行,

in PasswordDeriveBytes I changed iterations from 100 to 1PasswordDeriveBytes我将迭代从 100 更改为 1

in decryptJava method I added one line and deleted one line.decryptJava方法中,我添加了一行并删除了一行。

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

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