2016-06-20 1 views
0

텍스트 필드를 안전하게 저장하려고합니다. 이를 위해 콘텐츠를 암호화하고 해독하려고합니다.암호 해독시 안드로이드에서 텍스트를 암호화 할 때 빈 문자열이 반환됩니다.

public class SecureStorage { 

    public String getPassword() { 
     if(!isRooted()) { 
      String password = pref.getPassword(""); 
      System.out.println("pass getPass: " + password); 
      return password.isEmpty() ? password : new String(decrypt(Base64.decode(password, Base64.DEFAULT))); 

     } else 
      return ""; 
    } 

    public void setPassword(String passwordStr) { 
     if(!isRooted()) { 
      byte[] password = encrypt(passwordStr.getBytes()); 
      pref.setPassword(password == null ? "" : Base64.encodeToString(password, Base64.DEFAULT)); 
     } 
    } 

    private SecretKey generateKey() { 
     // Generate a 256-bit key 
     final int outputKeyLength = 256; 
     try { 
      SecureRandom secureRandom = new SecureRandom(); 
      // Do *not* seed secureRandom! Automatically seeded from system entropy. 
      KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); 
      keyGenerator.init(outputKeyLength, secureRandom); 
      return keyGenerator.generateKey(); 
     } catch (NoSuchAlgorithmException e) { 
      e.printStackTrace(); 
     } 
     return null; 
    } 

    private byte[] getRawKey(byte[] key) throws Exception { 
     KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); 
     SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG", "Crypto"); 
     secureRandom.setSeed(key); 
     keyGenerator.init(128, secureRandom); // 192 and 256 bits may not be available 
     SecretKey secretKey = keyGenerator.generateKey(); 
     byte[] rawKey = secretKey.getEncoded(); 
     return rawKey; 
    } 

    /** The method that encrypts the string. 
    @param toEncrypt The string to be encrypted. 
    @return The encrypted string in bytes. */ 
    //**************************************** 
    private byte[] encrypt(byte[] toEncrypt) { 
     byte[] encryptedByte = new String().getBytes(); 
     try { 
      SecretKeySpec secretKeySpec = new SecretKeySpec(getRawKey(Utils.generateUID().getBytes()), "AES"); 
      Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
      cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); 
      encryptedByte = cipher.doFinal(toEncrypt); 
     } catch (NoSuchAlgorithmException e) { 
      e.printStackTrace(); 
     } catch (InvalidKeyException e) { 
      e.printStackTrace(); 
     } catch (NoSuchPaddingException e) { 
      e.printStackTrace(); 
     } catch (BadPaddingException e) { 
      e.printStackTrace(); 
     } catch (IllegalBlockSizeException e) { 
      e.printStackTrace(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
     return encryptedByte; 
    } 

    //************************************** 
    /** The method that decrypts the string. 
    @param encryptedByte The string to be encrypted. 
    @return The decrypted string in bytes. */ 
    //**************************************** 
    private byte[] decrypt(byte[] encryptedByte) { 
     byte[] decryptedByte = new String().getBytes(); 
     try { 
      SecretKeySpec secretKeySpec = new SecretKeySpec(getRawKey(Utils.generateUID().getBytes()), "AES"); 
      Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
      cipher.init(Cipher.DECRYPT_MODE, secretKeySpec); 
      decryptedByte = cipher.doFinal(encryptedByte); 
     } catch (NoSuchAlgorithmException e) { 
      e.printStackTrace(); 
     } catch (InvalidKeyException e) { 
      e.printStackTrace(); 
     } catch (NoSuchPaddingException e) { 
      e.printStackTrace(); 
     } catch (BadPaddingException e) { 
      e.printStackTrace(); 
     } catch (IllegalBlockSizeException e) { 
      e.printStackTrace(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
     return decryptedByte; 
    } 
} 

내가 텍스트를 암호화 할 수 있어요 :이 코드입니다. 나는 SharedPreferences를 사용하여 암호화 된 텍스트를 저장하고 sharedprefs를 가져와 텍스트의 암호를 해독하여 TextView에 제공합니다. 그러나 getPassword()에서 SharedPreference 값을 얻었고 SharedPrefs에 값이 있으면 해독하려고합니다. 나는 SharedPrefs를 문자열 (password)로 만들고 해독하려고하고 있지만 나는 할 수 없다! 빈 문자열이 나옵니다!

+0

무엇이'pref.getPassword ("");'무엇입니까? – Jimmy

+0

가능한 예외를 모두 잡기 때문에 빈 문자열이 나타납니다. 로그에서 어떤 예외가 발생했는지 확인 했습니까? 나는 BadPaddingException이 있다고 의심한다. –

+0

그것을 얻는다! Im 생성 무작위 키와 ​​그것을 저장하지 않는다! 이제는 새 키를 다시 호출 할 때입니다. 이제 문자와 함께 키를 저장하고 있습니다! –

답변

1

CBC 모드는 작동시키기 위해 초기화 벡터 (IV)가 필요합니다. IV는 암호문을 무작위로 선택하여 공격자가 이전의 일반 텍스트가 현재 텍스트와 동일한 접두어를 가지고 있는지 여부를 판단하지 못하게합니다.

IV가 생성되지 않으므로 생성됩니다. 잘못된 IV는 첫 번째 블록 (AES의 처음 16 바이트)에만 영향을 미칩니다. 평문이 블록보다 짧으면 완전히 다른 암호 해독으로 이어지고 패딩을 약 255/256의 확률로 제거 할 수 없습니다.

IV는 비밀이 아닙니다. 해독 전에 해독을 암호문 앞에 붙이고 조각으로 나누는 것이 일반적입니다. 이와

public byte[] encrypt(byte[] toEncrypt) throws Exception { 
    try { 
     SecretKeySpec secretKeySpec = new SecretKeySpec(getRawKey(Utils.generateUID().getBytes()), "AES"); 
     Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
     cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); 
     byte[] iv = cipher.getIV(); 
     byte[] ct = cipher.doFinal(toEncrypt); 

     byte[] result = new byte[ct.length + iv.length]; 
     System.arraycopy(iv, 0, result, 0, iv.length); 
     System.arraycopy(ct, 0, result, iv.length, ct.length); 
     return result; 
    } catch(...) {...} 
    return new byte[0]; 
} 

public byte[] decrypt(byte[] encryptedByte) throws Exception { 
    try { 
     SecretKeySpec secretKeySpec = new SecretKeySpec(getRawKey(Utils.generateUID().getBytes()), "AES"); 
     Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 

     byte[] iv = new byte[cipher.getBlockSize()]; 
     byte[] ct = new byte[encryptedByte.length - cipher.getBlockSize()]; 
     System.arraycopy(encryptedByte, 0, iv, 0, cipher.getBlockSize()); 
     System.arraycopy(encryptedByte, cipher.getBlockSize(), ct, 0, ct.length); 

     cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(iv)); 
     return cipher.doFinal(ct); 
    } catch (...) {...} 
    return new byte[0]; 
} 

문제는 암호문은 (16 바이트 추가로 IV의 경우) 예상했던 것보다 더 큰 것을 수 있습니다. 공격자가 이전의 일반 텍스트가 동일한 접두사를 가졌다 고 판단 할 때 유용한 정보를 얻지 못하게 할 수 있다면 정적 IV를 사용할 수 있습니다. 그러나 이것은 보통 대단한 아이디어가 아니며, 이 정말로 그 공간이 인 경우에만 수행되어야 함을 알고 있어야합니다.

private static final byte[] IV = new byte[16]; 
... 
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(IV)); 
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(IV)); 
관련 문제