जावा एईएस / सीबीसी डिक्रिप्शन के बाद प्रारंभिक बाइट्स गलत


116

निम्नलिखित उदाहरण में क्या गलत है?

समस्या यह है कि डिक्रिप्टेड स्ट्रिंग का पहला भाग बकवास है। हालांकि, बाकी सब ठीक है, मुझे मिलता है ...

Result: eB6OgeS��i are you? Have a nice day.
@Test
public void testEncrypt() {
  try {
    String s = "Hello there. How are you? Have a nice day.";

    // Generate key
    KeyGenerator kgen = KeyGenerator.getInstance("AES");
    kgen.init(128);
    SecretKey aesKey = kgen.generateKey();

    // Encrypt cipher
    Cipher encryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    encryptCipher.init(Cipher.ENCRYPT_MODE, aesKey);

    // Encrypt
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, encryptCipher);
    cipherOutputStream.write(s.getBytes());
    cipherOutputStream.flush();
    cipherOutputStream.close();
    byte[] encryptedBytes = outputStream.toByteArray();

    // Decrypt cipher
    Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    IvParameterSpec ivParameterSpec = new IvParameterSpec(aesKey.getEncoded());
    decryptCipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);

    // Decrypt
    outputStream = new ByteArrayOutputStream();
    ByteArrayInputStream inStream = new ByteArrayInputStream(encryptedBytes);
    CipherInputStream cipherInputStream = new CipherInputStream(inStream, decryptCipher);
    byte[] buf = new byte[1024];
    int bytesRead;
    while ((bytesRead = cipherInputStream.read(buf)) >= 0) {
        outputStream.write(buf, 0, bytesRead);
    }

    System.out.println("Result: " + new String(outputStream.toByteArray()));

  } 
  catch (Exception ex) {
    ex.printStackTrace();
  }
}

48
गंभीर परियोजना में इस सवाल का कोई जवाब नहीं है! इस प्रश्न में दिए गए सभी उदाहरण पैडिंग ओरेकल के लिए कमजोर हैं और कुल मिलाकर बहुत क्रिप्टोग्राफी उपयोग हैं। आप नीचे दिए गए किसी भी स्निपेट का उपयोग करके अपनी परियोजना में गंभीर क्रिप्टोग्राफी भेद्यता का परिचय देंगे।
HoLyVieR 19

16
@HoLyVieR, निम्नलिखित उद्धरणों के बारे में: "आपको अपनी स्वयं की क्रिप्टोग्राफी लाइब्रेरी विकसित नहीं करनी चाहिए" और "एक उच्च स्तरीय एपीआई का उपयोग करें जो आपकी रूपरेखा प्रदान करता है।" यहां कोई भी अपनी खुद की क्रिप्टोग्राफी लाइब्रेरी विकसित नहीं कर रहा है। हम केवल पहले से ही मौजूद उच्च स्तरीय एपीआई का उपयोग कर रहे हैं जो जावा फ्रेमवर्क प्रदान करता है। आप साहब बेतहाशा गलत हैं।
k170

10
@MaartenBodewes, सिर्फ इसलिए कि आप दोनों सहमत नहीं हैं कि आप दोनों सही हैं। अच्छे डेवलपर्स उच्च स्तर के एपीआई को लपेटने और निम्न स्तर के एपीआई को फिर से लिखने के बीच के अंतर को जानते हैं। अच्छे पाठकों का ध्यान जाएगा कि ओपी ने "सरल जावा एईएस एनक्रिप्ट / डिक्रिप्ट उदाहरण" के लिए कहा और यही उसे मिला । मैं दूसरे जवाबों से भी सहमत नहीं हूं, यही वजह है कि मैंने खुद का जवाब पोस्ट किया। शायद आप लोग भी यही कोशिश करें और अपनी विशेषज्ञता से हम सभी को बताएं।
k170

6
@HoLyVieR यह वास्तव में सबसे बेतुकी बात है जो मैंने कभी एसओ पर पढ़ी है! आप कौन हैं जो लोगों को बता सकते हैं कि वे क्या कर सकते हैं और विकसित नहीं कर सकते हैं?
टेडट्रिपिन

14
मुझे अभी भी @HoLyVieR कोई उदाहरण नहीं दिखता है। चलो पुस्तकालयों के लिए कुछ, या संकेत देखते हैं? रचनात्मक बिल्कुल नहीं।
danieljimenez

जवाबों:


245

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

आशा है कि यह आप सभी के लिए उपयोगी होगा: संकलन करने के लिए आपको अतिरिक्त Apache Commons Codec jar की आवश्यकता है, जो यहाँ उपलब्ध है: http://commons.apache.org/proper/commons-codec/download_codec.cgi

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

import org.apache.commons.codec.binary.Base64;

public class Encryptor {
    public static String encrypt(String key, String initVector, String value) {
        try {
            IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);

            byte[] encrypted = cipher.doFinal(value.getBytes());
            System.out.println("encrypted string: "
                    + Base64.encodeBase64String(encrypted));

            return Base64.encodeBase64String(encrypted);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;
    }

