【问题标题】:Encrypting and Decrypting String using a Java equilavent of the C# CryptoStream使用 C# CryptoStream 的 Java 等效项加密和解密字符串
【发布时间】:2011-01-04 22:27:12
【问题描述】:

我正在考虑用 Java 为移动平台操作系统开发应用程序。

我在 C# WPF 中为 Windows 环境开发了一个应用程序。我正在使用加密流以使用以下代码加密和解密字符串。下面显示的代码只是加密

public string encrypt(string encryptionString)
    {
        byte[] clearTextBytes = Encoding.UTF8.GetBytes(encryptionString);

        SymmetricAlgorithm rijn = SymmetricAlgorithm.Create();

        MemoryStream ms = new MemoryStream();
        byte[] rgbIV = Encoding.ASCII.GetBytes("ryojvlzmdalyglrj");
        byte[] key = Encoding.ASCII.GetBytes("hcxilkqbbhczfeultgbskdmaunivmfuo");
        CryptoStream cs = new CryptoStream(ms, rijn.CreateEncryptor(key, rgbIV), CryptoStreamMode.Write);

        cs.Write(clearTextBytes, 0, clearTextBytes.Length);

        cs.Close();

        return Convert.ToBase64String(ms.ToArray());
    }

加密的字符串存储在在线数据库中。我需要做的是让 java 应用程序能够从数据库中读取字符串并使用来自 C# 应用程序的相同加密密钥解密字符串。

感谢您的帮助。

【问题讨论】:

  • 这个使用什么算法?模式?填充?你需要知道这些东西才能解密。
  • 我不认为这是重复的,因为上述问题涵盖了我使用 C# 的 Visual Basic,但可能相似但不同
  • @Erickson:.Net 的SymmetricAlgorithm 在 CBC 模式下默认为 PKCS7 填充。但通常你是正确的:你需要知道算法、模式和填充来完全指定加密方案。
  • VisualBasic.NET 和 C# 之间没有区别,除了语法。事实上,没有“C# CryptoStream”之类的东西,它的“.NET CryptoStream is used in [C#|VB]”。 IE。两种语言都使用相同的类。

标签: c# java encryption cryptostream


【解决方案1】:

就我个人而言,我喜欢 BouncyCastle 用于 Java 加密。这段代码(使用 BouncyCastle 轻量级 API)应该可以解决问题:

String decrypt(byte[] cryptoBytes, byte[] key, byte[] iv) {
    BlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
    cipher.init(false, new ParametersWithIV(new KeyParameter(key), iv));
    byte[] out = new byte[cipher.getOutputSize(cryptoBytes.length)];
    int offset = cipher.processBytes(cryptoBytes, 0, cryptoBytes.length, out, 0);
    cipher.doFinal(out, offset);
    return new String(out);
}

我发现 BouncyCastle 的轻量级 API 不如 JCE 提供者的东西那么痛苦,但如果你愿意,你可以将它用作提供者。

看起来 .net SymmetricAlgorithm 和 BC 的 PaddedBufferedBlockCipher 默认为 PKCS7 填充,因此您应该可以使用默认值。

