Perché Java consente la crittografia AES-256 bit su sistemi senza criteri di forza illimitati JCE se si utilizza PBE?

7

È risaputo che grazie ai controlli di esportazione della crittografia, Oracle JRE viene fornito con resistenza crittografica "limitata" abilitata come elencata in Documentazione JCA . Per AES, il valore predefinito massimo corrisponde alla lunghezza della chiave 128 bit . Per attivare la crittografia 192 bit o 256 bit , i file delle JCE Unlimited Strength Jurisdiction Policy deve essere installato nel JRE.

Recentemente mi sono imbattuto in una situazione per caso che mi ha portato a credere che ci sia un problema con questa applicazione. Non sono sicuro che lo chiamerei un bug, ma sicuramente non è ben documentato (o almeno non riesco a trovare nulla che lo documenta).

Il controllo della lunghezza della chiave viene eseguito all'interno di cipher.init() , e credo che utilizzi Cipher.getMaxAllowedKeyLength("AES") per determinare se la dimensione massima della chiave è 128 o Integer.MAX_VALUE .

Usando la normale crittografia con chiave, questo controllo va bene. Su un'installazione JRE predefinita, il codice sottostante viene eseguito come previsto (sto utilizzando Groovy per il test ma l'ho provato anche in puro Java):

static boolean isUnlimitedStrengthCrypto() {
    Cipher.getMaxAllowedKeyLength("AES") > 128
}

@Test
public void testShouldEncryptAndDecryptWith128BitKey() throws Exception {
    // Arrange
    MessageDigest sha1 = MessageDigest.getInstance("SHA1")
    String key = Hex.encodeHexString(sha1.digest("thisIsABadPassword".getBytes()))[0..<32]
    String iv = Hex.encodeHexString(sha1.digest("thisIsABadIv".getBytes()))[0..<32]

    logger.info("Key: ${key}")
    logger.info("IV : ${iv}")

    SecretKey secretKey = new SecretKeySpec(Hex.decodeHex(key.toCharArray()), "AES")
    IvParameterSpec ivParameterSpec = new IvParameterSpec(Hex.decodeHex(iv.toCharArray()))

    // Act    
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
    cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec)

    String message = "This is a plaintext message."

    byte[] cipherBytes = cipher.doFinal(message.getBytes())

    cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec)

    byte[] recoveredBytes = cipher.doFinal(cipherBytes)

    String recovered = new String(recoveredBytes)
    System.out.println("Recovered message: " + recovered)

    // Assert
    assert recovered == message
}

Questo genera l'output:

[main] INFO  *.crypto.OpenSSLPBEEncryptorTest  - Key: 6d71f677ecb99cf623246fb48a1d8130
[main] INFO  *.crypto.OpenSSLPBEEncryptorTest  - IV : 912ed675905eb4cb0f9f5714c9c9ec39

E questo test:

@Test
public void testShouldNotEncryptAndDecryptWith256BitKey() throws Exception {
    // Arrange
    Assume.assumeTrue("This test should only run when unlimited (256 bit) encryption is not available", !isUnlimitedStrengthCrypto())

    MessageDigest sha1 = MessageDigest.getInstance("SHA1")
    String key = Hex.encodeHexString(sha1.digest("thisIsABadPassword".getBytes()))[0..<32] * 2
    String iv = Hex.encodeHexString(sha1.digest("thisIsABadIv".getBytes()))[0..<32]

    logger.info("Key: ${key}")
    logger.info("IV : ${iv}")

    SecretKey secretKey = new SecretKeySpec(Hex.decodeHex(key.toCharArray()), "AES")
    IvParameterSpec ivParameterSpec = new IvParameterSpec(Hex.decodeHex(iv.toCharArray()))

    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")

    // Act
    def msg = shouldFail(InvalidKeyException) {
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec)
    }

    // Assert
    assert msg =~ "Illegal key size"
}

Genera questo output:

[main] INFO  *.crypto.OpenSSLPBEEncryptorTest  - Key: 6d71f677ecb99cf623246fb48a1d81306d71f677ecb99cf623246fb48a1d8130
[main] INFO  *.crypto.OpenSSLPBEEncryptorTest  - IV : 912ed675905eb4cb0f9f5714c9c9ec39

E passa con successo lanciando l'eccezione.

Il problema sorge quando viene utilizzata la crittografia basata su password.

Poiché la derivazione della chiave dalla password (e salt, se fornita), si verifica durante cipher.init() ma dopo il controllo della lunghezza della chiave, il controllo della lunghezza si applica effettivamente alla rappresentazione byte[] di% codice%. Ciò significa che se viene utilizzata una password < = 16 caratteri ( SecretKey.getEncoded() ), il controllo passerà anche se la cifra specificata utilizza una chiave 16 bytes / 128 bits . La chiave derivata sarà 256 bit anche se la politica di giurisdizione lo proibisce. Al contrario, se una password > 16 caratteri sono usati, anche con un 256 bits cipher, il controllo della lunghezza fallirà e verrà lanciato un 128 bit . Il seguente codice lo dimostra:

@Test
public void testShouldEncryptAndDecryptWithPBEShortPassword() throws Exception {
    // Arrange
    final String PASSWORD = "password"
    String salt = "saltsalt"

    logger.info("Password: ${PASSWORD}")
    logger.info("Salt    : ${salt}")

    String algorithm;
    algorithm = "PBEWITHMD5AND256BITAES-CBC-OPENSSL"
    PBEKeySpec pbeSpec = new PBEKeySpec(PASSWORD.toCharArray());
    SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(algorithm, "BC");
    SecretKey secretKey = secretKeyFactory.generateSecret(pbeSpec);
    PBEParameterSpec saltParams = new PBEParameterSpec(salt.getBytes("US-ASCII"), 0);

    // Act
    Cipher cipher = Cipher.getInstance(algorithm, "BC");
    cipher.init(Cipher.ENCRYPT_MODE, secretKey, saltParams);

    String message = "This is a plaintext message."

    byte[] cipherBytes = cipher.doFinal(message.getBytes())

    cipher.init(Cipher.DECRYPT_MODE, secretKey, saltParams)

    byte[] recoveredBytes = cipher.doFinal(cipherBytes)

    String recovered = new String(recoveredBytes)
    System.out.println("Recovered message: " + recovered)

    // Assert
    assert recovered == message
}

@Test
public void testShouldNotEncryptAndDecryptWithPBELongPassword() throws Exception {
    // Arrange
    Assume.assumeTrue("This test should only run when unlimited (256 bit) encryption is not available", !isUnlimitedStrengthCrypto())

    final String PASSWORD = "thisIsABadPassword"
    String salt = "saltsalt"

    logger.info("Password: ${PASSWORD}")
    logger.info("Salt    : ${salt}")

    String algorithm;
    algorithm = "PBEWITHMD5AND256BITAES-CBC-OPENSSL"
    PBEKeySpec pbeSpec = new PBEKeySpec(PASSWORD.toCharArray());
    SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(algorithm, "BC");
    SecretKey secretKey = secretKeyFactory.generateSecret(pbeSpec);
    PBEParameterSpec saltParams = new PBEParameterSpec(salt.getBytes("US-ASCII"), 0);

    Cipher cipher = Cipher.getInstance(algorithm, "BC");

    // Act
    def msg = shouldFail(InvalidKeyException) {
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, saltParams);
    }

    // Assert
    assert msg =~ "Illegal key size"
}

Entrambi i test "passano" in quello su un sistema con crittografia a forza "limitata", la crittografia InvalidKeyException è ancora disponibile se la password è abbastanza breve. Al contrario, questo test dimostra che una password lunga causa un'eccezione anche quando si utilizza la crittografia 256 bit :

@Test
public void testShouldNotEncryptAndDecryptWithPBELongPasswordEvenWith128BitKey() throws Exception {
    // Arrange
    Assume.assumeTrue("This test should only run when unlimited (256 bit) encryption is not available", !isUnlimitedStrengthCrypto())

    final String PASSWORD = "thisIsABadPassword"
    String salt = "saltsalt"

    logger.info("Password: ${PASSWORD}")
    logger.info("Salt    : ${salt}")

    String algorithm;
    algorithm = "PBEWITHMD5AND128BITAES-CBC-OPENSSL"
    PBEKeySpec pbeSpec = new PBEKeySpec(PASSWORD.toCharArray());
    SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(algorithm, "BC");
    SecretKey secretKey = secretKeyFactory.generateSecret(pbeSpec);
    PBEParameterSpec saltParams = new PBEParameterSpec(salt.getBytes("US-ASCII"), 0);

    Cipher cipher = Cipher.getInstance(algorithm, "BC");

    // Act
    def msg = shouldFail(InvalidKeyException) {
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, saltParams);
    }

    // Assert
    assert msg =~ "Illegal key size"
}

Ho pensato che potesse essere un falso positivo, quindi ho usato OpenSSL per crittografare i file usando 128 bit e 128 crittografia con password "lunghe" e "brevi" e ho provato a decodificarle con Java. I risultati provengono da un sistema con crittografia di forza "limitata":

$ openssl enc -aes-128-cbc -e -in plain.txt -out salted_raw_128_long.enc -k thisIsABadPassword -p
$ openssl enc -aes-128-cbc -e -in plain.txt -out salted_raw_128_short.enc -k password -p
$ openssl enc -aes-256-cbc -e -in plain.txt -out salted_raw_256_long.enc -k thisIsABadPassword -p
$ openssl enc -aes-256-cbc -e -in plain.txt -out salted_raw_256_short.enc -k password -p


