【问题标题】:encryption result in java and .net are not samejava和.net中的加密结果不一样
【发布时间】:2014-02-24 03:43:58
【问题描述】:

我的 .net 项目中有一个加密密码的方法

public string Encrypt(string plainText)
{
    string PassPhrase = "#$^&*!@!$";
    string SaltValue = "R@j@}{BAe";
    int PasswordIterations = Convert.ToInt32(textBox5.Text); //amend to match java encryption iteration
    string InitVector = "@1B2c3D4e5F6g7H8";
    int KeySize = 256; //amend to match java encryption key size

    byte[] initVectorBytes = Encoding.ASCII.GetBytes(InitVector);
    byte[] saltValueBytes = Encoding.ASCII.GetBytes(SaltValue);

    byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);

    PasswordDeriveBytes password= new PasswordDeriveBytes(
        PassPhrase,
        saltValueBytes,
        "MD5",
        PasswordIterations);

    byte[] keyBytes = password.GetBytes(KeySize / 8);
    RijndaelManaged symmetricKey = new RijndaelManaged();
    symmetricKey.Mode = CipherMode.CBC;

    ICryptoTransform encryptor = symmetricKey.CreateEncryptor(
                                                     keyBytes,
                                                     initVectorBytes);
    MemoryStream memoryStream = new MemoryStream();

    CryptoStream cryptoStream = new CryptoStream(memoryStream,
                                                 encryptor,
                                                 CryptoStreamMode.Write);

    cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
    cryptoStream.FlushFinalBlock();
    byte[] cipherTextBytes = memoryStream.ToArray();

    memoryStream.Close();
    cryptoStream.Close();

    string cipherText = Convert.ToBase64String(cipherTextBytes);

    return cipherText;
}

我的任务是将此方法转换为 java,但在 java 中我没有得到与 .Net 版本相同的结果

我的java代码是

package com.andc.billing.pdc.security;

import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.management.openmbean.InvalidKeyException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class PasswordCrypto {

    private static final String password = "#$^&*!@!$";
    private static String initializationVector = "@1B2c3D4e5F6g7H8";
    private static String salt = "R@j@}{BAe";
    private static int pswdIterations = 2;
    private static int keySize = 128;
    private static final Log log = LogFactory.getLog(PasswordCrypto.class);

    public static String encrypt(String plainText) throws 
        NoSuchAlgorithmException, 
        InvalidKeySpecException, 
        NoSuchPaddingException, 
        InvalidParameterSpecException, 
        IllegalBlockSizeException, 
        BadPaddingException, 
        UnsupportedEncodingException, 
        InvalidKeyException, 
        InvalidAlgorithmParameterException, java.security.InvalidKeyException, NoSuchProviderException 
    {   
        byte[] saltBytes = salt.getBytes("ASCII");//"UTF-8");
        byte[] ivBytes = initializationVector.getBytes("ASCII");//"UTF-8");

        // Derive the key, given password and salt.
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");//PBEWithMD5AndDES");
        PBEKeySpec spec = new PBEKeySpec(
                password.toCharArray(), 
                saltBytes, 
                pswdIterations, 
                keySize
        );

        SecretKey secretKey = factory.generateSecret(spec);
        SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");


        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); //Cipher.getInstance("AES/CBC/PKCSPadding"
        cipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(ivBytes));

        byte[] encryptedTextBytes = cipher.doFinal(plainText.getBytes("ASCII"));//UTF-8"));
        String str=new org.apache.commons.codec.binary.Base64().encodeAsString(encryptedTextBytes);
        log.info(str);
        return str;
    }
}

.net“1”的加密结果是:

7mPh3/E/olBGbFpoA18oqw==

java

7RPk77AIKAhOttNLW4e5yQ==

你能帮我解决这个问题吗?

【问题讨论】:

  • 这不是问题,除非他们解密到不同的东西;所以...两种实现都可以平等地解密两个字符串吗?
  • 在 .net 方面,您使用 RijndaelManaged 而不是 AesManaged。 Rijndael 是 AES 使用的算法,但在 AES 中还有更多限制。
  • +10 为您提供帮助,但是当在 .net 中我使用 Rfc2898DeriveBytes 而不是 PasswordDeriveBytes 时,结果与 java 相同,但我无法更改 .net 端,我正在寻找 java 代码来给我相同的结果我在 .net 中使用 PasswordDeriveBytes

标签: c# java encryption aes md5


【解决方案1】:

非常感谢您提供的解决方案 - 它运行良好,但稍作修正(根据下面的初始帖子 mentioned):

请使用:

b = generateExtendedKey(count);

代替:

b = generateExtendedKey(++count);

它甚至适用于 256 密钥大小:

这是一个使用 256 位密钥解密 C# Rijndael 编码数据的小代码:

public static String decrypt(final String cipherText, final String passPhrase, final String saltValue, final int passwordIterations, final String initVector, final int keySize)
    throws Exception {
    final byte[] initVectorBytes = initVector.getBytes("ASCII");
    final byte[] saltValueBytes = saltValue.getBytes("ASCII");
    final byte[] cipherTextBytes = Base64.decode(cipherText);
    final PKCS5S1ParametersGenerator generator = new PasswordDeriveBytes(new SHA1Digest());
    generator.init(passPhrase.getBytes("ASCII"), saltValueBytes, passwordIterations);
    final byte[] key = ((KeyParameter) generator.generateDerivedParameters(keySize)).getKey();
    final SecretKey secretKey = new SecretKeySpec(key, ALGORITHM);
    final Cipher cipher = Cipher.getInstance(TRANSFORMATION);
    final IvParameterSpec iv = new IvParameterSpec(initVectorBytes);
    cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
    final byte[] decryptedVal = cipher.doFinal(cipherTextBytes);
    return new String(decryptedVal);
}