【讨论】:

    【解决方案2】:

    您可能需要查看 javax.crypto.CipherInputStream 和 javax.crypto.CipherOutputStream。

    http://download.oracle.com/javase/1.5.0/docs/api/javax/crypto/CipherInputStream.html http://download.oracle.com/javase/1.5.0/docs/api/javax/crypto/CipherOutputStream.html

    它们的使用方式与上面的示例几乎完全相同,尽管 Cipher 对象的初始化可能略有不同。

    【讨论】:

    • 这些类的一个问题是底层 Cipher 实例抛出的任何异常都会被无声地吞噬。特别是,BadPaddingException 非常有用,可以让您知道您有错误(或错误的密钥),它会消失在梦幻岛。
    【解决方案3】:

    我使用以下内容在 .net 和 java 之间进行加密

    在 .net 中我使用:

        /// <summary>
        /// DES Encryption method - used to encryp password for the java.
        /// </summary>
        /// <param name="plainText"></param>
        /// <returns></returns>
        public string EncryptData(string plainText)
        {
            DES des = new DESCryptoServiceProvider();
            des.Mode = CipherMode.ECB;
            des.Padding = PaddingMode.PKCS7;
    
            des.Key = Encoding.UTF8.GetBytes(_secretPhrase.Substring(0, 8));
            des.IV = Encoding.UTF8.GetBytes(_secretPhrase.Substring(0, 8));
    
            byte[] bytes = Encoding.UTF8.GetBytes(plainText);
            byte[] resultBytes = des.CreateEncryptor().TransformFinalBlock(bytes, 0, bytes.Length);
    
            return Convert.ToBase64String(resultBytes);
        }
    
        /// <summary>
        /// DES Decryption method - used the decrypt password encrypted in java
        /// </summary>
        /// <param name="encryptedText"></param>
        /// <returns></returns>
        public string DecryptData(string encryptedText)
        {
            DES des = new DESCryptoServiceProvider();
            des.Mode = CipherMode.ECB;
            des.Padding = PaddingMode.PKCS7;
            des.Key = Encoding.UTF8.GetBytes(_secretPhrase.Substring(0, 8));
            des.IV = System.Text.Encoding.UTF8.GetBytes(_secretPhrase.Substring(0, 8));
    
            byte[] bytes = Convert.FromBase64String(encryptedText);
            byte[] resultBytes = des.CreateDecryptor().TransformFinalBlock(bytes, 0, bytes.Length);
    
            return Encoding.UTF8.GetString(resultBytes);
        }
    

    在java中我使用:

    公共类 CryptoUtil {

    public static final Logger LOG = Logger.getLogger(CryptoUtil.class);
    
    private Cipher cipher = null;
    
    private SecretKey key = null;
    
    // This variable holds a string based on which a unique key will be generated
    private static final String SECRET_PHRASE = "SECRET PHRASE GOES HERE";
    
    // Charset will be used to convert between String and ByteArray
    private static final String CHARSET = "UTF8";
    
     // The algorithm to be used for encryption/decryption DES(Data Encryption Standard)
    private static final String ALGORITHM = "DES";
    
    public CryptoUtil() throws DDICryptoException {
        try {
            // generate a key from SecretKeyFactory
            DESKeySpec keySpec = new DESKeySpec(SECRET_PHRASE.getBytes(CHARSET));
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
            key = keyFactory.generateSecret(keySpec);
            cipher = Cipher.getInstance(ALGORITHM);
        } catch (Exception e) {
            LOG.error(e);
            throw new DDICryptoException(e);
        }
    }
    
    
    /**
     * This method takes a plain text string and returns encrypted string using DES algorithm
     * @param plainText
     * @return String
     * @throws DDICryptoException
     */
    public String encrypt(String plainText) throws DDICryptoException {
        String encryptedString = null;
        try {
            // initializes the cipher with a key.
            cipher.init(Cipher.ENCRYPT_MODE, key);
    
            byte[] plainTextAsUTF8 = plainText.getBytes(CHARSET);
    
            // decrypts data in a single-part or multi-part operation
            byte[] encryptedBytes = cipher.doFinal(plainTextAsUTF8);
    
            encryptedString = new sun.misc.BASE64Encoder().encode(encryptedBytes);
        } catch (Exception e) {
            LOG.error(e);
            throw new DDICryptoException(e);
    
        }
        return encryptedString;
    
    }
    
    /**
     * This method takes a plain text string and returns encrypted string using DES algorithm
     * @param encryptedString
     * @return
     * @throws DDICryptoException
     */
    public String decrypt(String encryptedString) throws DDICryptoException {    
        String decryptedString = null;
        try {
            byte[] decodedString = new sun.misc.BASE64Decoder().decodeBuffer(encryptedString);
    
            // initializes the cipher with a key.
            cipher.init(Cipher.DECRYPT_MODE, key);
    
            // decrypts data in a single-part or multi-part operation
            byte[] decryptedBytes = cipher.doFinal(decodedString);
            decryptedString = new String(decryptedBytes, CHARSET);
        } catch (Exception e) {
            LOG.error(e);
            throw new DDICryptoException(e);
        }
        return decryptedString;
    }
    

    }

    【讨论】:

    • 你真的应该重新考虑你的方法。这里有很多问题:1)DES坏了。 2) 欧洲央行不使用 IV。 3) 如果您使用 CBC 模式,则 IV 必须是随机数,即为每条消息新生成的随机数。您不能重复使用 IV,否则您会将您的消息暴露给统计分析攻击(以及其他攻击)。 4)在Java中,你的SECRET_PHRASE是一个静态的最终字符串,这意味着它将被实习,这有点不安全(尽管它需要相当多的工作才能利用)。
    【解决方案4】:

    我已经设法解决了这个问题。现在解密工作正常。使用以下代码

        String plainPassword = "";
                try
                {
                    SecretKeySpec key = new SecretKeySpec("hcxilkqbbhczfeultgbskdmaunivmfuo".getBytes("US-ASCII"), "AES");
    
                    IvParameterSpec iv = new IvParameterSpec("ryojvlzmdalyglrj".getBytes("US_ASCII"));
    
                    Cipher cipher = Cipher.getInsta
    
    nce("AES/CBC/PKCS7Padding");
    
                cipher.init(Cipher.DECRYPT_MODE, key, iv);
    
                byte[] encoded = cipher.doFinal(Base64.decodeBase64(encryptedPassword.getBytes()));
                plainPassword = new String(encoded);
            }
            catch (Exception ex)
            {
                Log.d("Decryption Error", ex.toString());
            }
    
            return plainPassword;
    

    现在的问题在于加密。除了将密码从解密模式更改为加密模式外,我使用了相同的代码,但是由于某种原因,当我打印出加密的字符串时,它只会打印出一堆垃圾,这与 C# 创建的字符串完全不同。下面是加密的代码

    public String encrypt(String plainPasword)
        {
            String password = "";
            try
            {
                SecretKeySpec key = new SecretKeySpec("hcxilkqbbhczfeultgbskdmaunivmfuo".getBytes("US-ASCII"), "AES");
    
                IvParameterSpec iv = new IvParameterSpec("ryojvlzmdalyglrj".getBytes("US_ASCII"));
    
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
    
                cipher.init(Cipher.ENCRYPT_MODE, key, iv);
    
                byte[] encoded = cipher.doFinal(plainPasword.getBytes());
                password = new String(encoded);
    
    
            }
            catch (Exception ex)
            {
                Log.d("Encryption Error", ex.toString());
            }
            return password;
        }
    

    这似乎有什么问题,我无法解决。谢谢

    【讨论】:

    • 这将是一个问题:password = new String(encoded);encoded 变量是一个原始字节数组,可能包含与字符不对应的字节,例如零字节。您的解密代码假定密文是 base64 编码的,因此您需要将其添加到加密步骤中。 Apache commons (commons.apache.org/codec) 有一个在 Java 中执行此操作的库。
    【解决方案5】:
            StringBuffer strbuf = new StringBuffer(buf.length * 2);
            int i;
    
            for (i = 0; i < buf.length; i++) {
                if (((int) buf[i] & 0xff) < 0x10) {
                    strbuf.append("0");
                }
    
                strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
            }
    

    在将结果字节数组转换为字符串之前,您必须对其进行编码。上面的代码对我有用,而我的实际加密功能在下面。

    public String encrypt(String data) throws Exception{
        try {
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            Key k = new SecretKeySpec(key.getBytes(), 0, key.length(), "AES");
    
            // Calculate ciphertext size.
            int blocksize = 16;
            int ciphertextLength = 0;
            int remainder = data.getBytes().length % blocksize;
            if (remainder == 0) {
                ciphertextLength = data.getBytes().length + blocksize;
            } else {
                ciphertextLength = data.getBytes().length - remainder + blocksize;
            }
    
    
            cipher.init(Cipher.ENCRYPT_MODE, k);
            byte[] buf = new byte[ciphertextLength];
            cipher.doFinal(data.getBytes(), 0, data.length(), buf, 0);
    
            StringBuffer strbuf = new StringBuffer(buf.length * 2);
            int i;
    
            for (i = 0; i < buf.length; i++) {
                if (((int) buf[i] & 0xff) < 0x10) {
                    strbuf.append("0");
                }
    
                strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
            }
            return strbuf.toString();
        } catch (Exception e) {
            Logger.logException(e);
        }
        return null;
    }
    

    【讨论】:

      【解决方案6】:

      Equivalent to CryptoStream .NET in Java? 上查看答案#5

      请务必阅读底部的 cmets...

      KeySpec ks = new DESKeySpec("key12345".getBytes("UTF-8")); SecretKey key = SecretKeyFactory.getInstance("DES").generateSecret(ks);
      IvParameterSpec iv = new IvParameterSpec( Hex.decodeHex("1234567890ABCDEF".toCharArray()));
      Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, key, iv);
      byte[] decoded = cipher.doFinal(Base64.decodeBase64("B3xogi/Qfsc="));
      System.out.println("Decoded: " + new String(decoded, "UTF-8"));

      希望这会有所帮助...
      JK

      【讨论】:

        【解决方案7】:

        塞梅隆, 整洁的代码!

        我遇到了一个有趣的情况,我们的客户将 IVkey 相同。

        在尝试了各种组合后,我得到了错误的填充异常,有效的解决方案是

        byte[] iv=new byte[8]; // assuming RC2
        System.arraycopy(key.getBytes(), 0, iv, 0, key.getBytes().length > iv.length ? key.getBytes().length);
        
        // Now decrypt and hopefully this should work
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-01-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-11-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多