2014-09-23 8 views
20

के साथ धीमी एईएस जीसीएम एन्क्रिप्शन और डिक्रिप्शन मैं एईएस/जीसीएम/नोपैडिंग का उपयोग कर डेटा एन्क्रिप्ट और डिक्रिप्ट करने की कोशिश कर रहा हूं। मैंने जेसीई असीमित शक्ति नीति फ़ाइलों को स्थापित किया और नीचे (सरल दिमागी) बेंचमार्क चलाया। मैंने ओपनएसएसएल का उपयोग करके ऐसा ही किया है और 1 जीबी/एस मेरे पीसी पर एन्क्रिप्शन और डिक्रिप्शन प्राप्त करने में सक्षम था।जावा 8u20

नीचे दिए गए बेंचमार्क के साथ मैं केवल उसी पीसी पर जावा 8 का उपयोग करके 3 एमबी/एस एन्क्रिप्शन और डिक्रिप्शन प्राप्त करने में सक्षम हूं। कोई आइडिया है कि मैं क्या गलत कर रहा हूँ?

public static void main(String[] args) throws Exception { 
    final byte[] data = new byte[64 * 1024]; 
    final byte[] encrypted = new byte[64 * 1024]; 
    final byte[] key = new byte[32]; 
    final byte[] iv = new byte[12]; 
    final Random random = new Random(1); 
    random.nextBytes(data); 
    random.nextBytes(key); 
    random.nextBytes(iv); 

    System.out.println("Benchmarking AES-256 GCM encryption for 10 seconds"); 
    long javaEncryptInputBytes = 0; 
    long javaEncryptStartTime = System.currentTimeMillis(); 
    final Cipher javaAES256 = Cipher.getInstance("AES/GCM/NoPadding"); 
    byte[] tag = new byte[16]; 
    long encryptInitTime = 0L; 
    long encryptUpdate1Time = 0L; 
    long encryptDoFinalTime = 0L; 
    while (System.currentTimeMillis() - javaEncryptStartTime < 10000) { 
     random.nextBytes(iv); 
     long n1 = System.nanoTime(); 
     javaAES256.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(16 * Byte.SIZE, iv)); 
     long n2 = System.nanoTime(); 
     javaAES256.update(data, 0, data.length, encrypted, 0); 
     long n3 = System.nanoTime(); 
     javaAES256.doFinal(tag, 0); 
     long n4 = System.nanoTime(); 
     javaEncryptInputBytes += data.length; 

     encryptInitTime = n2 - n1; 
     encryptUpdate1Time = n3 - n2; 
     encryptDoFinalTime = n4 - n3; 
    } 
    long javaEncryptEndTime = System.currentTimeMillis(); 
    System.out.println("Time init (ns): "  + encryptInitTime); 
    System.out.println("Time update (ns): " + encryptUpdate1Time); 
    System.out.println("Time do final (ns): " + encryptDoFinalTime); 
    System.out.println("Java calculated at " + (javaEncryptInputBytes/1024/1024/((javaEncryptEndTime - javaEncryptStartTime)/1000)) + " MB/s"); 

    System.out.println("Benchmarking AES-256 GCM decryption for 10 seconds"); 
    long javaDecryptInputBytes = 0; 
    long javaDecryptStartTime = System.currentTimeMillis(); 
    final GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(16 * Byte.SIZE, iv); 
    final SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); 
    long decryptInitTime = 0L; 
    long decryptUpdate1Time = 0L; 
    long decryptUpdate2Time = 0L; 
    long decryptDoFinalTime = 0L; 
    while (System.currentTimeMillis() - javaDecryptStartTime < 10000) { 
     long n1 = System.nanoTime(); 
     javaAES256.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec); 
     long n2 = System.nanoTime(); 
     int offset = javaAES256.update(encrypted, 0, encrypted.length, data, 0); 
     long n3 = System.nanoTime(); 
     javaAES256.update(tag, 0, tag.length, data, offset); 
     long n4 = System.nanoTime(); 
     javaAES256.doFinal(data, offset); 
     long n5 = System.nanoTime(); 
     javaDecryptInputBytes += data.length; 

     decryptInitTime += n2 - n1; 
     decryptUpdate1Time += n3 - n2; 
     decryptUpdate2Time += n4 - n3; 
     decryptDoFinalTime += n5 - n4; 
    } 
    long javaDecryptEndTime = System.currentTimeMillis(); 
    System.out.println("Time init (ns): " + decryptInitTime); 
    System.out.println("Time update 1 (ns): " + decryptUpdate1Time); 
    System.out.println("Time update 2 (ns): " + decryptUpdate2Time); 
    System.out.println("Time do final (ns): " + decryptDoFinalTime); 
    System.out.println("Total bytes processed: " + javaDecryptInputBytes); 
    System.out.println("Java calculated at " + (javaDecryptInputBytes/1024/1024/((javaDecryptEndTime - javaDecryptStartTime)/1000)) + " MB/s"); 
} 

