Как сопоставить свойство byte[] с Hibernate?

Я использую Hibernate/Java для сохранения объекта в базе данных. Объект имеет поле пароль, которое представляет собой строку. При регистрации пользователя в моем приложении я хэширую пароль с помощью SHA-1 (я признаю, что это немного слабо). Это создает byte[], который я затем преобразую в String с помощью new String(byte[] arr); Всякий раз, когда я хочу войти в систему, я просто извлекаю хешированный пароль из базы данных (как >String) и сравните его с дайджестом введенного пароля при входе в систему с помощью hashedPasswordFromDatabase.equals(SHA1_HASH(inputPassword));

Это отлично работало в моей системе разработки (Windows 7, JDK 1.6.0_23/JDK 1.7, MySQL 5.5, Tomcat 6.0.26), но после развертывания на нашем сервере (запуск JDK 1.6 в Linux) Метод equals никогда не возвращает TRUE даже для одинаковых паролей. Я быстро настроил новую систему разработки (Ubuntu 12.04, MySQL 5.5, JDK 1.7.0_03, Tomcat 7.0.22), и там тоже не работает.

Мне известны возможные проблемы с кодировкой, указанные в документации API Java для класса String, а также указанные в нескольких местах здесь, в SO. Я попробовал несколько кодировок, предложенных на этом форуме (например, Base64, Latin-1), и в итоге получил UnsupportedEncodingException. Я думаю, что мне лучше избегать преобразования String. Итак, как мне спроектировать базу данных таким образом, чтобы класс сущностей, сгенерированный Hibernate, использовал byte[] для поля password вместо String?


person Sayo Oladeji    schedule 14.05.2012    source источник
comment
+1, очень хороший вопрос. Кроме того, это не ответ на ваш вопрос, мне очень повезло с использованием некоторых утилит Commons Base64 для входа и выхода из базы данных.   -  person Chris Thompson    schedule 15.05.2012
comment
Зачем хранить строку, представляющую число, а не само число?   -  person m0skit0    schedule 15.05.2012
comment
Не используйте один раунд хеширования для защиты паролей. Используйте что-то вроде PBKDF2 или bcrypt с десятками тысяч раундов --- даже 100k не является неразумным. Хранение фиксированной длины byte[] напрямую должно быть простым для большей базы данных, но вы всегда можете создать BigInteger из массива байтов и сохранить его как числовой тип.   -  person erickson    schedule 15.05.2012
comment
@erickson, да, хорошо. Я намерен повысить надежность защиты паролем (удаление SHA-1, соление, многократное хеширование и т. д.) после решения этой проблемы.   -  person Sayo Oladeji    schedule 15.05.2012
comment
Какой тип данных в базе данных?   -  person Steve Ebersole    schedule 05.06.2012


Ответы (3)


Да, проблема скорее всего в конвертации byte[] в String. Вы должны знать, что SHA создает необработанный массив byte, и нет никакой гарантии, что произвольный byte[] создаст действительный String, независимо от кодировки. Таким образом, ваш код работал случайно.

Избавьтесь от проблемы полностью:

  • хранение необработанных byte[] в BLOB - самый безопасный и эффективный способ хранения. В Hibernate просто используйте свойство byte[] в вашем POJO.

  • закодируйте byte[], используя base64 (см. Декодируйте данные Base64 в Java) и сохраните их в виде строки.

Кстати, помните о солении!

person Tomasz Nurkiewicz    schedule 14.05.2012
comment
Большое спасибо. Вы имеете в виду, что BLOB фактически заставит Hibernate генерировать поле в сущности как byte[]? - person Sayo Oladeji; 15.05.2012
comment
@SayoStealth-virusOladeji: Если вы используете byte[], hbm2ddl должен сгенерировать BLOB. И наоборот - если у вас есть BLOB в вашей базе данных, вы можете безопасно сопоставить его с byte[]. - person Tomasz Nurkiewicz; 15.05.2012
comment
Большой! В настоящее время я просматриваю страницу Base64, которой вы поделились; его стоит прочитать. - person Sayo Oladeji; 15.05.2012

В моем случае плохой дизайн базы данных подтолкнул меня к использованию Blob в случае Clob. Решение заключалось в том, чтобы сопоставить в спящем режиме свойство с аннотацией Lob и поместить другое свойство в тип String.

На другом уровне кода, когда я вызываю get или set, я использую свойство String, а это свойство, получаю или устанавливаю значение массива байтов.

@Entity
@Table(name = "CMUN_TAGS")
@SequenceGenerator(name = "idSeqTag", sequenceName = "SEQ_CMUN_TAGS")
public class CmunTagsDO implements java.io.Serializable {
  private BigDecimal lngIdTag;
  private byte[] blobValTag;
  private String strValTag;

  @Id
  @Column(name = "LNG_ID_TAG", unique = true, nullable = false, precision = 20, scale = 0)
  @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "idSeqTag")
  public BigDecimal getLngIdTag() {
    return this.lngIdTag;
  }

  public void setLngIdTag(BigDecimal lngIdTag) {
    this.lngIdTag = lngIdTag;
  }

  @Column(name = "BLOB_VAL_TAG", nullable = false)
  @Lob
  public byte[] getBlobValTag() {
    return this.blobValTag;
  }

  public void setBlobValTag(byte[] blobValTag) {
    this.blobValorTag = blobValorTag;
  }

  @Transient
  public String getStrValTag() {
    strValTag = new String(getBlobValTag());
    return strValTag;
  }

  public void setStrValTag(String strValTag) {
    setBlobValTag(strValTag.getBytes());
    this.strValTag = strValTag;
  }
}
person Elidio Marquina    schedule 05.06.2012

Вы можете преобразовать свой байт в шестнадцатеричное представление следующим образом:

public String encryptPassword(String passwordInClear) {
            // Salt all you want here.
            MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
    byte[] digest = sha256.digest(passwordInClear.getBytes());
    return digestToString(digest);
}

private String digestToString(byte[] digest) {
    StringBuilder hashString = new StringBuilder();
    for (int i = 0; i < digest.length; i++) {
        String hex = Integer.toHexString(digest[i]);
        if (hex.length() == 1) {
            hashString.append('0');
            hashString.append(hex.charAt(hex.length() - 1));
        } else {
            hashString.append(hex.substring(hex.length() - 2));
        }
    }
    return hashString.toString();
}
person Guillaume Polet    schedule 14.05.2012
comment
Как это влияет на надежность хешированного пароля? Ослабляет ли этот подход его? - person Sayo Oladeji; 15.05.2012
comment
@SayoStealth-virusOladeji Нет, это не ослабляет и не усиливает его. Это простая вещь, которая сопоставляет байт с его эквивалентным строковым значением, представляя каждый байт его числовым значением (0 становится 00, 16 становится 10, 255 становится FF и т. д.). Переход от одного к другому является двунаправленным, но реальная сила или слабость заключается в том, как вы перевариваете пароль. - person Guillaume Polet; 15.05.2012
comment
Большой! Большое спасибо. Я думаю, что я буду использовать этот подход, так как мне не придется трогать базу данных и все, что рядом с ней :) - person Sayo Oladeji; 15.05.2012