    public static String decrypt(String key, String initVector, String encrypted) {
        try {
            IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);

            byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));

            return new String(original);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;
    }

    public static void main(String[] args) {
        String key = "Bar12345Bar12345"; // 128 bit key
        String initVector = "RandomInitVector"; // 16 bytes IV

        System.out.println(decrypt(key, initVector,
                encrypt(key, initVector, "Hello World")));
    }
}

47
यदि आप 3rd पार्टी अपाचे कॉमन्स कोडेक लाइब्रेरी पर निर्भर नहीं होना चाहते हैं, तो आप बेस 64 एन्कोडिंग / डिकोडिंग करने के लिए JDK की javax.xml.bind.DatatypeConverter का उपयोग कर सकते हैं : System.out.println("encrypted string:" + DatatypeConverter.printBase64Binary(encrypted)); byte[] original = cipher.doFinal(DatatypeConverter.parseBase64Binary(encrypted));
curd0

8
आप एक निरंतर IV का उपयोग कर रहे हैं ?!
vianna77

36
जावा 8 में पहले से ही बेस 64 टूल हैं: java.util.Base64.getDecoder () और java.util.Base64.getEncoder ()
हिस्टो स्टोयनोव

11
IV को गुप्त नहीं होना चाहिए, लेकिन इसे CBC मोड (और CTR के लिए अद्वितीय) के लिए अप्रत्याशित होना चाहिए। इसे सिफरटेक्स्ट के साथ भेजा जा सकता है। ऐसा करने का एक सामान्य तरीका IV को सिफरटेक्स्ट से प्रीफ़िक्स करना और डिक्रिप्शन से पहले इसे स्लाइस करना है। इसके माध्यम से उत्पन्न किया जाना चाहिएSecureRandom
आर्टजोम बी

6
पासवर्ड कुंजी नहीं है। एक IV यादृच्छिक होना चाहिए।
Maarten Bodewes

40

यहाँ के बिना एक समाधान Apache Commons Codec's Base64:

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

public class AdvancedEncryptionStandard
{
    private byte[] key;

    private static final String ALGORITHM = "AES";

    public AdvancedEncryptionStandard(byte[] key)
    {
        this.key = key;
    }

    /**
     * Encrypts the given plain text
     *
     * @param plainText The plain text to encrypt
     */
    public byte[] encrypt(byte[] plainText) throws Exception
    {
        SecretKeySpec secretKey = new SecretKeySpec(key, ALGORITHM);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);

        return cipher.doFinal(plainText);
    }

    /**
     * Decrypts the given byte array
     *
     * @param cipherText The data to decrypt
     */
    public byte[] decrypt(byte[] cipherText) throws Exception
    {
        SecretKeySpec secretKey = new SecretKeySpec(key, ALGORITHM);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, secretKey);

        return cipher.doFinal(cipherText);
    }
}

उपयोग उदाहरण:

byte[] encryptionKey = "MZygpewJsCpRrfOr".getBytes(StandardCharsets.UTF_8);
byte[] plainText = "Hello world!".getBytes(StandardCharsets.UTF_8);
AdvancedEncryptionStandard advancedEncryptionStandard = new AdvancedEncryptionStandard(
        encryptionKey);
byte[] cipherText = advancedEncryptionStandard.encrypt(plainText);
byte[] decryptedCipherText = advancedEncryptionStandard.decrypt(cipherText);

System.out.println(new String(plainText));
System.out.println(new String(cipherText));
System.out.println(new String(decryptedCipherText));

प्रिंटों:

Hello world!
դ;��LA+�ߙb*
Hello world!

5
यह पूरी तरह से एक आदर्श उदाहरण है, सिर्फ @ chandpriyankara के रूप में। लेकिन क्यों encrypt(String)और नहीं के एक हस्ताक्षर को परिभाषित encrypt(byte[] )? एन्क्रिप्शन (डिक्रिप्शन भी) एक बाइट आधारित प्रक्रिया है (एईएस वैसे भी है)। एन्क्रिप्शन बाइट्स इनपुट के रूप में लेता है, और बाइट्स आउटपुट करता है, इसलिए डिक्रिप्शन (मामले में मामला: Cipherऑब्जेक्ट करता है)। अब, एक विशेष उपयोग के मामले में एक स्ट्रिंग से आने वाले एन्क्रिप्टेड बाइट्स हो सकते हैं, या एक स्ट्रिंग के रूप में भेजे जा सकते हैं (मेल के लिए बेस 64 माइम लगाव) ..., लेकिन यह एन्कोडिंग बाइट्स का एक मुद्दा है, जिसके लिए सैकड़ों मौजूद हैं समाधान, एईएस / एन्क्रिप्शन के लिए पूरी तरह से असंबंधित।
जीपीआई

3
@ जीपीआई: हां, लेकिन मुझे यह अधिक उपयोगी लगता है Stringsक्योंकि यह मूल रूप से मैं 95% समय के साथ काम करता हूं और आप किसी भी तरह से परिवर्तित होते हैं।
बुलीविपलाजा

9
नहीं, यह chandpriyankara के कोड के बराबर नहीं है! आपका कोड ईसीबी का उपयोग करता है जो आम तौर पर असुरक्षित है और नहीं चाहता था। सीबीसी को स्पष्ट रूप से निर्दिष्ट करना चाहिए। जब सीबीसी निर्दिष्ट किया जाता है, तो आपका कोड टूट जाएगा।
Dan

