it-swarm-tr.com

CryptoJS AES şifreleme ve Java AES şifre çözme

Bunu sadece soruyorum çünkü 2 gün boyunca Kripto AES şifrelemesi hakkında birçok yazı okudum ve tam olarak aldığımı düşündüğümde, hiç alamayacağımı anladım. 

Bu gönderiye sorunuma en yakın olanı, aynı sorunum var ancak yanıtlanmadı:

CryptoJS AES şifreleme ve Java AES şifre çözme değeri uyuşmazlığı

Bunu birçok yönden yapmaya çalıştım ama doğru anlamadım.

İlk Kapalı

Zaten şifrelenmiş dizgeyi alıyorum (sadece nasıl yaptıklarını görmek için kodu aldım), bu yüzden şifreleme yolunu değiştirmek bir seçenek değildir. Bu yüzden benzer soruların hepsi benim için yararlı değil.

İkinci

Gizli anahtara erişimim var ve onu değiştirebiliyorum (gerekliyse uzunluğu ayarlamak bir seçenek).

Şifreleme CryptoJS'de yapılır ve şifrelenmiş dizeyi bir GET parametresi olarak gönderirler.

GetParamsForAppUrl.prototype.generateUrlParams = function() {
const self = this;
 return new Promise((resolve, reject) => {
   const currentDateInMilliseconds = new Date().getTime();
   const secret = tokenSecret.secret;
   var encrypted = CryptoJS.AES.encrypt(self.authorization, secret);
   encrypted = encrypted.toString();
   self.urlParams = {
     token: encrypted,
     time: currentDateInMilliseconds
   };
   resolve();
 });
};

Bu kolayca CryptoJS ile kullanarak javascript şifresini çözmek:

var decrypted = CryptoJS.AES.decrypt(encrypted_string, secret);
    console.log(decrypted.toString(CryptoJS.enc.Utf8)); 

Ancak bunu Javascript'te yapmak istemiyorum, güvenlik nedeniyle, bu yüzden Java'da şifresini çözmeye çalışıyorum:

String secret = "secret";
byte[] cipherText = encrypted_string.getBytes("UTF8");
SecretKey secKey = new SecretKeySpec(secret.getBytes(), "AES");
Cipher aesCipher = Cipher.getInstance("AES");
aesCipher.init(Cipher.DECRYPT_MODE, secKey);
byte[] bytePlainText = aesCipher.doFinal(byteCipherText);
String myDecryptedText = = new String(bytePlainText);

Ne yaptığım hakkında hiçbir fikrim olmadan önce, baz64 kod çözmeyi denedim, bazı IV'leri ve okuduğum birçok şeyi ekledim, elbette hiçbiri işe yaramadı. 

Ama anlamaya başladıktan sonra ne yaptığımı, yukarıdaki basit senaryoyu yazdım ve yazıya aynı hatayı verdim: Geçersiz AES anahtar uzunluğu

Buradan nereye gideceğimi bilmiyorum. Bununla ilgili çok şey okuduktan sonra, çözüm karmaşalı ya da dolgulu görünüyor, ancak şifreleme yöntemi üzerinde hiçbir kontrolüm yok, bu yüzden gerçekten sır ya da altlık yaratamıyorum.

Ancak dediğim gibi, gizli anahtarı belirli bir uzunlukla eşleştirebilecek şekilde değiştirebilirim ve değiştirmeyi denedim, ama karanlıkta çekim yaparken, bunun çözüm olup olmadığını gerçekten bilmiyorum.

