1

I'm using this simple class from a tutorial to encrypt a String in my Android app and decrypt it on a Java REST backend:

package classes;


import java.security.NoSuchAlgorithmException;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;


public class MCrypt {

        private String iv = "fedcba9876543210"; 
        private IvParameterSpec ivspec;
        private SecretKeySpec keyspec;
        private Cipher cipher;

        private String SecretKey = "0123456789abcdef";

        public MCrypt()
        {
                ivspec = new IvParameterSpec(iv.getBytes());

                keyspec = new SecretKeySpec(SecretKey.getBytes(), "AES");

                try {
                    cipher = Cipher.getInstance("AES/CBC/NoPadding");
                } catch (NoSuchAlgorithmException e) {
                        e.printStackTrace();
                } catch (NoSuchPaddingException e) {
                        e.printStackTrace();
                }
        }

        public String encrypt(String text) throws Exception
        {
                if(text == null || text.length() == 0)
                        throw new Exception("Empty string");

                byte[] encrypted = null;

                try {
                        cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);

                        encrypted = cipher.doFinal(padString(text).getBytes());
                } catch (Exception e)
                {                       
                        throw new Exception("[encrypt] " + e.getMessage());
                }

                return bytesToHex(encrypted);
        }

        public String decrypt(String code) throws Exception
        {
                if(code == null || code.length() == 0)
                        throw new Exception("Empty string");

                byte[] decrypted = null;

                try {
                        cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);

                        decrypted = cipher.doFinal(hexToBytes(code));

                } catch (Exception e)
                {
                       System.out.println(e.toString());
                }

                return new String(decrypted);
        }



        public static String bytesToHex(byte[] data)
        {
                if (data==null)
                {
                        return null;
                }

                int len = data.length;
                String str = "";
                for (int i=0; i<len; i++) {
                        if ((data[i]&0xFF)<16)
                                str = str + "0" + java.lang.Integer.toHexString(data[i]&0xFF);
                        else
                                str = str + java.lang.Integer.toHexString(data[i]&0xFF);
                }
                return str;
        }


        public static byte[] hexToBytes(String str) {
                if (str==null) {
                        return null;
                } else if (str.length() < 2) {
                        return null;
                } else {
                        int len = str.length() / 2;
                        byte[] buffer = new byte[len];
                        for (int i=0; i<len; i++) {
                                buffer[i] = (byte) Integer.parseInt(str.substring(i*2,i*2+2),16);
                        }
                        return buffer;
                }
        }



        private static String padString(String source)
        {
          char paddingChar = ' ';
          int size = 16;
          int x = source.length() % size;
          int padLength = size - x;

          for (int i = 0; i < padLength; i++)
          {
                  source += paddingChar;
          }

          return source;
        }
}

Its decrypting my String fine. When I send an encrypted String that i tampered with (replaced a character within the encrypted String, the one that goes in the decrypt method as 'String code'), I get an exception (NumberFormatException) when I try to convert the byte[] to a String.

I'm totally new to cryptography and know I need to study the basics.

What I'm wondering is: is this the usual way to check if the encrypted String is valid? Isn't there a boolean method that can check beforehand? If I keep it like this, will the throwing of exceptions have a negative effect on the performance of my backend service?

EDIT: Heres the stacktrace:

java.lang.NumberFormatException: For input string: "zf"
java.lang.NullPointerException
    at java.lang.String.<init>(String.java:601)
    at classes.MCrypt.decrypt(MCrypt.java:71)
    at service.Service.cryptotest(Service.java:340)
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:616)
    at us.antera.t5restfulws.services.impl.RestfulWSDispatcher.invokeMethod(RestfulWSDispatcher.java:133)
    at us.antera.t5restfulws.services.impl.RestfulWSDispatcher.service(RestfulWSDispatcher.java:76)
    at $RequestHandler_15b88bc6f1e4.service(Unknown Source)
    at $RequestHandler_15b88bc6f1d7.service(Unknown Source)
    at org.apache.tapestry5.services.TapestryModule$HttpServletRequestHandlerTerminator.service(TapestryModule.java:253)
    at org.apache.tapestry5.internal.gzip.GZipFilter.service(GZipFilter.java:53)
    at $HttpServletRequestHandler_15b88bc6f1d9.service(Unknown Source)
    at org.apache.tapestry5.internal.services.IgnoredPathsFilter.service(IgnoredPathsFilter.java:62)
    at $HttpServletRequestFilter_15b88bc6f1d5.service(Unknown Source)
    at $HttpServletRequestHandler_15b88bc6f1d9.service(Unknown Source)
    at org.apache.tapestry5.services.TapestryModule$1.service(TapestryModule.java:852)
    at $HttpServletRequestHandler_15b88bc6f1d9.service(Unknown Source)
    at $HttpServletRequestHandler_15b88bc6f1d4.service(Unknown Source)
    at org.apache.tapestry5.TapestryFilter.doFilter(TapestryFilter.java:171)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1212)
    at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:399)
    at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:766)
    at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:450)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at org.mortbay.jetty.Server.handle(Server.java:326)
    at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
    at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:928)
    at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:549)
    at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
    at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
    at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:410)
    at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)

