2

We are incorporating some encryption into our URL generator and during testing I noticed that the following step makes the program hang for quite some time.

cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(IV.getBytes("UTF-8")));

Here is part of the function in which it is called, once it reaches that line it will simply hang and can take more than 2 minutes to finally pass. Wondering if anyone knows the cause or a solution.

public static String encrypt(String toEncrypt) throws Exception 
  {

        SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");

        Security.addProvider(new BouncyCastleProvider());
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); 

        cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(IV.getBytes("UTF-8")));

        byte[] encrypted = cipher.doFinal(toEncrypt.getBytes());
        byte[] encryptedValue = Base64.encodeBase64(encrypted);

        return new String(encryptedValue);
  }

Thanks,

  • 1
    How big is the data that you encrypt? – Artjom B. Sep 30 '16 at 19:33
  • @ArtjomB. Only like a string 60 characters long. Plus this is before actually encrypting it as we are only setting it up. – Psychosupreme Sep 30 '16 at 19:38
  • How do you know it is blocking on this line? Are you debugging it? If so this can be the culprit (yes, the debuggin itself). Did you try to run it without the debugger? – Antoniossss Sep 30 '16 at 19:55
  • Take a thread dump using `jstack` to see where the execution blocks. Maybe, `/dev/random` entropy pool is exhausted or something. There can be other weird reasons like in [this question](http://stackoverflow.com/questions/38942514/simple-java-program-100-times-slower-after-plugging-in-usb-hotspot). – apangin Sep 30 '16 at 19:58
  • BTW, what OS do you run on? Does the program hang when using default JCE provider instead of BouncyCastleProvider? – apangin Sep 30 '16 at 20:01
  • @Antoniossss Ya this is during debugging. I've also used printlins before and after to make sure of it without debugger. – Psychosupreme Sep 30 '16 at 20:07
  • @apangin Using Windows 7 and ecclipse. This program runs no issues on other computers so wondering if it may be libraries even. Tho even with an import of the project it seems to be just mine. So wondering if there is an actual cause – Psychosupreme Sep 30 '16 at 20:07
  • While it's probably not the cause of the slowness, I'm 99% sure that `IV.getBytes("UTF-8")` cannot be right. For AES in CBC mode, the IV needs to be an array of 16 uniform random bytes. Whatever your `IV` variable is, UTF-8 encoding it is not going to give you a proper CBC IV. – Ilmari Karonen Oct 01 '16 at 13:42
  • Hi @zaph, i think its not possible – Bhaskara Arani Oct 01 '16 at 17:47

1 Answers1

4
Security.addProvider(new BouncyCastleProvider());

So for every string to be encrypted, you create a new security provider. Would you start a new web server for every web request? Would you buy a new computer when you want edit a file?

I'm not claiming that exactly this line is the culprit, but doing all the initialization every time just can't be right.

Drop static (*), use a singleton, ideally from Guice or alike, initialize it once. Your encryption should look like

Fixed according to the comments by @apangin and @IlmariKaronen.

static {
    // This really should be done just once.
    // Moreover, you most probably don't need it.
    Security.addProvider(new BouncyCastleProvider());
}

Encryptor() {
    SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); 
    cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(IVBytes));
}

public synchronized String encrypt(String toEncrypt) throws Exception {
    byte[] encrypted = cipher.doFinal(toEncrypt.getBytes("UTF-8"));
    byte[] encryptedValue = Base64.encodeBase64(encrypted);
    return new String(encryptedValue);
}

As encryption itself is very fast and you're encrypting just short strings, using synchronized should work well. Alternatively, go for a thread-local cipher or alike.

Note that reusing the IV makes it insecure. But that's another story.

(*) That's unrelated to performance, but a good thing.

maaartinus
  • 44,714
  • 32
  • 161
  • 320
  • 1
    You say that doing the initialization every time is not right, but at the same time you call `cipher.init` on each encrypt operation! Cipher initialization may take 10 times longer than the encryption itself, see [the related question](http://stackoverflow.com/questions/35778606/java-get-rid-of-cipher-init-overhead/). – apangin Oct 01 '16 at 14:41
  • @apangin I know, but still it's much faster than initializing everything and *probably fast enough*. And there's no suitable `reset` method, so there's no simple solution... apart from using ECB, which is insecure. – maaartinus Oct 01 '16 at 15:05
  • 1
    [`doFinal`](http://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.html#doFinal-byte:A-) **does reset** cipher to the state it was right after `init` call. – apangin Oct 01 '16 at 17:04
  • [Donald Knuth](https://en.wikipedia.org/wiki/Donald_Knuth) The real problem is that programmers have spent far too much time worrying about efficiency in the wrong places and at the wrong times; premature optimization is the root of all evil (or at least most of it) in programming. – zaph Oct 01 '16 at 18:03
  • @apangin The difference is that initializating the cipher at least makes sense. Adding the same provider over and over again most certainly does not. You can't seriously be suggesting that this answer is incorrect. – user207421 Oct 01 '16 at 18:29
  • @zaph [Donald Knuth](https://en.m.wikiquote.org/wiki/Donald_Knuth) 'Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. *Yet we should not pass up our opportunities in that critical 3%.*' [Emphasis added.] – user207421 Oct 01 '16 at 18:33
  • @EJP Making the 10% faster is a critical opportunity? No, it just makes the code more complicated, adds more code and that combination in general adds more bugs. The real solution is to measure and *then* work on the areas that are really slowing things down. And developers complain about code bloat? – zaph Oct 01 '16 at 19:17