Öyleyse sorum temel olarak, Eğer şifrelenmiş dizeyi (ilk komut dosyası gibi javascript'te) ve gizli anahtarı alırsam, şifresini çözmenin bir yolu var mı (Java)? Eğer öyleyse, nasıl yapılır?

8
Lauro182

CryptoJS, IV'ü şifrelenmiş verinin içine koymak için, OpenSSL ile aynı tuş türetme işlevini ve aynı formatı uygular. Bu nedenle, OpenSSL kodlu verilerle ilgilenen tüm Java kodları geçerlidir.

Aşağıdaki Javascript kodu verildi:

var text = "The quick brown fox jumps over the lazy dog. ???? ????";
var secret = "René Über";
var encrypted = CryptoJS.AES.encrypt(text, secret);
encrypted = encrypted.toString();
console.log("Cipher text: " + encrypted);

Şifreleme metnini alıyoruz:

U2FsdGVkX1+tsmZvCEFa/iGeSA0K7gvgs9KXeZKwbCDNCs2zPo+BXjvKYLrJutMK+hxTwl/hyaQLOaD7LLIRo2I5fyeRMPnroo6k8N9uwKk=

Java tarafında, biz var

String secret = "René Über";
String cipherText = "U2FsdGVkX1+tsmZvCEFa/iGeSA0K7gvgs9KXeZKwbCDNCs2zPo+BXjvKYLrJutMK+hxTwl/hyaQLOaD7LLIRo2I5fyeRMPnroo6k8N9uwKk=";

byte[] cipherData = Base64.getDecoder().decode(cipherText);
byte[] saltData = Arrays.copyOfRange(cipherData, 8, 16);

MessageDigest md5 = MessageDigest.getInstance("MD5");
final byte[][] keyAndIV = GenerateKeyAndIV(32, 16, 1, saltData, secret.getBytes(StandardCharsets.UTF_8), md5);
SecretKeySpec key = new SecretKeySpec(keyAndIV[0], "AES");
IvParameterSpec iv = new IvParameterSpec(keyAndIV[1]);

byte[] encrypted = Arrays.copyOfRange(cipherData, 16, cipherData.length);
Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
aesCBC.init(Cipher.DECRYPT_MODE, key, iv);
byte[] decryptedData = aesCBC.doFinal(encrypted);
String decryptedText = new String(decryptedData, StandardCharsets.UTF_8);

System.out.println(decryptedText);

Sonuç:

The quick brown fox jumps over the lazy dog. ???? ????

Başladığımız metin bu. Emojiler, aksanlar ve umiller de işe yarıyor.

GenerateKeyAndIV, OpenSSL'nin anahtar türetme işlevini EVP_BytesToKey 'u yeniden düzenleyen bir yardımcı işlevdir (bkz. https://github.com/openssl/openssl/blob/master/crypto/evp/evp_key.c ).

/**
 * Generates a key and an initialization vector (IV) with the given salt and password.
 * <p>
 * This method is equivalent to OpenSSL's EVP_BytesToKey function
 * (see https://github.com/openssl/openssl/blob/master/crypto/evp/evp_key.c).
 * By default, OpenSSL uses a single iteration, MD5 as the algorithm and UTF-8 encoded password data.
 * </p>
 * @param keyLength the length of the generated key (in bytes)
 * @param ivLength the length of the generated IV (in bytes)
 * @param iterations the number of digestion rounds 
 * @param salt the salt data (8 bytes of data or <code>null</code>)
 * @param password the password data (optional)
 * @param md the message digest algorithm to use
 * @return an two-element array with the generated key and IV
 */
public static byte[][] GenerateKeyAndIV(int keyLength, int ivLength, int iterations, byte[] salt, byte[] password, MessageDigest md) {

    int digestLength = md.getDigestLength();
    int requiredLength = (keyLength + ivLength + digestLength - 1) / digestLength * digestLength;
    byte[] generatedData = new byte[requiredLength];
    int generatedLength = 0;

    try {
        md.reset();

        // Repeat process until sufficient data has been generated
        while (generatedLength < keyLength + ivLength) {

            // Digest data (last digest if available, password data, salt if available)
            if (generatedLength > 0)
                md.update(generatedData, generatedLength - digestLength, digestLength);
            md.update(password);
            if (salt != null)
                md.update(salt, 0, 8);
            md.digest(generatedData, generatedLength, digestLength);

            // additional rounds
            for (int i = 1; i < iterations; i++) {
                md.update(generatedData, generatedLength, digestLength);
                md.digest(generatedData, generatedLength, digestLength);
            }

            generatedLength += digestLength;
        }

        // Copy key and IV into separate byte arrays
        byte[][] result = new byte[2][];
        result[0] = Arrays.copyOfRange(generatedData, 0, keyLength);
        if (ivLength > 0)
            result[1] = Arrays.copyOfRange(generatedData, keyLength, keyLength + ivLength);

        return result;

    } catch (DigestException e) {
        throw new RuntimeException(e);

    } finally {
        // Clean out temporary data
        Arrays.fill(generatedData, (byte)0);
    }
}

Java Şifreleme Uzantısı (JCE) Sınırsız Güç Yargı Politikası uygulamasını yüklemeniz gerektiğini unutmayın. Aksi takdirde, 256 anahtar boyutuna sahip AES çalışmaz ve bir istisna alamaz:

Java.security.InvalidKeyException: Illegal key size

Güncelle

Cevabımın ilk sürümünde kullandığım EVP_BytesToKey 'un Ola Bini'nin Java kodu 'ını Java kodunu anlamak için daha deyimsel ve kolay bir şekilde değiştirdim (yukarıya bakın).

Ayrıca bkz. Java'da AES kullanılarak openssl komutuyla şifrelenmiş dosyanın şifresini nasıl çözebilirim? .

19
Codo

Bir sistemde şifreleme ve başka bir şifre çözme sırasında sistem varsayılanlarının insafına kalırsınız. Herhangi bir sistem varsayılanı eşleşmiyorsa (ve çoğu zaman uyuşmuyorsa) şifre çözme işleminiz başarısız olur.

Her şey bayt için bayt her iki tarafta aynı olmalıdır. Etkili bir şekilde, varsayılanlara güvenmek yerine her iki taraftaki her şeyi belirlemek anlamına gelir. Varsayılanları yalnızca her iki uçta da aynı sistemi kullanıyorsanız kullanabilirsiniz. O zaman bile, tam olarak belirtmek daha iyidir.

Anahtar, IV, şifreleme modu, dolguyu ve bayt dönüşümü dizgisinin her iki uçta da aynı olması gerekir. Özellikle anahtar baytların aynı olup olmadığını kontrol etmeye değer. Anahtarınızı oluşturmak için bir Anahtar Türetme İşlevi (KDF) kullanıyorsanız, bunun için tüm parametrelerin aynı olması ve dolayısıyla tam olarak belirtilmesi gerekir.

"Geçersiz AES anahtar uzunluğu", anahtarınızı oluştururken bir sorun olduğunu gösterebilir. getBytes() işlevini kullanıyorsunuz. Bu muhtemelen bir hatadır. Ne tür baytlar alacağınızı belirtmeniz gerekir: ANSI, UTF-8, EBCDIC, her neyse. Dizenin bayt dönüştürmesi için varsayılan varsayım, bu sorunun olası nedenidir. Her iki uçta da açıkça kullanılacak dönüşümü belirtin. Bu şekilde eşleştiğinden emin olabilirsin.

Kripto, parametrelerin şifreleme ve şifre çözme işlemleri ile tam olarak eşleşmemesi durumunda başarısız olacak şekilde tasarlanmıştır. Örneğin, anahtardaki bir bitlik fark bile başarısız olmasına neden olur.

2
rossum