पूरी तरह कार्यात्मक, पूरी तरह असुरक्षित, और बहुत खराब प्रोग्रामिंग प्रथाओं का उपयोग करना। कक्षा को बुरी तरह नाम दिया गया है। कुंजी आकार अग्रिम में जाँच नहीं किया गया है। लेकिन सबसे महत्वपूर्ण बात यह है कि कोड असुरक्षित ईसीबी मोड का उपयोग करता है, मूल प्रश्न में समस्या को छिपाता है । अंत में, यह एक वर्ण एन्कोडिंग निर्दिष्ट नहीं करता है, जिसका अर्थ है कि पाठ के लिए डिकोडिंग अन्य प्लेटफार्मों पर विफल हो सकती है।
मार्टेन बोदेस

24

मुझे लगता है जैसे आप अपने आरम्भिक वेक्टर (IV) के साथ ठीक से व्यवहार नहीं कर रहे हैं। एईएस, आईवीएस और ब्लॉक चेनिंग के बारे में मैंने पिछली बार पढ़ा था, लेकिन आपकी लाइन को एक लंबा समय हो गया है

IvParameterSpec ivParameterSpec = new IvParameterSpec(aesKey.getEncoded());

ठीक नहीं लगता। एईएस के मामले में, आप प्रारंभिक वेक्टर को एक सिफर उदाहरण की "प्रारंभिक स्थिति" के रूप में सोच सकते हैं, और यह स्थिति थोड़ी सी जानकारी है जिसे आप अपनी कुंजी से नहीं बल्कि एन्क्रिप्टिंग सिफर की वास्तविक गणना से प्राप्त कर सकते हैं। (कोई यह तर्क दे सकता है कि यदि IV को कुंजी से निकाला जा सकता है, तो इसका कोई फायदा नहीं होगा, क्योंकि कुंजी पहले से ही इसके अंत चरण के दौरान सिफर उदाहरण को दी गई है)।

इसलिए, आपको अपने एन्क्रिप्शन के अंत में सिफर उदाहरण से आईवी को बाइट के रूप में प्राप्त करना चाहिए

  cipherOutputStream.close();
  byte[] iv = encryptCipher.getIV();

और आप अपने को प्रारंभ करना चाहिए Cipherमें DECRYPT_MODEइस बाइट के साथ []:

  IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);

फिर, आपका डिक्रिप्शन ठीक होना चाहिए। उम्मीद है की यह मदद करेगा।


एक नौसिखिया की मदद करने के लिए धन्यवाद। मैंने इस उदाहरण को अन्य पदों से हटा दिया। मुझे नहीं लगता कि आपको पता है कि IV की आवश्यकता से कैसे बचें? मैंने देखा है, लेकिन कोशिश नहीं की, अन्य एईएस उदाहरण जो इसका उपयोग नहीं करते हैं।
टेडट्रिपिन

उस पर ध्यान न दें, मुझे जवाब मिल गया है! मुझे AES / ECB / PKCS5Padding का उपयोग करने की आवश्यकता है।
टेड्रिपिन

20
ज्यादातर बार आप ईसीबी का उपयोग नहीं करना चाहते हैं। बस गूगल क्यों।
जोआओ फर्नांडिस

2
@ मुशी: इस बात से सहमत है कि एक विश्वसनीय यादृच्छिक स्रोत से, IV को चुनने और स्पष्ट रूप से सेट करने से बेहतर है कि सिर्फ Cihper इंस्टेंस को लेने दें। दूसरी ओर, यह उत्तर कुंजी के लिए आरंभीकरण वेक्टर को भ्रमित करने के मूल मुद्दे को स्वीकार करता है। यही वजह है कि इसे पहली बार में उखाड़ा गया। अब, यह पोस्ट एक नमूना कोड गो-टू-पॉइंट के रूप में अधिक हो गया है, और यहां लोगों ने कुछ महान छूट दी है - बस मूल प्रश्न के बारे में क्या था।
जीपीआई

3
@ जीपीआई अपवोटेड। दूसरे "महान उदाहरण" वे महान नहीं हैं, और वे वास्तव में प्रश्न को संबोधित नहीं करते हैं। इसके बजाय ऐसा लगता है कि newbies के लिए क्रिप्टोग्राफिक नमूनों को नेत्रहीन रूप से कॉपी करने के लिए जगह है यह समझे बिना कि सुरक्षा के संभावित मुद्दे हो सकते हैं - और, हमेशा की तरह, वहाँ हैं।
मार्टन बोदवेस

17

आपके द्वारा डिक्रिप्शन के लिए उपयोग किया जाने वाला IV गलत है। इस कोड को बदलें

//Decrypt cipher
Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec(aesKey.getEncoded());
decryptCipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);

इस कोड के साथ

//Decrypt cipher
Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec(encryptCipher.getIV());
decryptCipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);

और इससे आपकी समस्या का समाधान होना चाहिए।


नीचे जावा में एक साधारण एईएस वर्ग का एक उदाहरण शामिल है। मैं उत्पादन वातावरण में इस वर्ग का उपयोग करने की अनुशंसा नहीं करता, क्योंकि यह आपके आवेदन की सभी विशिष्ट आवश्यकताओं के लिए जिम्मेदार नहीं हो सकता है।