संपादित करें: मैं इस सरल दिमाग बेंचमार्क सुधार करने के लिए एक मजेदार व्यायाम के रूप में छोड़।

मैं कुछ और अधिक ServerVM का उपयोग कर परीक्षण किया है, हटाया nanoTime कॉल करता है और शुरू की वार्मअप, लेकिन जैसा कि मैंने उम्मीद इस में से कोई भी बेंचमार्क परिणामों पर किसी भी सुधार किया था। यह 3 मेगाबाइट प्रति सेकंड पर फ्लैट-रेखांकित है।

+5

सबसे पहले, बेंचमार्किंग गलत है: कोई गर्मजोशी, एकल पुनरावृत्ति, अत्यधिक नैनो टाइम कॉल। एईएस-एनआई के लिए हॉटस्पॉट के इंट्रिनिक्स का उपयोग केवल एक अनुकूलित जेआईटी कंपाइलर के साथ किया जाता है, आपको प्रदर्शन का आकलन करने से पहले वहां पहुंचना होगा। दूसरा, एईएस/सीबीसी आज़माएं। क्या आप वास्तव में ओपनएसएसएल के साथ एईएस-जीसीएम मापते हैं, और यह आपको 1 जीबी/एस देता है? –

+1

यह भी ध्यान दें कि एईएस-एनआई इंट्रिनिक्स का उपयोग करने के लिए इसे सर्वर वीएम, समर्थन के साथ एक आधुनिक इंटेल सीपीयू का उपयोग करने की आवश्यकता है, * और * एक गर्मजोशी अनुक्रम है। ध्यान दें कि OpenSSL वहां सबसे तेज़ libs में से एक है, बाइट कोड व्यवसाय तर्क के लिए अपेक्षाकृत तेज़ हो सकता है, लेकिन क्रिप्टोग्राफ़ी के लिए * * अच्छी तरह से लागू सी/सी ++ पुस्तकालयों के साथ अंतर दिखाई देगा। –

+0

हां, मुझे पता है कि यह सबसे मजबूत बेंचमार्क नहीं है, लेकिन 3 एमबी/एस बनाम 1 जीबी/एस अभी भी बहुत महत्वपूर्ण है और मुझे लगता है कि यह सरल बेंचमार्क बिंदु को लाने के लिए पर्याप्त है। मैंने एईएस/सीबीसी की कोशिश की है और मैं एन्क्रिप्शन के लिए 400 एमबी/एस और जावा के सिफर का उपयोग करके डिक्रिप्शन के लिए 1 जीबी/एस से अधिक प्राप्त करने में सक्षम हूं। – Christo

उत्तर

18

माइक्रो बेंचमार्किंग एक तरफ, JDK 8 में GCM कार्यान्वयन (कम से कम 1.8.0_25 तक) के प्रदर्शन को अपंग है।

मैं लगातार पुन: पेश कर सकते हैं एक और अधिक परिपक्व सूक्ष्म बेंचमार्क के साथ 3MB/s (एक Haswell i7 लैपटॉप पर)।

code dive से, यह एक बेवकूफ गुणक कार्यान्वयन और जीसीएम गणनाओं के लिए कोई हार्डवेयर त्वरण के कारण प्रतीत होता है।

तुलनात्मक रूप से जेडीके 8 में एईएस (ईसीबी या सीबीसी मोड में) एक एईएस-एनआई त्वरित आंतरिक का उपयोग करता है और (जावा के लिए कम से कम) बहुत तेज़ (उसी हार्डवेयर पर 1 जीबी/एस के क्रम में) होता है, लेकिन समग्र एईएस/जीसीएम प्रदर्शन पूरी तरह से टूटे हुए जीसीएम प्रदर्शन का प्रभुत्व है।

plans to implement hardware acceleration, और there have been third party submissions to improve the performance with हैं, लेकिन इन्हें अभी तक रिलीज़ नहीं किया गया है।

इस बारे में कुछ और पता होना चाहिए कि जेडीके जीसीएम कार्यान्वयन भी पूरे सादे टेक्स्ट को डिक्रिप्शन पर बफर करता है जब तक कि सिफरटेक्स्ट के अंत में प्रमाणीकरण टैग सत्यापित नहीं होता है, जो इसे बड़े संदेशों के उपयोग के लिए तैयार करता है।

Bouncy कैसल तेजी GCM कार्यान्वयन (और OCB आप सॉफ्टवेयर पेटेंट कानूनों द्वारा भारग्रस्त नहीं की ओपन सोर्स सॉफ्टवेयर लेखन कर रहे हैं) (लेखन के समय) है।


जुलाई 2015 Updated - 1.8.0_45 और JDK 9

