С помощью нижеприведенного класса можно легко зашифровать некоторую строку (например пароль) с использование алгоритма DES, и затем при желании расшифровать её. При этом секретный ключ сохранен в теле класса, так что строку можно будет расшифровать даже после перезапуска программы.
UPD 06.02.13
Выкладываю обновленную версию класса. В новой версии используется сторонняя билиотека Apache Codec http://commons.apache.org/codec/, также методы класса сделаны нестатическими, благодаря чему стало возможным с данным классом создавать несколько шифрователей с разными ключами или алгоритмами в одном приложении и динамически менять ключ шифрования. Минимальная версия jdk - 1.7, однако после внесения небольших изменений может использоваться и на более ранних версиях java.
Пример использования нового класса
// Файл SecuritySettings.java package security; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.logging.Level; import java.util.logging.Logger; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; /** * * @author Cloud */ public final class SecuritySettings { private final static class MySecretKey implements SecretKey { private byte[] key = new byte[]{1, 2, 3, 4, 5, 6, 7, 8}; // ключ // не должен иметь длину более 8 байт, для безопасного шифрования его // необходимо изменить public String getAlgorithm() { return "DES"; } public String getFormat() { return "RAW"; } public byte[] getEncoded() { return key; } } private static SecretKey key; private static Cipher ecipher; private static Cipher dcipher; static { try { key = new MySecretKey(); ecipher = Cipher.getInstance("DES"); dcipher = Cipher.getInstance("DES"); ecipher.init(Cipher.ENCRYPT_MODE, key); dcipher.init(Cipher.DECRYPT_MODE, key); } catch (InvalidKeyException ex) { Logger.getLogger(SecuritySettings.class.getName()).log(Level.SEVERE, null, ex); } catch (NoSuchAlgorithmException ex) { Logger.getLogger(SecuritySettings.class.getName()).log(Level.SEVERE, null, ex); } catch (NoSuchPaddingException ex) { Logger.getLogger(SecuritySettings.class.getName()).log(Level.SEVERE, null, ex); } } /** * Функция шифрования * @param str строка открытого текста * @return зашифрованная строка в формате Base64 */ public static String encrypt(String str) { try { byte[] utf8 = str.getBytes("UTF8"); byte[] enc = ecipher.doFinal(utf8); return new sun.misc.BASE64Encoder().encode(enc); } catch (IllegalBlockSizeException ex) { Logger.getLogger(SecuritySettings.class.getName()).log(Level.SEVERE, null, ex); } catch (BadPaddingException ex) { Logger.getLogger(SecuritySettings.class.getName()).log(Level.SEVERE, null, ex); } catch (UnsupportedEncodingException ex) { Logger.getLogger(SecuritySettings.class.getName()).log(Level.SEVERE, null, ex); } return null; } /** * Функция расшифрования * @param str зашифрованная строка в формате Base64 * @return расшифрованная строка */ public static String decrypt(String str) { try { byte[] dec = new sun.misc.BASE64Decoder().decodeBuffer(str); byte[] utf8 = dcipher.doFinal(dec); return new String(utf8, "UTF8"); } catch (IllegalBlockSizeException ex) { Logger.getLogger(SecuritySettings.class.getName()).log(Level.SEVERE, null, ex); } catch (BadPaddingException ex) { Logger.getLogger(SecuritySettings.class.getName()).log(Level.SEVERE, null, ex); } catch (IOException ex) { Logger.getLogger(SecuritySettings.class.getName()).log(Level.SEVERE, null, ex); } return null; } }
UPD 06.02.13
Выкладываю обновленную версию класса. В новой версии используется сторонняя билиотека Apache Codec http://commons.apache.org/codec/, также методы класса сделаны нестатическими, благодаря чему стало возможным с данным классом создавать несколько шифрователей с разными ключами или алгоритмами в одном приложении и динамически менять ключ шифрования. Минимальная версия jdk - 1.7, однако после внесения небольших изменений может использоваться и на более ранних версиях java.
// Файл StringCrypter.java package rva.common.util; import java.io.IOException; import org.apache.commons.codec.binary.Base64; import java.io.UnsupportedEncodingException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.logging.Level; import java.util.logging.Logger; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; /** * Класс для шифрования и дешифрования строк * Использует библиотеку Apache Codec http://commons.apache.org/codec/ * @author Рудницкий Валентин */ public class StringCrypter { /** * Упрощенный конструктор. Создает StringCrypter с ключом DESSecretKey со значением по умолчанию (не рекомендуется) */ public StringCrypter() { this(new byte[]{1, 2, 3, 4, 5, 6, 7, 8}); } /** * Упрощенный конструктор. Создает StringCrypter с ключом * DESSecretKey (алгоритм шифрования DES) со значением key. * Ключ key должен иметь длину 8 байт */ public StringCrypter(byte[] key) { try { updateSecretKey(new DESSecretKey(key)); } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException ex) { throw new IllegalArgumentException(ex.getMessage()); } } public StringCrypter(SecretKey key) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException { updateSecretKey(key); } private void updateSecretKey(SecretKey key) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException { ecipher = Cipher.getInstance(key.getAlgorithm()); dcipher = Cipher.getInstance(key.getAlgorithm()); ecipher.init(Cipher.ENCRYPT_MODE, key); dcipher.init(Cipher.DECRYPT_MODE, key); } public static class DESSecretKey implements SecretKey { private final byte[] key; /** * ключ должен иметь длину 8 байт */ public DESSecretKey(byte[] key) { this.key = key; } @Override public String getAlgorithm() { return "DES"; } @Override public String getFormat() { return "RAW"; } @Override public byte[] getEncoded() { return key; } } private Cipher ecipher; private Cipher dcipher; /** * Функция шифрования * * @param str строка открытого текста * @return зашифрованная строка в формате Base64 */ public String encrypt(String str) { try { byte[] utf8 = str.getBytes("UTF8"); byte[] enc = ecipher.doFinal(utf8); return Base64.encodeBase64String(enc); } catch (IllegalBlockSizeException | BadPaddingException | UnsupportedEncodingException ex) { Logger.getLogger(StringCrypter.class.getName()).log(Level.SEVERE, null, ex); } return null; } /** * Функция дешифрования * * @param str зашифрованная строка в формате Base64 * @return расшифрованная строка */ public String decrypt(String str) { try { byte[] dec = Base64.decodeBase64(str); byte[] utf8 = dcipher.doFinal(dec); return new String(utf8, "UTF8"); } catch (IllegalBlockSizeException | BadPaddingException | IOException ex) { Logger.getLogger(StringCrypter.class.getName()).log(Level.SEVERE, null, ex); } return null; } }
Пример использования нового класса
// создаем экземпляр класса StringCrypter с ключем шифрования StringCrypter crypter=new StringCrypter(new byte[]{1,4,5,6,8,9,7,8}); String testStr = "forCryptString"; // шифрование String encBase64Str = crypter.encrypt(testStr); // дешифрование String decryptedStr = crypter.decrypt(encBase64Str);
у меня подчёркивает sun.
ОтветитьУдалитьне подскажете в чём дело?
выложил обновленную версию класса, без использования пакета sun
УдалитьЕсли я правильно понял - подчеркивает все записи из пакета sun?
ОтветитьУдалитьВозможно ваша IDE (среда разработки) сконфигурирована так, чтобы выдавать ошибки компилляции при использовании запрещенных (deprecated) и защищенных (restricted) методов. Пакет sun является защищенным.
Как вариант решения этой проблемы можете сменить в настройках IDE уровень ошибок для защищенных методов с ошибок компилляциии на предупреждения.
Либо, что более предпочтительно, использовать стороннюю библиотеку, например Apache Codec http://commons.apache.org/codec/ (org.apache.commons.codec.binary.Base64)для декодирования Base64 формата.
а реализации на PHP нет? Хоца отправить зашифровано постом в пхп.
ОтветитьУдалитьнету. Только java
Удалитьзасунул всё в свой проект в NetBeans IDE 7.1.1
ОтветитьУдалитьтак он упорно подчеркивает
catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException ex)
что делать?
Скорее всего у вас jdk ниже версии 1.7. А подчеркивается использование операторов multucatch, т.е. отлавливание одним блоком catch нескольких исключений, возможность, которая появилась только в 7 версии java.
УдалитьКак вариант решения проблемы можете разбить этот оператор на несколько, т.е. заменить
catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException ex) {
throw new IllegalArgumentException(ex.getMessage());
}
на
catch (InvalidKeyException ex) {
throw new IllegalArgumentException(ex.getMessage());
} catch (NoSuchAlgorithmException ex) {
throw new IllegalArgumentException(ex.getMessage());
} catch (NoSuchPaddingException ex) {
throw new IllegalArgumentException(ex.getMessage());
}
изменение платформу не решило проблему (1.7.0_04)
ОтветитьУдалитьа вот замена помогла только в первом случаи
ругается по пе прежнему на две строки
} catch (IllegalBlockSizeException | BadPaddingException | IOException ex) {
Помимо изменения платформы нужно в настройках проекта изменить формат исходного кода на JDK 7
Удалитьили
по аналогии с предыдущим замените
catch (IllegalBlockSizeException | BadPaddingException | IOException ex) {
Logger.getLogger(StringCrypter.class.getName()).log(Level.SEVERE, null, ex);
}
на
catch (IllegalBlockSizeException ex) {
Logger.getLogger(StringCrypter.class.getName()).log(Level.SEVERE, null, ex);
} catch (BadPaddingException ex) {
Logger.getLogger(StringCrypter.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(StringCrypter.class.getName()).log(Level.SEVERE, null, ex);
}
ах да огромное пасибо!!!
ОтветитьУдалитьи тут одн лишняя скобочка
StringCrypter crypter=new StringCrypter(new byte[]{1,4,5,6,8,9,7,8});
ок, подправил, спс
УдалитьСпс огромное )
ОтветитьУдалитьпривет. выкидывает ошибку
ОтветитьУдалитьjavax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)
at com.sun.crypto.provider.DESCipher.engineDoFinal(DESCipher.java:314)
at javax.crypto.Cipher.doFinal(Cipher.java:1922)
at ua.kiev.makson.logic.StringCrypter.decrypt(StringCrypter.java:113)
at ua.kiev.makson.logic.JavaSQL.showAll(JavaSQL.java:53)
at ua.kiev.makson.logic.Password.start(Password.java:64)
at ua.kiev.makson.logic.Password.main(Password.java:25)
Привет, судя по тексту ошибки похоже на то что строка которая передана для дешифрования имеет неправильный формат (не является защифрованной строкой, т.е. не была создана методом encrypt)
УдалитьПостоянно выкидывать эту ошибку. возможно мой трабл связан с тем что я сначала шифрую - добавляю в бд - потом по надобности достаю значение - пытаюсь дешифровать и ОШИБКА.
Удалитьесли в отдельном классе то все ок.
в чем может быть трабл?
Сложно сказать, сам класс шифрования должен работать нормально (я проверял :) ), возможно проблема в кодировках, т.е. строка читается из БД не в той кодировке в которой была туда сохранена, или может быть поле таблицы в которое записывается зашифрованная строка имеет длину меньшую чем строка и строка например обрезается, или просто к строке при чтении из БД по каким то причинам добавляются лишние символы.
УдалитьВообще вариантов может быть масса, но вероятно строка которая подается на вход метода decrypt каким то образом отличается от строки которая была возвращена методом encrypt, я бы смотрел в эту сторону. Также желательно убедиться что для шифрования и дешифрования SecretKey используется один и тот же .
Добрый день. 10 секунд это многовато конечно, у меня вроде бы подвисало, но не так сильно, но вообще задержки этого метода это нормально, т.к. операция создания ключа шифрования это довольно тяжелая операция.
ОтветитьУдалитьМогу, как вариант, порекомендовать создавать объект класса StringCrypter при старте приложения, и сохранять его в какую нибудь переменную (статическую если класс не синглтон), и затем использовать его многократно для шифрования и дешифрования строк.
Однако надо учитывать, что класс StringCrypter не потокобезовасный (т.к. используемые им классы Cipher не потокобезовасные), поэтому если у вас доступ к StringCrypter предположительно будет производиться из нескольких потоков одновременно, то необходимо также добавить синхронизацию при шифровании и дешифровании.
Спасибо! Примерно понял проблему.
ОтветитьУдалитьПравильно я понимаю, что если идти по пути создания переменной при инициализации, то это значит, что когда в программе этот класс может использоваться в разных частях программы одновременно (например сервер), то могут быть ошибки синхронизации? Если да, то если не сложно, посоветуйте несколько источников, где можно почитать про синхронизацию.
Да, если класс будет использоваться в разных частях программы одновременно в разных потоках, могут появляться ошибки. Насчет источников, в официальной справке оракл очень неплохо написано про синхронизацию, вот пара ссылок https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html.
УдалитьКонкретно для StringCrypter, на мой взгляд, проще всего будет добавить слово synchronized к методам encrypt() и decrypt().
Чуть лучше, но сложнее, будет добавить блок synchronized (cipher) внутри методов encrypt() и decrypt() и сделать поля ecipher и dcipher final, код метода updateSecretKey перенести в конструктор, и сам метод updateSecretKey удалить.
Спасибо за ответ. Очень интересно. Буду пробовать.
ОтветитьУдалитьПри попытке зашифровать строку выдаёт следующую ошибку
ОтветитьУдалитьCaused by: java.lang.NoSuchMethodError: No static method encodeBase64String([B)Ljava/lang/String; in class Lorg/apache/commons/codec/binary/Base64; or its super classes (declaration of 'org.apache.commons.codec.binary.Base64' appears in /system/framework/ext.jar)
С чем это связано и как исправить эту ошибку? (библиотека 'commons-codec-1.11.jar')
Случайно не под Андроид пишете?
УдалитьСкорее всего такая ошибка происходит из-за того что в андроид уже включена своя версия commons-codec, которая более старая чем требуется и в которой нет метода encodeBase64String. Попробуйте использовать этот класс: android.util.Base64 вместо org.apache.commons.codec.binary.Base64.
Метод encrypt скорее всего будет выглядеть примерно так:
public String encrypt(String str) {
try {
byte[] utf8 = str.getBytes("UTF8");
byte[] enc = ecipher.doFinal(utf8);
return Base64.encodeToString(enc, Base64.DEFAULT);
} catch (IllegalBlockSizeException | BadPaddingException | UnsupportedEncodingException ex) {
Logger.getLogger(StringCrypter.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
И еще примерно также придется поменять метод decrypt.