import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

public class AES 
{
    public static byte[] encrypt(final byte[] keyBytes, final byte[] ivBytes, final byte[] messageBytes) throws InvalidKeyException, InvalidAlgorithmParameterException
    {       
        return AES.transform(Cipher.ENCRYPT_MODE, keyBytes, ivBytes, messageBytes);
    }

    public static byte[] decrypt(final byte[] keyBytes, final byte[] ivBytes, final byte[] messageBytes) throws InvalidKeyException, InvalidAlgorithmParameterException
    {       
        return AES.transform(Cipher.DECRYPT_MODE, keyBytes, ivBytes, messageBytes);
    }

    private static byte[] transform(final int mode, final byte[] keyBytes, final byte[] ivBytes, final byte[] messageBytes) throws InvalidKeyException, InvalidAlgorithmParameterException
    {
        final SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
        final IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
        byte[] transformedBytes = null;

        try
        {
            final Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");

            cipher.init(mode, keySpec, ivSpec);

            transformedBytes = cipher.doFinal(messageBytes);
        }        
        catch (NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException e) 
        {
            e.printStackTrace();
        }
        return transformedBytes;
    }

    public static void main(final String[] args) throws InvalidKeyException, InvalidAlgorithmParameterException
    {
        //Retrieved from a protected local file.
        //Do not hard-code and do not version control.
        final String base64Key = "ABEiM0RVZneImaq7zN3u/w==";

        //Retrieved from a protected database.
        //Do not hard-code and do not version control.
        final String shadowEntry = "AAECAwQFBgcICQoLDA0ODw==:ZtrkahwcMzTu7e/WuJ3AZmF09DE=";

        //Extract the iv and the ciphertext from the shadow entry.
        final String[] shadowData = shadowEntry.split(":");        
        final String base64Iv = shadowData[0];
        final String base64Ciphertext = shadowData[1];

        //Convert to raw bytes.
        final byte[] keyBytes = Base64.getDecoder().decode(base64Key);
        final byte[] ivBytes = Base64.getDecoder().decode(base64Iv);
        final byte[] encryptedBytes = Base64.getDecoder().decode(base64Ciphertext);

        //Decrypt data and do something with it.
        final byte[] decryptedBytes = AES.decrypt(keyBytes, ivBytes, encryptedBytes);

        //Use non-blocking SecureRandom implementation for the new IV.
        final SecureRandom secureRandom = new SecureRandom();

        //Generate a new IV.
        secureRandom.nextBytes(ivBytes);

        //At this point instead of printing to the screen, 
        //one should replace the old shadow entry with the new one.
        System.out.println("Old Shadow Entry      = " + shadowEntry);
        System.out.println("Decrytped Shadow Data = " + new String(decryptedBytes, StandardCharsets.UTF_8));
        System.out.println("New Shadow Entry      = " + Base64.getEncoder().encodeToString(ivBytes) + ":" + Base64.getEncoder().encodeToString(AES.encrypt(keyBytes, ivBytes, decryptedBytes)));
    }
}

ध्यान दें कि एईएस का एन्कोडिंग से कोई लेना-देना नहीं है, यही वजह है कि मैंने इसे अलग से और किसी भी तीसरे पक्ष के पुस्तकालयों की आवश्यकता के बिना इसे संभालने के लिए चुना।


सबसे पहले, आपने मूल प्रश्न का उत्तर दिया। दूसरा, आपने पहले से ही उत्तर दिया, अच्छी तरह से स्वीकार किए गए प्रश्न का उत्तर क्यों दे रहे हैं? मुझे लगा कि इस स्पैम को रोकने के लिए सुरक्षा चाहिए थी।
टेडट्रिपिन

14
स्वीकृत उत्तर की तरह, मैंने उदाहरण के माध्यम से आपके प्रश्न का उत्तर देना चुना। मैंने एक पूरी तरह से कार्यात्मक कोड प्रदान किया, जो आपको दिखाता है कि अन्य चीजों के साथ प्रारंभिक वेक्टर के साथ कैसे ठीक से व्यवहार करें। आपके दूसरे प्रश्न के रूप में, मुझे लगा कि अपाचे कोडेक अब आवश्यक नहीं है क्योंकि एक अद्यतन उत्तर की आवश्यकता थी। तो नहीं यह स्पैम नहीं है। ट्रिपिन को रोकें।
k170

7
एक IV का एक विशिष्ट उद्देश्य है जो सिफरटेक्स्ट को बेतरतीब करना और सिमेंटिक सुरक्षा प्रदान करना है। यदि आप एक ही कुंजी + IV जोड़ी का उपयोग करते हैं तो हमलावर यह निर्धारित कर सकते हैं कि क्या आपने पहले जैसा ही उपसर्ग के साथ संदेश भेजा है। IV को गुप्त नहीं होना चाहिए, लेकिन इसे अप्रत्याशित होना चाहिए। एक सामान्य तरीका यह है कि चतुर्थांश को सिफरटेक्स्ट से उपसर्ग करें और डिक्रिप्शन से पहले इसे स्लाइस करें।
आर्टजोम बी।

4
डाउनवोट: हार्डकोड IV,
आर्टजोम