JDK 8 होगा एक बेहतर (और निरंतर समय) जावा कार्यान्वयन (RedHat के फ्लोरियन वाईमर के योगदान) मिलता है - इस JDK 9 में उतरा है ईए बनाता है, लेकिन स्पष्ट रूप से अभी तक 1.8.0_45 में नहीं है। जेडीके 9 (कम से कम ईए बी 72) में जीसीएम इंट्रिनिक्स भी है - बी 72 पर एईएस/जीसीएम गति इंट्रिनिक्स सक्षम किए बिना 18 एमबी/एस है और 25 एमबी/एस इंट्रिनिक्स सक्षम है, जिनमें से दोनों निराशाजनक हैं - तुलना करने के लिए सबसे तेज़ (निरंतर समय नहीं) बीसी कार्यान्वयन ~ 60 एमबी/एस है और सबसे धीमा (स्थिर समय, पूरी तरह अनुकूल नहीं है) ~ 26 एमबी/एस है।


जनवरी 2016 - 1.8 अपडेट किया गया।0_72:

कुछ प्रदर्शन फिक्स जेडीके 1.8.0_60 में उतरा और उसी बेंचमार्क पर प्रदर्शन अब 18 एमबी/एस - मूल से 6x सुधार है, लेकिन बीसी कार्यान्वयन की तुलना में अभी भी धीमा है।

+0

बाउंसी पर जीसीएम डिक्रिप्शन के दौरान टैग आकार को कैश करता है (क्योंकि इसे सिफरटेक्स्ट के हिस्से के रूप में उपयोग किया जाता है), हालांकि मैं इसे बाहर निकालने के लिए नया कोड आज़मा रहा हूं (और टैग को अलग से अनुरोध करें, जैसा कि यह होना चाहिए)। बाद में एएडी को जोड़ने की अनुमति देने के लिए आवश्यक एक्सपोनेंटिएशन को फिर से लिखना काफी दर्द है। –

+0

जावा 8 पर कोई अपडेट? – kellyfj

+0

मैंने 84 फाइलों को एन्क्रिप्ट/डिक्रिप्ट करने के लिए कुछ परीक्षण किया है। जेडीके लागू: क्रिप्ट = 15 सेकंड/डिक्रिप्ट = 111 सेकंड। बीसी: क्रिप्ट = 14 सेकंड/डिक्रिप्ट = 23 सेकंड !!!यह कैसे संभव है कि मूल जेडीके बीसी की तुलना में डिक्रिप्शन इतना धीमा हो? बीसी सिर्फ एक जार फ़ाइल है जिसमें केवल क्लास फाइलें हैं (इसलिए जावा भाषा फ़ाइल)। ओरेकल (सूर्य) के पास जेआरई/जेडीके के साथ देशी ओएस तक पहुंच है, इसलिए वे इन सभी चीजों को निम्न स्तर और तेज भाषा में कार्यान्वित कर सकते हैं और अपने कोड को अनुकूलित कर सकते हैं (जैसे सी में)! यह मेरे लिए समझ में नहीं आता है:/ – Alexxx

3

अब इसे आंशिक रूप से जावा 8u60 में JDK-8069072 के साथ संबोधित किया गया है। इस फिक्स के बिना मुझे 2.5 एम/एस मिलता है। इस फिक्स के साथ मुझे 25 एम/एस मिलता है। जीसीएम को अक्षम करने से मुझे 60 एम/एस मिल जाता है।

jdk.tls.disabledAlgorithms=SSLv3,GCM 

फिर साथ अपने जावा प्रक्रिया शुरू:

GCM पूरी तरह से निम्न पंक्ति के साथ java.security नाम की एक फ़ाइल बनाने को अक्षम करने के

java -Djava.security.properties=/path/to/my/java.security ... 

अगर यह काम नहीं करता, तो आप की आवश्यकता हो सकती /usr/java/default/jre/lib/security/java.security संपादित करके सुरक्षा गुणों को ओवरराइड करने में सक्षम करें (वास्तविक पथ ओएस के आधार पर अलग हो सकता है) और जोड़ना:

policy.allowSystemProperty=true 
+0

मैंने मुख्य java.security खोला और saferandom.source = file:/dev/random -> securerandom.source = file:/dev/urandom बदल दिया। और यह मेरे लिए मदद मिली – demaksee

+0

यादृच्छिक/urandom के बारे में कुछ विवरण के लिए यहां देखें http://stackoverflow.com/questions/21757653/cipher-getinstance-is-too-slow#comment32942702_21757854 – demaksee

0

ओपनएसएसएल कार्यान्वयन को assembly routine द्वारा pclmulqdq निर्देश (x86 प्लेटफ़ॉर्म) का उपयोग करके अनुकूलित किया गया है। यह समान एल्गोरिदम के कारण बहुत तेज़ है।

जावा कार्यान्वयन धीमा है। लेकिन यह असेंबली दिनचर्या (समान नहीं) का उपयोग कर हॉटस्पॉट में भी अनुकूलित किया गया था। आपको हॉटस्पॉट आंतरिक का उपयोग करने के लिए जेवीएम को गर्म करना होगा। -XX का डिफ़ॉल्ट मान: CompileThreshold 10000

// स्यूडोकोड

warmUp_GCM_cipher_loop10000_times है();

do_benchmark();