插件: 如果您关心密钥大小限制,您可以使用this solution,它工作得很好(在 Ubuntu 12、Java 1.7 64 位(java 版本“1.7.0_25” Java(TM) SE 运行时环境 (build 1.7.0_25-b15) Java HotSpot(TM) 64 位服务器 VM(内部版本 23.25-b01,混合模式))

【讨论】:

    【解决方案2】:

    我注意到的第一件事是您使用的算法不同,在 .Net 中它是 PBKDF1 的扩展,在 java 中它是 PBKDF2,PBKDF2 取代了 PBKDF1。

    在 .net 中,您使用的是 the PasswordDeriveBytes class,它“使用 PBKDF1 算法的扩展从密码中获取密钥。”

    我还注意到密码迭代在 Java 中被硬编码为 2,并且来自 .Net 中的文本框...确保它们相同。

    纠正那个,让我们知道结果。

    更新:对于 .net 中的 PBKDF2,请使用 Rfc2898DeriveBytes 类。

    一些非常好的相关资料have a read of this page

    编辑:This link should be helpful,如果你可以使用Chilkat library

    这是 1 和 2 之间的复杂区别,1 最多只能处理 20 个字节,MS 已经构建了一个扩展,允许更多,下面的代码应该更准确地再现 .net 输出。 Taken from here.

    import org.bouncycastle.crypto.CipherParameters;
    import org.bouncycastle.crypto.Digest;
    import org.bouncycastle.crypto.digests.SHA1Digest;
    import org.bouncycastle.crypto.generators.PKCS5S1ParametersGenerator;
    import org.bouncycastle.crypto.params.KeyParameter;
    import org.bouncycastle.util.encoders.Hex;
    
    
    public class PKCS5Test
    {
        /**
         * @param args
         */
        public static void main(String[] args) throws Exception
        {
            byte[] password = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
            byte[] salt = PKCS5S1ParametersGenerator.PKCS5PasswordToBytes("MyTesting".toCharArray());
    
            PKCS5S1ParametersGenerator generator = new PasswordDeriveBytes(new SHA1Digest());
            generator.init(password, salt, 100);
    
            byte[] key = ((KeyParameter)generator.generateDerivedParameters(512)).getKey();
            System.out.println( "64 " + new String(Hex.encode(key)).toUpperCase() );
        }
    
        static class PasswordDeriveBytes extends PKCS5S1ParametersGenerator
        {
            private final Digest d;
    
            private byte[] output = null;
    
            public PasswordDeriveBytes(Digest d)
            {
                super(d);
    
                this.d = d;
            }
    
            public CipherParameters generateDerivedParameters(int keySize)
            {
                keySize = keySize / 8;
    
                byte[] result = new byte[keySize];
                int done = 0;
                int count = 0;
                byte[] b = null;
    
                while (done < result.length)
                {
                    if (b == null)
                    {
                        b = generateInitialKey();
                    }
                    else if (++count < 1000)
                    {
                        b = generateExtendedKey(++count);
                    }
                    else
                    {
                        throw new RuntimeException("Exceeded limit");
                    }
    
                    int use = Math.min(b.length, result.length - done);
                    System.arraycopy(b, 0, result, done, use);
                    done += use;
                }
    
                return new KeyParameter(result);
            }
    
            private byte[] generateOutput()
            {
                byte[] digestBytes = new byte[d.getDigestSize()];
    
                d.update(password, 0, password.length);
                d.update(salt, 0, salt.length);
                d.doFinal(digestBytes, 0);
    
                for (int i = 1; i < (iterationCount - 1); i++)
                {
                    d.update(digestBytes, 0, digestBytes.length);
                    d.doFinal(digestBytes, 0);
                }
    
                return digestBytes;
            }
    
            private byte[] generateInitialKey()
            {
                output = generateOutput();
                d.update(output, 0, output.length);
    
                byte[] digestBytes = new byte[d.getDigestSize()];
                d.doFinal(digestBytes, 0);
                return digestBytes;
            }
    
            private byte[] generateExtendedKey(int count)
            {
                byte[] prefix = Integer.toString(count).getBytes();
                d.update(prefix, 0, prefix.length);
                d.update(output, 0, output.length);
    
                byte[] digestBytes = new byte[d.getDigestSize()];
                d.doFinal(digestBytes, 0);
    
                //System.err.println( "X: " + new String(Hex.encode(digestBytes)).toUpperCase() );
                return digestBytes;
            }
        }
    } 
    

    【讨论】:

    • +10 的帮助和感谢,密码迭代是真的,作为我模拟主要应用程序加密方法的 .net 代码,什么时候在 .net 中使用 Rfc2898DeriveBytes 并将密钥大小更改为128 java 的结果是一样的,我正在寻找一种方法来生成结果,比如 java 256 位密钥大小中的 PasswordDeriveBytes
    猜你喜欢
    • 1970-01-01
    • 2016-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-19
    • 2013-08-15
    相关资源
    最近更新 更多