1
CTR मोड को NoPadding के साथ जोड़ा जाना चाहिए। CBC के बजाय CTR मोड निश्चित रूप से आवश्यक नहीं है (जब तक गद्दी oracles लागू नहीं होती है), लेकिन यदि CTR का उपयोग किया जाता है, तो उपयोग करें "/NoPadding"। CTR एक ऐसी विधा है जो AES को एक स्ट्रीम सिफर में बदल देती है, और एक स्ट्रीम सिफर ब्लॉक के बजाय बाइट्स पर कार्य करता है।
मार्टेन बोदवेस

16

इस उत्तर में, मैं "सिंपल जावा एईएस एनक्रिप्ट / डिक्रिप्ट उदाहरण" मुख्य विषय पर नहीं बल्कि विशिष्ट डिबगिंग प्रश्न का चयन करना चाहता हूं क्योंकि मुझे लगता है कि इससे अधिकांश पाठकों को लाभ होगा।

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

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

एईएस-जीसीएम एन्क्रिप्शन / डिक्रिप्शन ट्यूटोरियल

यहां एईएस-जीसीएम के साथ एन्क्रिप्ट / डिक्रिप्ट करने के लिए आवश्यक कदम हैं जावा क्रिप्टोग्राफी आर्किटेक्चर (जेसीए) के साथ । अन्य उदाहरणों के साथ मिश्रण न करें , क्योंकि सूक्ष्म अंतर आपके कोड को पूरी तरह असुरक्षित बना सकते हैं।

1. कुंजी बनाएँ

जैसा कि यह आपके उपयोग-मामले पर निर्भर करता है, मैं सबसे सरल मामला मानूंगा: एक यादृच्छिक गुप्त कुंजी।

SecureRandom secureRandom = new SecureRandom();
byte[] key = new byte[16];
secureRandom.nextBytes(key);
SecretKey secretKey = SecretKeySpec(key, "AES");

जरूरी:

2. इनिशियलाइज़ेशन वेक्टर बनाएं

एक इनिशियलाइज़ेशन वेक्टर (IV) का उपयोग किया जाता है ताकि एक ही गुप्त कुंजी विभिन्न सिफर ग्रंथों का निर्माण करे

byte[] iv = new byte[12]; //NEVER REUSE THIS IV WITH SAME KEY
secureRandom.nextBytes(iv);

जरूरी:

3. IV और कुंजी के साथ एन्क्रिप्ट करें

final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv); //128 bit auth tag length
cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);
byte[] cipherText = cipher.doFinal(plainText);

जरूरी:

  • 16 बाइट / 128 बिट प्रमाणीकरण टैग का उपयोग करें (अखंडता / प्रामाणिकता को सत्यापित करने के लिए उपयोग किया जाता है)
  • प्रमाणीकरण टैग सिफर पाठ (JCA कार्यान्वयन में) से स्वचालित रूप से जोड़ा जाएगा
  • चूंकि GCM एक स्ट्रीम सिफर की तरह व्यवहार करता है, इसलिए किसी प्रकार की पैडिंग की आवश्यकता नहीं है
  • CipherInputStreamडेटा का बड़ा हिस्सा एन्क्रिप्ट करते समय उपयोग करें
  • अतिरिक्त (गैर-गुप्त) डेटा की जाँच करना चाहते हैं यदि इसे बदल दिया गया है? आप यहां अधिक के साथ संबद्ध डेटा का उपयोग करना चाह सकते हैं cipher.updateAAD(associatedData);

3. एकल संदेश के लिए सीरियल

सिर्फ IV और सिफरटेक्स्ट अपीयर करें। जैसा कि ऊपर कहा गया है, IV को गुप्त होने की आवश्यकता नहीं है।

ByteBuffer byteBuffer = ByteBuffer.allocate(iv.length + cipherText.length);
byteBuffer.put(iv);
byteBuffer.put(cipherText);
byte[] cipherMessage = byteBuffer.array();

यदि आपको स्ट्रिंग प्रतिनिधित्व की आवश्यकता है, तो बेस 64 के साथ वैकल्पिक रूप से एनकोड करें । या तो एंड्रॉइड या जावा 8 के अंतर्निहित कार्यान्वयन का उपयोग करें (Apache Commons Codec का उपयोग न करें - यह एक भयानक कार्यान्वयन है)। एन्कोडिंग का उपयोग "कन्वर्ट" बाइट सरणियों को स्ट्रिंग प्रतिनिधित्व करने के लिए किया जाता है ताकि इसे ASCII सुरक्षित बनाया जा सके जैसे:

String base64CipherMessage = Base64.getEncoder().encodeToString(cipherMessage);

4. डिक्रिप्शन तैयार करें: Deserialize

यदि आपने संदेश को इनकोड किया है, तो पहले इसे बाइट सरणी में डिकोड करें:

byte[] cipherMessage = Base64.getDecoder().decode(base64CipherMessage)

जरूरी:

5. डिक्रिप्ट

सिफर को प्रारंभ करें और एन्क्रिप्शन के साथ समान पैरामीटर सेट करें:

final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
//use first 12 bytes for iv
AlgorithmParameterSpec gcmIv = new GCMParameterSpec(128, cipherMessage, 0, 12);
cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmIv);
//use everything from 12 bytes on as ciphertext
byte[] plainText = cipher.doFinal(cipherMessage, 12, cipherMessage.length - 12);

