android एंड्रॉइड 4.2 ने अपना एन्क्रिप्ट/डिक्रिप्ट कोड तोड़ दिया और प्रदत्त समाधान काम नहीं करते हैं




cryptography bouncycastle (6)

क्योंकि इससे सब कुछ मुझे एन्क्रिप्टेड पासवर्ड उत्पन्न करने में मदद नहीं करता था जो सभी एंड्रॉइड उपकरणों (> = 2.1) पर निर्धारक था, मैंने एक और एईएस कार्यान्वयन की खोज की। मुझे वह मिला जो सभी उपकरणों पर मेरे लिए काम करता है। मैं एक सुरक्षा विशेषज्ञ नहीं हूं, इसलिए यदि तकनीक उतनी सुरक्षित नहीं है, तो कृपया मेरे उत्तर को कम मत करें। मैं केवल उन लोगों के लिए कोड पोस्ट कर रहा हूं जिन्होंने एक ही समस्या में भाग लिया है जिसे मैंने पहले सामना किया था।

import java.security.GeneralSecurityException;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import android.util.Log;

public class EncodeDecodeAES {


    private static final String TAG_DEBUG = "TAG";
    private IvParameterSpec ivspec;
    private SecretKeySpec keyspec;
    private Cipher cipher;

    private String iv = "fedcba9876543210";//Dummy iv (CHANGE IT!)
    private String SecretKey = "0123456789abcdef";//Dummy secretKey (CHANGE IT!)

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

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

        try {
            cipher = Cipher.getInstance("AES/CBC/NoPadding");
        } catch (GeneralSecurityException e) {
            Log.d(TAG_DEBUG, e.getMessage());
        }
    }

    public byte[] 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) {
            Log.d(TAG_DEBUG, e.getMessage());
            throw new Exception("[encrypt] " + e.getMessage());
        }

        return encrypted;
    }

    public byte[] 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) {
            Log.d(TAG_DEBUG, e.getMessage());
            throw new Exception("[decrypt] " + e.getMessage());
        }
        return 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;
    }
}

आप इसका उपयोग इस प्रकार कर सकते हैं:

EncodeDecodeAES aes = new EncodeDecodeAES ();
/* Encrypt */
String encrypted = EncodeDecodeAES.bytesToHex(aes.encrypt("Text to Encrypt"));
/* Decrypt */
String decrypted = new String(aes.decrypt(encrypted));

स्रोत: HERE

सबसे पहले, मैंने पहले से ही एंड्रॉइड 4.2 को अपने एईएस एन्क्रिप्ट / डिक्रिप्ट कोड और एंड्रॉइड 4.2 पर एन्क्रिप्शन त्रुटि तोड़ दी है और प्रदान किया गया समाधान:

SecureRandom sr = null;
if (android.os.Build.VERSION.SDK_INT >= JELLY_BEAN_4_2) {
    sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
} else {
    sr = SecureRandom.getInstance("SHA1PRNG");
}

मेरे लिए काम नहीं करता है, क्योंकि, एंड्रॉइड में एन्क्रिप्टेड डेटा डीकोड करते समय <4.2 एंड्रॉइड 4.2 में, मुझे मिलता है:

javax.crypto.BadPaddingException: pad block corrupted
at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(BaseBlockCipher.java:709)

मेरा कोड काफी सरल है, और एंड्रॉइड 4.2 तक काम कर रहा था:

public static byte[] encrypt(byte[] data, String seed) throws Exception {

    KeyGenerator keygen = KeyGenerator.getInstance("AES");
    SecureRandom secrand = SecureRandom.getInstance("SHA1PRNG");
    secrand.setSeed(seed.getBytes());
    keygen.init(128, secrand);

    SecretKey seckey = keygen.generateKey();
    byte[] rawKey = seckey.getEncoded();

    SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
    return cipher.doFinal(data);
}

