diff options
author | Kenny Root <kenny@the-b.org> | 2008-12-27 12:31:25 +0000 |
---|---|---|
committer | Kenny Root <kenny@the-b.org> | 2008-12-27 12:31:25 +0000 |
commit | ad9a45cf8055707d9f77da90c3f44c8208a5f23c (patch) | |
tree | ffa68a54389e0a8cd01be239646dd86c90e88af3 /src | |
parent | e82927d1e6c3bd8ac2f85b77beabc75b7424c9b9 (diff) | |
download | connectbot-ad9a45cf8055707d9f77da90c3f44c8208a5f23c.tar.gz connectbot-ad9a45cf8055707d9f77da90c3f44c8208a5f23c.tar.bz2 connectbot-ad9a45cf8055707d9f77da90c3f44c8208a5f23c.zip |
Change secret key encryption algorithm to be more secure
Diffstat (limited to 'src')
-rw-r--r-- | src/org/connectbot/bean/PubkeyBean.java | 15 | ||||
-rw-r--r-- | src/org/connectbot/util/Encryptor.java | 204 | ||||
-rw-r--r-- | src/org/connectbot/util/PubkeyUtils.java | 45 |
3 files changed, 245 insertions, 19 deletions
diff --git a/src/org/connectbot/bean/PubkeyBean.java b/src/org/connectbot/bean/PubkeyBean.java index bb9f3c0..183eeea 100644 --- a/src/org/connectbot/bean/PubkeyBean.java +++ b/src/org/connectbot/bean/PubkeyBean.java @@ -17,14 +17,7 @@ */ package org.connectbot.bean; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; -import java.security.spec.InvalidKeySpecException; - -import javax.crypto.BadPaddingException; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; import org.connectbot.util.PubkeyDatabase; import org.connectbot.util.PubkeyUtils; @@ -144,16 +137,12 @@ public class PubkeyBean extends AbstractBean { return values; } - public boolean changePassword(String oldPassword, String newPassword) throws NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException { + public boolean changePassword(String oldPassword, String newPassword) throws Exception { PrivateKey priv; try { priv = PubkeyUtils.decodePrivate(getPrivateKey(), getType(), oldPassword); - } catch (InvalidKeyException e) { - return false; - } catch (BadPaddingException e) { - return false; - } catch (InvalidKeySpecException e) { + } catch (Exception e) { return false; } diff --git a/src/org/connectbot/util/Encryptor.java b/src/org/connectbot/util/Encryptor.java new file mode 100644 index 0000000..ebcfc12 --- /dev/null +++ b/src/org/connectbot/util/Encryptor.java @@ -0,0 +1,204 @@ +/* + ConnectBot: simple, powerful, open-source SSH client for Android + Copyright (C) 2007-2008 Kenny Root, Jeffrey Sharkey + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +package org.connectbot.util; + +/** + * This class is from: + * + * Encryptor.java + * Copyright 2008 Zach Scrivena + * zachscrivena@gmail.com + * http://zs.freeshell.org/ + */ + +import java.security.MessageDigest; +import java.security.SecureRandom; +import java.util.Arrays; +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + + +/** + * Perform AES-128 encryption. + */ +public final class Encryptor +{ + /** name of the character set to use for converting between characters and bytes */ + private static final String CHARSET_NAME = "UTF-8"; + + /** random number generator algorithm */ + private static final String RNG_ALGORITHM = "SHA1PRNG"; + + /** message digest algorithm (must be sufficiently long to provide the key and initialization vector) */ + private static final String DIGEST_ALGORITHM = "SHA-256"; + + /** key algorithm (must be compatible with CIPHER_ALGORITHM) */ + private static final String KEY_ALGORITHM = "AES"; + + /** cipher algorithm (must be compatible with KEY_ALGORITHM) */ + private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding"; + + + /** + * Private constructor that should never be called. + */ + private Encryptor() + {} + + + /** + * Encrypt the specified cleartext using the given password. + * With the correct salt, number of iterations, and password, the decrypt() method reverses + * the effect of this method. + * This method generates and uses a random salt, and the user-specified number of iterations + * and password to create a 16-byte secret key and 16-byte initialization vector. + * The secret key and initialization vector are then used in the AES-128 cipher to encrypt + * the given cleartext. + * + * @param salt + * salt that was used in the encryption (to be populated) + * @param iterations + * number of iterations to use in salting + * @param password + * password to be used for encryption + * @param cleartext + * cleartext to be encrypted + * @return + * ciphertext + * @throws Exception + * on any error encountered in encryption + */ + public static byte[] encrypt( + final byte[] salt, + final int iterations, + final String password, + final byte[] cleartext) + throws Exception + { + /* generate salt randomly */ + SecureRandom.getInstance(RNG_ALGORITHM).nextBytes(salt); + + /* compute key and initialization vector */ + final MessageDigest shaDigest = MessageDigest.getInstance(DIGEST_ALGORITHM); + byte[] pw = password.getBytes(CHARSET_NAME); + + for (int i = 0; i < iterations; i++) + { + /* add salt */ + final byte[] salted = new byte[pw.length + salt.length]; + System.arraycopy(pw, 0, salted, 0, pw.length); + System.arraycopy(salt, 0, salted, pw.length, salt.length); + Arrays.fill(pw, (byte) 0x00); + + /* compute SHA-256 digest */ + shaDigest.reset(); + pw = shaDigest.digest(salted); + Arrays.fill(salted, (byte) 0x00); + } + + /* extract the 16-byte key and initialization vector from the SHA-256 digest */ + final byte[] key = new byte[16]; + final byte[] iv = new byte[16]; + System.arraycopy(pw, 0, key, 0, 16); + System.arraycopy(pw, 16, iv, 0, 16); + Arrays.fill(pw, (byte) 0x00); + + /* perform AES-128 encryption */ + final Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); + + cipher.init( + Cipher.ENCRYPT_MODE, + new SecretKeySpec(key, KEY_ALGORITHM), + new IvParameterSpec(iv)); + + Arrays.fill(key, (byte) 0x00); + Arrays.fill(iv, (byte) 0x00); + + return cipher.doFinal(cleartext); + } + + + /** + * Decrypt the specified ciphertext using the given password. + * With the correct salt, number of iterations, and password, this method reverses the effect + * of the encrypt() method. + * This method uses the user-specified salt, number of iterations, and password + * to recreate the 16-byte secret key and 16-byte initialization vector. + * The secret key and initialization vector are then used in the AES-128 cipher to decrypt + * the given ciphertext. + * + * @param salt + * salt to be used in decryption + * @param iterations + * number of iterations to use in salting + * @param password + * password to be used for decryption + * @param ciphertext + * ciphertext to be decrypted + * @return + * cleartext + * @throws Exception + * on any error encountered in decryption + */ + public static byte[] decrypt( + final byte[] salt, + final int iterations, + final String password, + final byte[] ciphertext) + throws Exception + { + /* compute key and initialization vector */ + final MessageDigest shaDigest = MessageDigest.getInstance(DIGEST_ALGORITHM); + byte[] pw = password.getBytes(CHARSET_NAME); + + for (int i = 0; i < iterations; i++) + { + /* add salt */ + final byte[] salted = new byte[pw.length + salt.length]; + System.arraycopy(pw, 0, salted, 0, pw.length); + System.arraycopy(salt, 0, salted, pw.length, salt.length); + Arrays.fill(pw, (byte) 0x00); + + /* compute SHA-256 digest */ + shaDigest.reset(); + pw = shaDigest.digest(salted); + Arrays.fill(salted, (byte) 0x00); + } + + /* extract the 16-byte key and initialization vector from the SHA-256 digest */ + final byte[] key = new byte[16]; + final byte[] iv = new byte[16]; + System.arraycopy(pw, 0, key, 0, 16); + System.arraycopy(pw, 16, iv, 0, 16); + Arrays.fill(pw, (byte) 0x00); + + /* perform AES-128 decryption */ + final Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); + + cipher.init( + Cipher.DECRYPT_MODE, + new SecretKeySpec(key, KEY_ALGORITHM), + new IvParameterSpec(iv)); + + Arrays.fill(key, (byte) 0x00); + Arrays.fill(iv, (byte) 0x00); + + return cipher.doFinal(ciphertext); + } +} diff --git a/src/org/connectbot/util/PubkeyUtils.java b/src/org/connectbot/util/PubkeyUtils.java index 0ab667c..d6bf332 100644 --- a/src/org/connectbot/util/PubkeyUtils.java +++ b/src/org/connectbot/util/PubkeyUtils.java @@ -44,6 +44,7 @@ import java.security.spec.KeySpec; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.RSAPublicKeySpec; import java.security.spec.X509EncodedKeySpec; +import java.util.Arrays; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; @@ -55,6 +56,8 @@ import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.PBEParameterSpec; import javax.crypto.spec.SecretKeySpec; +import android.util.Log; + import com.trilead.ssh2.crypto.Base64; import com.trilead.ssh2.signature.DSASHA1Verify; import com.trilead.ssh2.signature.RSASHA1Verify; @@ -63,6 +66,12 @@ public class PubkeyUtils { public static final String PKCS8_START = "-----BEGIN PRIVATE KEY-----"; public static final String PKCS8_END = "-----END PRIVATE KEY-----"; + // Size in bytes of salt to use. + private static final int SALT_SIZE = 8; + + // Number of iterations for password hashing. PKCS#5 recommends 1000 + private static final int ITERATIONS = 1000; + public static String formatKey(Key key){ String algo = key.getAlgorithm(); String fmt = key.getFormat(); @@ -99,12 +108,36 @@ public class PubkeyUtils { return c.doFinal(data); } - public static byte[] encrypt(byte[] cleartext, String secret) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException { - return cipher(Cipher.ENCRYPT_MODE, cleartext, secret.getBytes()); + public static byte[] encrypt(byte[] cleartext, String secret) throws Exception { + byte[] salt = new byte[SALT_SIZE]; + + byte[] ciphertext = Encryptor.encrypt(salt, ITERATIONS, secret, cleartext); + + byte[] complete = new byte[salt.length + ciphertext.length]; + + System.arraycopy(salt, 0, complete, 0, salt.length); + System.arraycopy(ciphertext, 0, complete, salt.length, ciphertext.length); + + Arrays.fill(salt, (byte) 0x00); + Arrays.fill(ciphertext, (byte) 0x00); + + return complete; } - public static byte[] decrypt(byte[] ciphertext, String secret) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { - return cipher(Cipher.DECRYPT_MODE, ciphertext, secret.getBytes()); + public static byte[] decrypt(byte[] complete, String secret) throws Exception { + try { + byte[] salt = new byte[SALT_SIZE]; + byte[] ciphertext = new byte[complete.length - salt.length]; + + System.arraycopy(complete, 0, salt, 0, salt.length); + System.arraycopy(complete, salt.length, ciphertext, 0, ciphertext.length); + + return Encryptor.decrypt(salt, ITERATIONS, secret, ciphertext); + } catch (Exception e) { + Log.d("decrypt", "Could not decrypt with new method", e); + // We might be using the old encryption method. + return cipher(Cipher.DECRYPT_MODE, complete, secret.getBytes()); + } } public static byte[] getEncodedPublic(PublicKey pk) { @@ -115,7 +148,7 @@ public class PubkeyUtils { return new PKCS8EncodedKeySpec(pk.getEncoded()).getEncoded(); } - public static byte[] getEncodedPrivate(PrivateKey pk, String secret) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException { + public static byte[] getEncodedPrivate(PrivateKey pk, String secret) throws Exception { if (secret.length() > 0) return encrypt(getEncodedPrivate(pk), secret); else @@ -128,7 +161,7 @@ public class PubkeyUtils { return kf.generatePrivate(privKeySpec); } - public static PrivateKey decodePrivate(byte[] encoded, String keyType, String secret) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeySpecException { + public static PrivateKey decodePrivate(byte[] encoded, String keyType, String secret) throws Exception { if (secret != null && secret.length() > 0) return decodePrivate(decrypt(encoded, secret), keyType); else |