जरूरी:

  • यदि आपने इसे एन्क्रिप्शन के दौरान जोड़ा है, तो इसके साथ संबद्ध डेटा जोड़ना न भूलें cipher.updateAAD(associatedData);

इस जिस्ट में एक वर्किंग कोड स्निपेट पाया जा सकता है।


ध्यान दें कि सबसे हाल के एंड्रॉइड (एसडीके 21+) और जावा (7+) कार्यान्वयन में एईएस-जीसीएम होना चाहिए। पुराने संस्करणों में इसका अभाव हो सकता है। मैं अभी भी इस मोड को चुनता हूं, क्योंकि यह एनक्रिप्ट-तत्कालीन-मैक (जैसे एईएस-सीबीसी + एचएमएसी के साथ ) के समान मोड की तुलना में अधिक कुशल होने के अलावा लागू करना आसान है । एचईएसी के साथ एईएस-सीबीसी को कैसे लागू किया जाए, इस लेख को देखें


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

1
हालांकि मैं इस प्रयास की प्रशंसा करता हूं, इसलिए मैं सिर्फ एक गलती की ओर इशारा करता हूं: "iv अद्वितीय होने के साथ संयोजन में अप्रत्याशित होना चाहिए (यानी यादृच्छिक iv का उपयोग करें)" - यह सीबीसी मोड के लिए सही है लेकिन जीसीएम के लिए नहीं।
Maarten Bodewes

this is true for CBC mode but not for GCMक्या आपको पूरे हिस्से से मतलब है, या केवल यह वास्तव में अप्रत्याशित होने की आवश्यकता नहीं है?
पैट्रिक फेवरे

1
"यदि आपको विषय नहीं मिलता है, तो आपको शायद पहली बार में निम्न स्तर की आदिम का उपयोग नहीं करना चाहिए" सुनिश्चित करें, कि मामला होना चाहिए, कई डेवलपर्स अभी भी करते हैं। मुझे यकीन नहीं है कि उन स्थानों पर सुरक्षा / क्रिप्टोग्राफी के संबंध में उच्च गुणवत्ता वाली सामग्री डालने से परहेज नहीं करना चाहिए जहां अक्सर ऐसा नहीं होता है। - मेरी गलती btw की ओर इशारा करने के लिए thx
पैट्रिक फेवर

1
ठीक है, सिर्फ इसलिए कि मुझे उत्तर लेखन सामग्री पसंद है (उद्देश्य के बजाय): IV हैंडलिंग को विशेष रूप से डिक्रिप्शन के दौरान सरल किया जा सकता है: जावा सभी के बाद एक मौजूदा बाइट सरणी से सीधे IV बनाना आसान बनाता है। समान डिक्रिप्शन के लिए जाता है, जिसे ऑफसेट 0 पर शुरू नहीं करना पड़ता है। यह सब कॉपी करना आवश्यक नहीं है। इसके अलावा अगर आप, सही चतुर्थ के लिए पिछले 255 बाइट्स जाना नहीं जा रहे हैं - आप चतुर्थ (? आप करते हैं) तो क्यों एक भी (अहस्ताक्षरित) बाइट का उपयोग नहीं करने के लिए लंबाई भेजना है?
Maarten Bodewes

2

ऑनलाइन संपादक Runnable संस्करण: -

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
//import org.apache.commons.codec.binary.Base64;
import java.util.Base64;

public class Encryptor {
    public static String encrypt(String key, String initVector, String value) {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));

            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);

            byte[] encrypted = cipher.doFinal(value.getBytes());

            //System.out.println("encrypted string: "
              //      + Base64.encodeBase64String(encrypted));

            //return Base64.encodeBase64String(encrypted);
            String s = new String(Base64.getEncoder().encode(encrypted));
            return s;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    public static String decrypt(String key, String initVector, String encrypted) {
        try {
            IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);

            byte[] original = cipher.doFinal(Base64.getDecoder().decode(encrypted));

            return new String(original);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;
    }

    public static void main(String[] args) {
        String key = "Bar12345Bar12345"; // 128 bit key
        String initVector = "RandomInitVector"; // 16 bytes IV

        System.out.println(encrypt(key, initVector, "Hello World"));
        System.out.println(decrypt(key, initVector, encrypt(key, initVector, "Hello World")));
    }
}

शांत, खुश यह मदद की!
भूपेश पंत

एक पासवर्ड एक कुंजी नहीं है, एक IV को स्थिर नहीं होना चाहिए। अभी भी सख्ती से टाइप किए गए कोड, जो कुंजी को नष्ट करना असंभव बनाता है। कोई संकेत नहीं कि आईवी के साथ क्या करना है, न ही कोई धारणा है कि यह अप्रत्याशित होना चाहिए।
मार्टेन बोदेस

1

यह अक्सर मानक पुस्तकालय प्रदान समाधान पर भरोसा करने के लिए अच्छा विचार है:

private static void stackOverflow15554296()
    throws
        NoSuchAlgorithmException, NoSuchPaddingException,        
        InvalidKeyException, IllegalBlockSizeException,
        BadPaddingException
{

    // prepare key
    KeyGenerator keygen = KeyGenerator.getInstance("AES");
    SecretKey aesKey = keygen.generateKey();
    String aesKeyForFutureUse = Base64.getEncoder().encodeToString(
            aesKey.getEncoded()
    );

    // cipher engine
    Cipher aesCipher = Cipher.getInstance("AES/ECB/PKCS5Padding");

    // cipher input
    aesCipher.init(Cipher.ENCRYPT_MODE, aesKey);
    byte[] clearTextBuff = "Text to encode".getBytes();
    byte[] cipherTextBuff = aesCipher.doFinal(clearTextBuff);

    // recreate key
    byte[] aesKeyBuff = Base64.getDecoder().decode(aesKeyForFutureUse);
    SecretKey aesDecryptKey = new SecretKeySpec(aesKeyBuff, "AES");

    // decipher input
    aesCipher.init(Cipher.DECRYPT_MODE, aesDecryptKey);
    byte[] decipheredBuff = aesCipher.doFinal(cipherTextBuff);
    System.out.println(new String(decipheredBuff));
}

यह "एनकोड करने के लिए पाठ" प्रिंट करता है।

समाधान जावा क्रिप्टोग्राफी आर्किटेक्चर संदर्भ गाइड और https://stackoverflow.com/a/20591539/146745 उत्तर पर आधारित है ।


5
ECB मोड का उपयोग कभी न करें। अवधि।
कोन्स्टेंटिनो स्पार्किस

1
ईसीबी का उपयोग नहीं किया जाना चाहिए यदि एक ही कुंजी के साथ डेटा के एक से अधिक ब्लॉक को एन्क्रिप्ट किया जाए, इसलिए "टेक्स्ट को एनकोड करने के लिए" यह काफी अच्छा है। stackoverflow.com/a/1220869/146745
andrej

@AndroidDev कुंजी कुंजी अनुभाग तैयार करने के लिए बनाई गई है: aesKey = keygen.generateKey ()
andrej

1

यह स्वीकृत उत्तर पर सुधार है।

परिवर्तन:

(1) यादृच्छिक IV का उपयोग करना और इसे एन्क्रिप्ट किए गए पाठ के लिए प्रस्तुत करना

(2) पासफ़्रेज़ से कुंजी बनाने के लिए SHA-256 का उपयोग करना

(३) अपाचे कॉमन्स पर कोई निर्भरता नहीं

public static void main(String[] args) throws GeneralSecurityException {
    String plaintext = "Hello world";
    String passphrase = "My passphrase";
    String encrypted = encrypt(passphrase, plaintext);
    String decrypted = decrypt(passphrase, encrypted);
    System.out.println(encrypted);
    System.out.println(decrypted);
}

private static SecretKeySpec getKeySpec(String passphrase) throws NoSuchAlgorithmException {
    MessageDigest digest = MessageDigest.getInstance("SHA-256");
    return new SecretKeySpec(digest.digest(passphrase.getBytes(UTF_8)), "AES");
}

private static Cipher getCipher() throws NoSuchPaddingException, NoSuchAlgorithmException {
    return Cipher.getInstance("AES/CBC/PKCS5PADDING");
}

public static String encrypt(String passphrase, String value) throws GeneralSecurityException {
    byte[] initVector = new byte[16];
    SecureRandom.getInstanceStrong().nextBytes(initVector);
    Cipher cipher = getCipher();
    cipher.init(Cipher.ENCRYPT_MODE, getKeySpec(passphrase), new IvParameterSpec(initVector));
    byte[] encrypted = cipher.doFinal(value.getBytes());
    return DatatypeConverter.printBase64Binary(initVector) +
            DatatypeConverter.printBase64Binary(encrypted);
}

public static String decrypt(String passphrase, String encrypted) throws GeneralSecurityException {
    byte[] initVector = DatatypeConverter.parseBase64Binary(encrypted.substring(0, 24));
    Cipher cipher = getCipher();
    cipher.init(Cipher.DECRYPT_MODE, getKeySpec(passphrase), new IvParameterSpec(initVector));
    byte[] original = cipher.doFinal(DatatypeConverter.parseBase64Binary(encrypted.substring(24)));
    return new String(original);
}

एक हैश अभी भी एक पासवर्ड आधारित प्रमुख पीढ़ी फ़ंक्शन / PBKDF नहीं है। या तो आप एक यादृच्छिक कुंजी का उपयोग करते हैं या आप PBKDF जैसे PBKDF2 / पासवर्ड आधारित एन्क्रिप्शन का उपयोग करते हैं।
मार्टेन बॉड्यूस

@MaartenBodewes क्या आप सुधार का सुझाव दे सकते हैं?
wvdz

PBKDF2 जावा में मौजूद है, इसलिए मुझे लगता है कि मैंने सिर्फ एक सुझाव दिया है। ठीक है, मैंने एक कोड नहीं दिया , लेकिन वह मेरी राय में बहुत अधिक पूछ रहा है। पासवर्ड आधारित एन्क्रिप्शन के बहुत सारे उदाहरण हैं।
Maarten Bodewes

@MaartenBodewes मुझे लगा कि यह एक साधारण फिक्स हो सकता है। जिज्ञासा से बाहर, इस कोड का उपयोग करते समय क्या विशिष्ट कमजोरियां होंगी?
wvdz

0

