Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Mac.getInstance for HmacSHA1 slow

I'm running the following Java program on a Macbook Air running OSX 10.9.4, 1.7GHz i7, 8GB memory. I have the Java Cryptography Extension (JCE) installed.

import javax.crypto.Mac;

public class Main {
  public static void main(String[] args) throws Exception {
    Mac.getInstance("HmacSHA1");
  }
}

Running this simple program results in a run time of over 5 seconds!

$ javac -version
javac 1.7.0_45
$ javac Main.java
$ java -version
java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)
$ time java Main

real    0m5.326s
user    0m0.390s
sys     0m0.033s

I've done a bunch of searching but haven't found much in the way of workarounds or explanation.

  • Mac.getInstance() for HmacSHA1 taking ages to execute

    This sounds similar to my problem, but all sources I've read indicate that /dev/random and /dev/urandom are the same on OSX.

  • How to solve performance problem with Java SecureRandom?

    Again, discussions about SecureRandom's source of randomness, but doesn't seem to apply to OSX.

Has anyone heard of this problem before? Or know of a way for me to debug what's going on? It's really frustrating to take a 5 second penalty to your unit tests when they've been instantaneous up until now.

Edit: Here's timing inside the program and the list of security providers:

import java.security.Provider;
import java.security.Security;
import javax.crypto.Mac;

public class Main {
  public static void main(String[] args) throws Exception {
    for (Provider p: Security.getProviders()) {
        System.out.println(p.getName() + " " + p.getVersion() + " " + p.getInfo());
    }
    long start = System.currentTimeMillis();
    Mac.getInstance("HmacSHA1");
    System.out.println(System.currentTimeMillis() - start + "ms");
  }
}

$ java Main
SUN 1.7 SUN (DSA key/parameter generation; DSA signing; SHA-1, MD5 digests; SecureRandom; X.509 certificates; JKS keystore; PKIX CertPathValidator; PKIX CertPathBuilder; LDAP, Collection CertStores, JavaPolicy Policy; JavaLoginConfig Configuration)
SunRsaSign 1.7 Sun RSA signature provider
SunEC 1.7 Sun Elliptic Curve provider (EC, ECDSA, ECDH)
SunJSSE 1.7 Sun JSSE provider(PKCS12, SunX509 key/trust factories, SSLv3, TLSv1)
SunJCE 1.7 SunJCE Provider (implements RSA, DES, Triple DES, AES, Blowfish, ARCFOUR, RC2, PBE, Diffie-Hellman, HMAC)
SunJGSS 1.7 Sun (Kerberos v5, SPNEGO)
SunSASL 1.7 Sun SASL provider(implements client mechanisms for: DIGEST-MD5, GSSAPI, EXTERNAL, PLAIN, CRAM-MD5, NTLM; server mechanisms for: DIGEST-MD5, GSSAPI, CRAM-MD5, NTLM)
XMLDSig 1.0 XMLDSig (DOM XMLSignatureFactory; DOM KeyInfoFactory)
SunPCSC 1.7 Sun PC/SC provider
Apple 1.1 Apple Provider
5224ms

Edit 2:

Figured out how to run HPROF on the code.

$ java -agentlib:hprof=cpu=times Main
$ cat java.hprof.txt
...
TRACE 308670:
java.net.InetAddress$1.lookupAllHostAddr(InetAddress.java:Unknown line)
java.net.InetAddress.getAddressesFromNameService(InetAddress.java:Unknown line)
java.net.InetAddress.getLocalHost(InetAddress.java:Unknown line)
javax.crypto.JarVerifier.getSystemEntropy(JarVerifier.java:Unknown line)
...
CPU TIME (ms) BEGIN (total = 6680) Sat Aug 16 05:59:39 2014
rank   self  accum   count trace method
   1 74.87% 74.87%       1 308670 java.net.InetAddress$1.lookupAllHostAddr
...

So, it appears that for some reason the JarVerifier is trying to get entropy from the system and this is causing the program to spend 5 seconds in InetAddress$1.lookupAllHostAddr...

Edit 3:

Updating Java to "1.7.0_67" does not fix the problem.

like image 431
Axle Avatar asked Aug 15 '14 04:08

Axle


1 Answers

I've found a solution. As can be seen in Edit 2, Mac.getInstance() appears to call javax.crypto.JarVerifier.getSystemEntropy(), which eventually calls java.net.InetAddress.getLocalHost(). According to this article, Java 7 changed the way InetAddress looks for local host. For some reason, this causes my machine to lookupAllHostAddr, which takes ~5 seconds to finish.

One of the comments on the article lists the solution which worked for me, which was to add my hostname to /etc/hosts. The following:

127.0.0.1       localhost
255.255.255.255 broadcasthost
::1             localhost
fe80::1%lo0     localhost

changes to

127.0.0.1       localhost     <replace-me>.local
255.255.255.255 broadcasthost
::1             localhost
fe80::1%lo0     localhost

After the change to the hosts file, my time is back to a reasonable 200ms.

$ time java Main

real    0m0.242s
user    0m0.379s
sys 0m0.034s
like image 66
Axle Avatar answered Oct 03 '22 09:10

Axle