2012-05-14 133 views
7

我正在使用Hibernate/Java将实体持久化到数据库。该实体有一个密码字段,它是一个字符串。当在我的应用程序中注册用户时,我使用SHA-1散列密码(我承认这有点弱)。这将产生一个的byte []然后我用 new String(byte[] arr); 每当我要登录用户转换为字符串,我只是从数据库中检索哈希密码(如字符串),并将其与消化比较(Windows 7中,JDK 1.6.0_23/JDK 1.7中,MySQL 5.5,Tomcat的6.0.26)使用 hashedPasswordFromDatabase.equals(SHA1_HASH(inputPassword));如何使用Hibernate映射byte []属性?

在登录时输入的密码,这完全工作在我的开发系统,但在我们的服务器上部署它(在Linux上运行JDK 1.6),等于方法nev即使对于相同的密码,er也会评估为TRUE。我很快设置了一个新的开发系统(Ubuntu 12.04,MySQL 5.5,JDK 1.7.0_03,Tomcat 7.0.22),并且它在那里也不起作用。

我知道在Java API文档中提到的字符串类中可能出现的编码问题,也在SO上的几个地方声明。我已经尝试了一些在这个论坛上提出的编码(例如Base64,Latin-1),并且我最终得到了UnsupportedEncodingException。我想我会避免字符串转换更好。那么如何设计我的数据库,使Hibernate生成的实体类与byte []密码字段而不是字符串

+1

+1,非常好的问题。除此之外,这不是您的问题的答案,我使用一些Commons Base64公用程序进出数据库非常幸运。 –

+2

为什么要存储代表数字的字符串而不是存储数字本身? – m0skit0

+0

不要使用一轮哈希来保护密码。使用像PBKDF2或bcrypt这样的东西,其数千甚至数十万次 - 即使是100k也不是不合理的。直接存储固定长度的字节[]应该对于更多的数据库来说很容易,但是您可以始终从字节数组中创建一个BigInteger,并将其作为数字类型存储。 – erickson

回答

5

是的,问题很可能在byte[]String转换。您必须知道SHA生成原始的byte数组,并且无法保证任意byte[]将产生有效的String,与编码无关。因此,您的代码偶然在工作意外

通过完全避免问题:

  • 在BLOB存储原始byte[] - 最安全和最存储有效的方式。在Hibernate中,您只需在您的POJO上使用byte[]属性。

  • 编码byte[]使用(检出Decode Base64 data in Java)并将其存储为字符串。

BTW remember about salting

+0

非常感谢。你的意思是BLOB实际上会让Hibernate在实体中产生字段为'byte []'? –

+0

@ SayoStealth-virusOladeji:如果使用'byte []',hbm2ddl应该生成BLOB。反之亦然 - 如果数据库中有BLOB,则可以安全地将其映射到'byte []'。 –

+0

太棒了!我目前正在浏览您共享的Base64页面;它的值得阅读。 –

0

您可以将字节转换为这样的一个十六进制表示:

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(); 
} 
+0

这是如何影响哈希密码的强度的?这种方法是否会削弱它? –

+0

@ SayoStealth-virusOladeji不,它不削弱它或加强它。这是一个简单的事情,通过用数字值表示每个字节(0变成“00”,16变成“10”,255变成“FF”等等),将字节映射到其“等同”字符串值。从一个到另一个传递是双向的,但真正的“强度”或“弱点”在于你消化密码。 –

+0

太棒了!非常感谢。我想我会采用这种方法,因为我不需要触摸数据库和其他任何接近它的东西:) –

1

对我来说,坏的数据库设计把我推到在对CLOB情况下使用的Blob。解决方案是在Hibernate中使用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; 
    } 
}