Cipher  | Password length | Should Work | Does Work
--------|-----------------|-------------|-----------
AES-128 |   <= 16 chars   |     YES     |    YES
AES-128 |    > 16 chars   |     YES     |     NO
AES-256 |   <= 16 chars   |      NO     |    YES
AES-256 |    > 16 chars   |      NO     |     NO

Ho alcune domande:

  1. Qualcun altro può riprodurre questo comportamento?
  2. Questo comportamento è previsto o un bug?
  3. Se previsto, è sufficientemente documentato da qualche parte?

Aggiornamento Dopo ulteriori ricerche su una macchina senza le policy di giurisdizione illimitate, ho determinato queste lunghezze massime della password per i seguenti algoritmi PBE:

Algorithm        |        Max Password Length
---------------------------------------------
PBEWITHMD5AND128BITAES-CBC-OPENSSL |    16
PBEWITHMD5AND192BITAES-CBC-OPENSSL |    16
PBEWITHMD5AND256BITAES-CBC-OPENSSL |    16
PBEWITHMD5ANDDES                   |    16
PBEWITHMD5ANDRC2                   |    16
PBEWITHSHA1ANDRC2                  |    16
PBEWITHSHA1ANDDES                  |    16
PBEWITHSHAAND128BITAES-CBC-BC      |     7
PBEWITHSHAAND192BITAES-CBC-BC      |     7
PBEWITHSHAAND256BITAES-CBC-BC      |     7
PBEWITHSHAAND40BITRC2-CBC          |     7
PBEWITHSHAAND128BITRC2-CBC         |     7
PBEWITHSHAAND40BITRC4              |     7
PBEWITHSHAAND128BITRC4             |     7
PBEWITHSHA256AND128BITAES-CBC-BC   |     7
PBEWITHSHA256AND192BITAES-CBC-BC   |     7
PBEWITHSHA256AND256BITAES-CBC-BC   |     7
PBEWITHSHAAND2-KEYTRIPLEDES-CBC    |     7
PBEWITHSHAAND3-KEYTRIPLEDES-CBC    |     7
PBEWITHSHAANDTWOFISH-CBC           |     7
    
posta Andy 05.12.2015 - 01:12
fonte

1 risposta

1

Diritto internazionale

Il framework JCA non è progettato per applicare in modo inequivocabile il diritto internazionale, tuttavia le differenze arbitrarie tra legge e software dovrebbero essere corrette per facilitare la conformità. L'autenticazione reciproca assicura la corretta gestione delle restrizioni di forza solo se il fornitore la gestisce correttamente.

In caso di violazione della conformità, le dichiarazioni di non responsabilità di Oracle e i fornitori dei fornitori non daranno la colpa a coloro che integrano questi prodotti nei sistemi.

Questo sondaggio su Regolamento sulla crittografia internazionale e sull'economia dell'informazione globale è un buon inizio per capire quali sono le responsabilità del CTO, dello specialista della sicurezza e dell'architetto riguardo ai limiti crittografici in un mercato internazionale.

U.S.. Legge

Il Dipartimento del commercio statunitense per la politica industriale e di sicurezza è definito in serie di documenti un po 'complicati. La sezione pertinente per la crittografia computerizzata è denominata Categoria 5 ed è documentata qui .

Documentazione JCA

Questi sono i documenti Oracle relativi al framework JCA, tuttavia il framework prevede la cooperazione da parte dei provider.

Documentazione del provider di crittografia e test della forza bruta

Ogni fornitore può avere o meno la propria documentazione. Non sono rimasto molto impressionato dalla documentazione di BountyCastle l'anno scorso mentre stavo leggendo per descrizioni chiare di limiti e opzioni. Non sarebbe sorprendente che l'esecuzione di test con permutazioni chiave di algoritmi e file di policy sia l'unico modo per verificare in sicurezza la conformità con il diritto internazionale.

La domanda contiene in realtà più informazioni sui risultati dei test effettivi di quanti ne abbia mai visti. (Buon lavoro.)

Definizione di un bit di Shannon

Si noti che la maggior parte delle restrizioni sono misurate in bit, che dovrebbero essere. La crittografia è in gran parte un gioco probabilistico, e la definizione di Shannon di un bit è un rapporto di probabilità 2: 1. Questo si applica direttamente alle password, se si desidera essere precisi. Se i provider di crittografia applicano correttamente la matematica è discutibile.

Il numero di bit in un carattere dipende dall'intervallo della codifica. Una cifra esadecimale è log 2 (16) = 4 bit. Un carattere stampabile ASCII ha 95 valori possibili, quindi è in realtà log 2 (95) = 6,57 bit (non un byte). I caratteri stampabili UTF-8 superano notevolmente gli 8 bit.

Se i legislatori capiscono che la matematica è anche discutibile.

    
risposta data 11.02.2017 - 09:05
fonte

Leggi altre domande sui tag