स्प्रिंग बूट के साथ java.util.Base64 का उपयोग कर एक और समाधान

एन्क्रिप्ट करने वाला वर्ग

package com.jmendoza.springboot.crypto.cipher;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

@Component
public class Encryptor {

    @Value("${security.encryptor.key}")
    private byte[] key;
    @Value("${security.encryptor.algorithm}")
    private String algorithm;

    public String encrypt(String plainText) throws Exception {
        SecretKeySpec secretKey = new SecretKeySpec(key, algorithm);
        Cipher cipher = Cipher.getInstance(algorithm);
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        return new String(Base64.getEncoder().encode(cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8))));
    }

    public String decrypt(String cipherText) throws Exception {
        SecretKeySpec secretKey = new SecretKeySpec(key, algorithm);
        Cipher cipher = Cipher.getInstance(algorithm);
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        return new String(cipher.doFinal(Base64.getDecoder().decode(cipherText)));
    }
}

EncryptorController क्लास

package com.jmendoza.springboot.crypto.controller;

import com.jmendoza.springboot.crypto.cipher.Encryptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/cipher")
public class EncryptorController {

    @Autowired
    Encryptor encryptor;

    @GetMapping(value = "encrypt/{value}")
    public String encrypt(@PathVariable("value") final String value) throws Exception {
        return encryptor.encrypt(value);
    }

    @GetMapping(value = "decrypt/{value}")
    public String decrypt(@PathVariable("value") final String value) throws Exception {
        return encryptor.decrypt(value);
    }
}

application.properties

server.port=8082
security.encryptor.algorithm=AES
security.encryptor.key=M8jFt46dfJMaiJA0

उदाहरण

http: // localhost: 8082 / सिफर / एन्क्रिप्ट / jmendoza

2h41HH8Shzc4BRU3hVDOXA ==

http: // localhost: 8082 / सिफर / डिक्रिप्ट / 2h41HH8Shzc4BRU3hVDOXA ==

jmendoza


-1

स्वीकृत उत्तर का अनुकूलित संस्करण।

  • कोई 3 पार्टी काम नहीं करती

  • एन्क्रिप्ट किए गए संदेश में IV शामिल है (सार्वजनिक हो सकता है)

  • पासवर्ड किसी भी लम्बाई का हो सकता है

कोड:

import java.io.UnsupportedEncodingException;
import java.security.SecureRandom;
import java.util.Base64;

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

public class Encryptor {
    public static byte[] getRandomInitialVector() {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            SecureRandom randomSecureRandom = SecureRandom.getInstance("SHA1PRNG");
            byte[] initVector = new byte[cipher.getBlockSize()];
            randomSecureRandom.nextBytes(initVector);
            return initVector;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    public static byte[] passwordTo16BitKey(String password) {
        try {
            byte[] srcBytes = password.getBytes("UTF-8");
            byte[] dstBytes = new byte[16];

            if (srcBytes.length == 16) {
                return srcBytes;
            }

            if (srcBytes.length < 16) {
                for (int i = 0; i < dstBytes.length; i++) {
                    dstBytes[i] = (byte) ((srcBytes[i % srcBytes.length]) * (srcBytes[(i + 1) % srcBytes.length]));
                }
            } else if (srcBytes.length > 16) {
                for (int i = 0; i < srcBytes.length; i++) {
                    dstBytes[i % dstBytes.length] += srcBytes[i];
                }
            }

            return dstBytes;
        } catch (UnsupportedEncodingException ex) {
            ex.printStackTrace();
        }

        return null;
    }

    public static String encrypt(String key, String value) {
        return encrypt(passwordTo16BitKey(key), value);
    }

    public static String encrypt(byte[] key, String value) {
        try {
            byte[] initVector = Encryptor.getRandomInitialVector();
            IvParameterSpec iv = new IvParameterSpec(initVector);
            SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);

            byte[] encrypted = cipher.doFinal(value.getBytes());
            return Base64.getEncoder().encodeToString(encrypted) + " " + Base64.getEncoder().encodeToString(initVector);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;
    }

    public static String decrypt(String key, String encrypted) {
        return decrypt(passwordTo16BitKey(key), encrypted);
    }

    public static String decrypt(byte[] key, String encrypted) {
        try {
            String[] encryptedParts = encrypted.split(" ");
            byte[] initVector = Base64.getDecoder().decode(encryptedParts[1]);
            if (initVector.length != 16) {
                return null;
            }

            IvParameterSpec iv = new IvParameterSpec(initVector);
            SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);

            byte[] original = cipher.doFinal(Base64.getDecoder().decode(encryptedParts[0]));

            return new String(original);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;
    }
}

उपयोग:

String key = "Password of any length.";
String encrypted = Encryptor.encrypt(key, "Hello World");
String decrypted = Encryptor.decrypt(key, encrypted);
System.out.println(encrypted);
System.out.println(decrypted);

उदाहरण आउटपुट:

QngBg+Qc5+F8HQsksgfyXg== yDfYiIHTqOOjc0HRNdr1Ng==
Hello World

आपका पासवर्ड व्युत्पन्न कार्य असुरक्षित है। मैं e.printStackTrace()तथाकथित अनुकूलित कोड में उम्मीद नहीं करूंगा ।
मार्टेन बॉड्यूज
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.