public static byte[] decrypt(byte[] data, String seed) throws Exception {

    KeyGenerator keygen = KeyGenerator.getInstance("AES");
    SecureRandom secrand = SecureRandom.getInstance("SHA1PRNG");
    secrand.setSeed(seed.getBytes());
    keygen.init(128, secrand);

    SecretKey seckey = keygen.generateKey();
    byte[] rawKey = seckey.getEncoded();

    SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.DECRYPT_MODE, skeySpec);
    return cipher.doFinal(data);
}

मेरा अनुमान है कि डिफ़ॉल्ट प्रदाता एकमात्र चीज नहीं थी जो एंड्रॉइड 4.2 में बदल गई, अन्यथा मेरा कोड प्रस्तावित समाधान के साथ काम करेगा।

मेरा कोड कुछ पोस्ट पर आधारित था जो मैंने यहां बहुत समय पहले स्टैक ओवरव्लो में पाया था; मैं देखता हूं कि यह उल्लिखित पदों से अलग है क्योंकि यह केवल बाइट एरेज़ को क्रिप्ट और डिक्रिप्ट करता है, जबकि अन्य समाधान स्ट्रिंग्स (हेक्स स्ट्रिंग्स, मुझे लगता है) को क्रिप्ट और डिक्रिप्ट करते हैं।

क्या इसे बीज के साथ करना है? क्या इसमें न्यूनतम / अधिकतम लंबाई, वर्णों का प्रतिबंध आदि है?

कोई विचार / समाधान?

संपादित करें : बहुत सारे परीक्षणों के बाद, मुझे लगता है कि 2 समस्याएं हैं:

  1. प्रदाता एंड्रॉइड 4.2 (एपीआई 17) में बदल गया -> यह ठीक करना आसान है, बस पोस्ट के शीर्ष पर उल्लिखित समाधान को लागू करें

  2. एंड्रॉइड 2.2 (एपीआई 8) -> एंड्रॉइड 2.3 (एपीआई 9) में बाउंसीकास्टल 1.34 से 1.45 में बदल गया है, इसलिए मैंने जो डिक्रिप्शन समस्या पहले बताया था वह वही है जैसा कि यहां बताया गया है: 1.45 तक अपग्रेड करते समय बाउंसीकास्ट एईएस त्रुटि

तो अब सवाल यह है: क्या BouncyCastle 1.34 में BouncyCastle 1.34 में क्रिप्ट किए गए डेटा को पुनर्प्राप्त करने का कोई तरीका है?


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


समस्या यह है कि नए प्रदाता के साथ , कोड का निम्नलिखित स्निपेट

KeyGenerator keygen = KeyGenerator.getInstance("AES");
SecureRandom secrand = SecureRandom.getInstance("SHA1PRNG");
secrand.setSeed(seed.getBytes());
keygen.init(128, secrand);
SecretKey seckey = keygen.generateKey();
byte[] rawKey = seckey.getEncoded();

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


सबसे पहले एक अस्वीकरण:

एक कुंजी प्राप्त करने के लिए कभी भी "SecureRandom" का उपयोग करें! यह टूटा हुआ है और समझ में नहीं आता है!

यदि आप डिस्क से एईएस कुंजी पढ़ रहे हैं, तो बस वास्तविक कुंजी को स्टोर करें और इस अजीब नृत्य से गुजरें। आप बाइट्स से एईएस उपयोग के लिए एक गुप्त कुंजी प्राप्त कर सकते हैं:

    SecretKey key = new SecretKeySpec(keyBytes, "AES");

यदि आप कुंजी प्राप्त करने के लिए पासवर्ड का उपयोग कर रहे हैं, तो चेतावनी के साथ nelenkov.blogspot.com/2012/04/… पालन ​​करें कि अंगूठे का एक अच्छा नियम है नमक का आकार मुख्य आउटपुट के समान आकार होना चाहिए। यह इस तरह दिख रहा है:

    /* User types in their password: */
    String password = "password";

    /* Store these things on disk used to derive key later: */
    int iterationCount = 1000;
    int saltLength = 32; // bytes; should be the same size as the output (256 / 8 = 32)
    int keyLength = 256; // 256-bits for AES-256, 128-bits for AES-128, etc
    byte[] salt; // Should be of saltLength

    /* When first creating the key, obtain a salt with this: */
    SecureRandom random = new SecureRandom();
    byte[] salt = new byte[saltLength];
    random.nextBytes(salt);

    /* Use this to derive the key from the password: */
    KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt,
                iterationCount, keyLength);
    SecretKeyFactory keyFactory = SecretKeyFactory
                .getInstance("PBKDF2WithHmacSHA1");
    byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
    SecretKey key = new SecretKeySpec(keyBytes, "AES");

