1

Basically, I have an encryption method written in Java (Actually it's a 3rd party one I need to use, it came with the provided key), it works fine and dandy, but trying to implement the same thing thing in C# I have run into a major issue, I can't figure out how to provide a key length larger then 8 characters to DESCryptoServiceProvider.

Here's my java code:

public static String EncryptUrl(String parameters){

    try{
        String encodedStr = "";
        Cipher cipher;
        DESKeySpec keySpec = new DESKeySpec(key.getBytes("UTF8"));
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        cipher = Cipher.getInstance("DES");
        cipher.init(Cipher.ENCRYPT_MODE, keyFactory.generateSecret(keySpec));

        encodedStr = Base64.encodeBase64String(cipher.doFinal(parameters.getBytes("UTF8")));

        try{
            encodedStr = URLEncoder.encode(encodedStr, "UTF-8");
        } catch (UnsupportedEncodingException e) {

            throw new AssertionError("UTF-8 is unknown");
        }

        return encodedStr;
    }
    catch(Exception ex){
        return null;
    }
}

And here's my attempt so far at a C# implementation:

    public static string EncryptUrl(string originalString)
    {
        if (String.IsNullOrEmpty(originalString))
        {
            throw new ArgumentNullException
                   ("The string which needs to be encrypted can not be null.");
        }

        byte[] tempKeyBytes = Encoding.ASCII.GetBytes("^op!l#ahD");
        byte[] keyBytes = new byte[16];

        Array.Copy(tempKeyBytes, keyBytes, tempKeyBytes.Length);

        DESCryptoServiceProvider cryptoProvider = new DESCryptoServiceProvider()
        {
            Padding = PaddingMode.None,
            KeySize = 128,
            BlockSize = 128
        };
        MemoryStream memoryStream = new MemoryStream();

        ICryptoTransform encryptor =    cryptoProvider.CreateWeakEncryptor(tempKeyBytes, tempKeyBytes);


        CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
        StreamWriter writer = new StreamWriter(cryptoStream);
        writer.Write(originalString);
        cryptoStream.FlushFinalBlock();
        writer.Flush();
        string encryptedUrl =  Convert.ToBase64String(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);

        return Uri.EscapeDataString(encryptedUrl);
    }

So long story short The key I was provided is 9 characters long (I HAVE to use this key and i HAVE to use DES and i HAVE to implement in C#), DESCryptoServiceProvider has an issue when using anything above 8 characters as a key, because of the fact the DES implementation in .NET only accepts a 64-bit key, so i'm wondering if there is a way to use a 9 character key for a DES encryption implementation

fraser jordan
  • 114
  • 1
  • 9
  • This is not code converting tool, what is your problem what is not working. – mybirthname Oct 05 '16 at 03:41
  • The key I was provided is 9 characters long (I HAVE to use this key and i HAVE to use DES), DESCryptoServiceProvider has an issue when using anything above 8 characters as a key, because of the fact the DES implementation in .NET only accepts a 64-bit key, so i'm wondering if there is a way to use a 9 character key for a DES encryption implementation – fraser jordan Oct 05 '16 at 03:44
  • Add this to your question I upvote your question, hope someone help you. – mybirthname Oct 05 '16 at 03:47
  • 1
    It doesn't matter how many characters your key has. It is important to know how many bytes the key string is encoded into. As far as I'm aware, Java would also only accept a key of 8 bytes, not more, not less. Perhaps you're using this on an older Java version, where there are no such restrictions. Which version are you using? – Artjom B. Oct 05 '16 at 05:07
  • General advice: **Always use a fully qualified Cipher string.** `Cipher.getInstance("DES");` may result in different ciphers depending on the default security provider. It most likely results in `"DES/ECB/PKCS5Padding"`, but it doesn't have to be. If it changes, you'll lose compatibility between different JVMs. For reference: [Java default Crypto/AES behavior](http://stackoverflow.com/q/6258047/1816580) – Artjom B. Oct 05 '16 at 05:09
  • Is it even possible to encode a 9 character string into 8 or less bits?? – fraser jordan Oct 05 '16 at 18:35
  • @ArtjomB. turns out the DESKeySpec class only takes the first 8 bytes of whichever key you pass in [link to docs](https://docs.oracle.com/javase/7/docs/api/javax/crypto/spec/DESKeySpec.html) So I guess it doesn't matter how large your key may be – fraser jordan Oct 05 '16 at 20:28
  • @fraserjordan You misunderstand, it's that a 8 character string may encode to 9 bytes. As also in my comment below your answer, but Artjom was indeed first, now that I care to read the comments :) – Maarten Bodewes Oct 05 '16 at 21:20

1 Answers1

0

Here's what ended up working for me i.e what directly replicates the java class functionality in C#.

Points I had wrong initially:

  • Java uses ECB cipher mode by default, C# uses CBC by default so I needed to explicitly tell C# to use ECB
  • Java's DESKeySpec only uses the first 8 bytes of the given key, so in C# you need to only take the first 8 bytes of the given key
  • How I was writing to the memory stream was also wrong, turns out I HAD to use CryptoStream.FlushFinalBlock to get the desired output

Here's the final code in C#:

public static string EncripytParameters(string prameters)
{
    if (String.IsNullOrEmpty(prameters))
    {
        throw new ArgumentNullException
               ("The string which needs to be encrypted can not be null.");
    }

    byte[] keyBytes = Encoding.UTF8.GetBytes(Key).Take(8).ToArray();

    DES cryptoProvider = new DESCryptoServiceProvider()
    {
        Mode = CipherMode.ECB,
        Key = keyBytes
    };
    var cryptoObject = cryptoProvider.CreateEncryptor();

    MemoryStream memoryStream = new MemoryStream();

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

    byte[] tempArray = Encoding.UTF8.GetBytes(prameters);

    cryptoStream.Write(tempArray, 0, prameters.Length);

    cryptoStream.FlushFinalBlock();

    var outputData = memoryStream.ToArray();

    string encryptedParameters = Convert.ToBase64String(outputData);

    return Uri.EscapeDataString(encryptedParameters);
}
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
fraser jordan
  • 114
  • 1
  • 9
  • Glad you've got it working, but note that single DES encryption has been broken eons ago, and that ECB mode encryption is not secure. Getting a key from a printable string makes things worse. Basically, the original code is crap - migrate away asap. – Maarten Bodewes Oct 05 '16 at 21:01
  • Oh, and crashing the application by providing an UTF-8 string that encodes to 9 or more bytes is priceless too, of course. – Maarten Bodewes Oct 05 '16 at 21:02
  • Yeah all of this crap, but it's also all 3rd party and I have to use it unfortunately, I have let the client know of potential security risks so if they have concerns they can take it up with the 3rd party. I have a feeling the 3rd party won't be keen on changing anything though :( – fraser jordan Oct 05 '16 at 22:47
  • Note: This is how the 3rd party deals with SSO (what I need to implement) to some of their resources, so it's this or nothing really.. – fraser jordan Oct 05 '16 at 22:49