EDIT: Okay, clearly I made some rookie mistakes on my "test scenario" ;). I tried the same call with a valid 16bit hex string and the output of decrypt was some weird looking String, which was what I expected. So, I will first check if the incoming encrypted String is a valid hex at all, then send it to the decrypt method, then check if the decrypted String contains what I expect it to contain. Thanks a lot all of you :)

3
  • That "tampering" is presumably changing it to not being a valid hex string to start with. That's easy enough to check for. What's more interesting is if it's tampered to a different hex value, so that it gets as far as the decryption. Commented Oct 4, 2012 at 15:26
  • Sounds likely that you're replacing a character in the hex output with a non-hex character... Commented Oct 4, 2012 at 15:27
  • As easy as that: "z" is not a valid Hex ... So you might want to repair your "tampering" :) Commented Oct 4, 2012 at 15:28

2 Answers 2

1

So, generally speaking, encryption/decryption doesn't care about the underlying data - that is to say, that any data can be encrypted, and any data can be decrypted.

That said, it looks like you have a confusion about the output format. It isn't a String as such, but binary data encoded into ascii as hex. If you tamper with it without understanding it, you will just end up with an encoding that doesn't make sense. In this case, the tampered hex-string is no longer a hex-string and cannot be decoded back into a byte stream for decryption. A valid hex-string can only contain the characters [0-9a-f]

Edit:

You mention that you are trying to prevent against request forgery. It sounds like you're going about it in a fairly long-winded way, I might be wrong, but if you could explain your scenario and what you're trying to achieve, we might be able to come up with a simpler solution.

Sign up to request clarification or add additional context in comments.

5 Comments

This is basically a follow- up question to this stackoverflow.com/questions/12580063/… one, so I planned on sending some magic String in encrypted form.
You appear to be using AES, which is a block cipher. Does that mean you are encrypting the entire session manually? If so, you'd be much better off just using SSL. If you are just encrypting a 'secret' for use as a credential, be aware that either the secret, or the key, has to change with every request, otherwise you are open to a very basic replay attack.
I would recommend storing your secret (a string of 30 or so random characters) in your app code, and calculating SHA256(request + secret), and sending that hash value alongside each request. This is called an HMAC and is much easier to secure.
I want users to be able to report certain events, even when not signed up, identifying them with thei IMEI. If people file bogus reports, their IMEI is banned. What I planned on doing was making a combined String of the IMEI and my magic String, if the encrypted String contains my magic String, the IMEI should have been sent from my app, not the browser, I guess. So people could not create requests with a fake IMEI. As for the HMAC/ Hash thing: if the server doesnt know the request parameters, how can it check if the hash of (request+secret) is correct?
It's a bit too much to explain in a comment here, but wikipedia has a great article on this here: en.wikipedia.org/wiki/Hash-based_message_authentication_code
0

The problem with your tampering is that the algorithm you are using us not resistant against tampering; it does not provide integrity protection.

The issue with this is that your decryption may either fail (throw an exception) or it may succeed. The output of the decryption may then be compatible with the expected format or it may not. The acceptable format now may contain the correct value or it may not.

The best thing to do is to use a signature, HMAC or authenticated mode of encryption. Then any changes to the ciphertext will lead to an exception, and no garbage can get through to the routines behind the verification of the ciphertext..

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.