बस। आपको और कुछ भी नहीं उपयोग करना चाहिए।


private static final int ITERATION_COUNT = 1000;
private static final int KEY_LENGTH = 256;
private static final String PBKDF2_DERIVATION_ALGORITHM = "PBKDF2WithHmacSHA1";
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
private static final int PKCS5_SALT_LENGTH = 32;
private static final String DELIMITER = "]";
private static final SecureRandom random = new SecureRandom();

public static String encrypt(String plaintext, String password) {
    byte[] salt  = generateSalt();
    SecretKey key = deriveKey(password, salt);

    try {
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        byte[] iv = generateIv(cipher.getBlockSize());
        IvParameterSpec ivParams = new IvParameterSpec(iv);
        cipher.init(Cipher.ENCRYPT_MODE, key, ivParams);
        byte[] cipherText = cipher.doFinal(plaintext.getBytes("UTF-8"));

        if(salt != null) {
            return String.format("%s%s%s%s%s",
                    toBase64(salt),
                    DELIMITER,
                    toBase64(iv),
                    DELIMITER,
                    toBase64(cipherText));
        }

        return String.format("%s%s%s",
                toBase64(iv),
                DELIMITER,
                toBase64(cipherText));
    } catch (GeneralSecurityException e) {
        throw new RuntimeException(e);
    } catch (UnsupportedEncodingException e) {
        throw new RuntimeException(e);
    }
}

public static String decrypt(String ciphertext, String password) {
    String[] fields = ciphertext.split(DELIMITER);
    if(fields.length != 3) {
        throw new IllegalArgumentException("Invalid encypted text format");
    }
    byte[] salt        = fromBase64(fields[0]);
    byte[] iv          = fromBase64(fields[1]);
    byte[] cipherBytes = fromBase64(fields[2]);
    SecretKey key = deriveKey(password, salt);

    try {
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        IvParameterSpec ivParams = new IvParameterSpec(iv);
        cipher.init(Cipher.DECRYPT_MODE, key, ivParams);
        byte[] plaintext = cipher.doFinal(cipherBytes);
        return new String(plaintext, "UTF-8");
    } catch (GeneralSecurityException e) {
        throw new RuntimeException(e);
    } catch (UnsupportedEncodingException e) {
        throw new RuntimeException(e);
    }
}

private static byte[] generateSalt() {
    byte[] b = new byte[PKCS5_SALT_LENGTH];
    random.nextBytes(b);
    return b;
}

private static byte[] generateIv(int length) {
    byte[] b = new byte[length];
    random.nextBytes(b);
    return b;
}

private static SecretKey deriveKey(String password, byte[] salt) {
    try {
        KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, ITERATION_COUNT, KEY_LENGTH);
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(PBKDF2_DERIVATION_ALGORITHM);
        byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
        return new SecretKeySpec(keyBytes, "AES");
    } catch (GeneralSecurityException e) {
        throw new RuntimeException(e);
    }
}

private static String toBase64(byte[] bytes) {
    return Base64.encodeToString(bytes, Base64.NO_WRAP);
}

private static byte[] fromBase64(String base64) {
    return Base64.decode(base64, Base64.NO_WRAP);
}

Source


यह मेरे लिए क्या तय किया गया है (जैसा @Giorgio suggested गया है) बस इसे बदल रहा था :

SecureRandom secrand = SecureRandom.getInstance("SHA1PRNG");

इसके साथ :

SecureRandom secrand = SecureRandom.getInstance("SHA1PRNG", "Crypto");