4

I am looking at developing an application in Java for a mobile platform operating system.

I have developed an application in C# WPF for the Windows Environment. I am using a cryptostream in order to encrypt and decrypt a string using the following code. the code shown below is the encryption only

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

The encrypted string is stored in an online database. What I need to be able to do is for the java application to be able to read the string from the database and decrypt the string using the same encryption keys from the C# application.

Thanks for your help.

Boardy
  • 35,417
  • 104
  • 256
  • 447
  • What algorithm does this use? Mode? Padding? You'll need to know these things in order to decrypt. – erickson Jan 04 '11 at 22:31
  • Duplicate: http://stackoverflow.com/questions/4489942/equivalent-to-cryptostream-net-in-java – Taylor Leese Jan 04 '11 at 22:33
  • I do not consider this as a duplicate as the above mentioned question is covering Visual Basic where as I am using C#, however may be similar it is different – Boardy Jan 04 '11 at 22:45
  • @Erickson: .Net's `SymmetricAlgorithm` defaults to PKCS7 padding in CBC mode. But generally you are correct: you need to know the algorithm, mode and padding to fully specify an encryption scheme. – Cameron Skinner Jan 04 '11 at 22:51
  • there is no difference between VisualBasic.NET and C#, except for syntax. In fact there is no such thing as "C# CryptoStream", its ".NET CryptoStream being used in [C#|VB]". I.e. it is the same class being used in either language. – AviD Jan 09 '11 at 17:06

7 Answers7

6

Personally, I like BouncyCastle for Java crypto. This code (using the BouncyCastle lightweight API) should do the trick:

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

I find BouncyCastle's lightweight API to be less painful than the JCE provider stuff but you can use it as a provider if you wish.

It looks like both the .net SymmetricAlgorithm and BC's PaddedBufferedBlockCipher default to PKCS7 padding so you should be OK with using the defaults.

Cameron Skinner
  • 51,692
  • 2
  • 65
  • 86
2

You may want to check out javax.crypto.CipherInputStream and 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

They are used almost the exact same way as your sample above, though initialization of the Cipher objects may be slightly different.

  • One gotcha with those classes is that any exceptions thrown by the underlying Cipher instance are silently swallowed. In particular, the `BadPaddingException`, which is very useful in letting you know you have a bug (or a bad key), disappears into neverland. – President James K. Polk Jan 05 '11 at 00:07
0

I use the following for encrypting between .net and java

In .net i use:

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

and in java I use:

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

}

Alex Mendez
  • 5,120
  • 1
  • 25
  • 23
  • You should really rethink your approach. There are a bunch of problems here: 1) DES is broken. 2) ECB doesn't use an IV. 3) If you're using CBC mode then the IV must be a nonce, i.e. a newly generated random number for each message. You cannot re-use an IV otherwise you're exposing your message to statistical analysis attacks (amongst others). 4) In Java your `SECRET_PHRASE` is a static final String which means it will be interned which is a little unsafe (although it requires a fair bit of work to exploit). – Cameron Skinner Jan 14 '11 at 00:01
0

I have sort of managed to resolve the problem. The decryption now works fine. Using the following code

    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;

The problem is now with the encryption. I have used the same code except changed the cipher from decrypt mode to encrypt mode but for some reason when I print out the encrypted string it just prints a load of rubbish that's nothing like the string that C# creates. Below is teh code for the encryption

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

What seems to be wrong with this i can't work it out. Thanks

Boardy
  • 35,417
  • 104
  • 256
  • 447
  • 1
    This will be a problem: `password = new String(encoded);`. The `encoded` variable is a raw byte array and is likely to contain bytes that do not correspond to characters, e.g. zero bytes. Your decryption code assumes that the ciphertext is base64 encoded so you'll need to add that to your encryption step. Apache commons (http://commons.apache.org/codec/) has a library for doing this in Java. – Cameron Skinner Jan 11 '11 at 17:35
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));
        }

You have to encode the resulting byte array before converting it to string. The code above did the trick for me, while my actual encryption function is below.

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

See Answer #5 on Equivalent to CryptoStream .NET in Java?

Be sure to read the comments at the bottom...

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

Hope this helps...
JK

Community
  • 1
  • 1
John K.
  • 5,426
  • 1
  • 21
  • 20
-1

Cemeron, Neat code there!

I came across an interesting situation where our customer had given the IV the same as the key.

After trying out various combinations where I was getting bad padding exception, the solution that worked was

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
Hamad
  • 5,096
  • 13
  • 37
  • 65