Java RSA为啥每次密文都不一样?
Posted
技术标签:
【中文标题】Java RSA为啥每次密文都不一样?【英文标题】:Java RSA why different cipher text every time?Java RSA为什么每次密文都不一样? 【发布时间】:2016-02-08 08:48:16 【问题描述】:我正在使用带有 RSA 算法的 JSP/Servlets/mysql 开发一个登录模块。注册新用户后,我将 PublicKey 保存到 MySQL 并用它加密密码。
当用户尝试登录时,我检索该 PublicKey 并使用相应的 PublicKey 加密刚刚输入的密码,并将其与之前保存的加密密码进行比较,但它总是返回不同的密文。
我不明白为什么我每次都得到不同的加密密码。有什么我做错了吗?每次运行“keyFactory.generatePublic”时是否生成一个新的PublicKey?
感谢您的帮助
我生成公钥的方法是:
public byte[] generateBytePublicKey() throws Exception
byte[] pk = null;
try
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM);
keyGen.initialize(1024);
pk = keyGen.generateKeyPair().getPublic().getEncoded();
(...... etc etc)
return pk;
我的密码加密方法:
public byte[] encryptBytes(String pwd, byte[] key) throws Exception
byte[] cipherText = null;
PublicKey pk;
try
byte[] dataBytes = pwd.getBytes();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
pk = keyFactory.generatePublic(new X509EncodedKeySpec(key));
final Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pk);
cipherText = cipher.doFinal(dataBytes);
(..... etc etc)
return cipherText;
我将加密密码和公钥存储到 MySQL 表中的方法:
(.....................)
try
Statement stmt = null;
ResultSet rs = null;
byte[] bytePublicKey;
byte[] cipherPwd;
bytePublicKey = generateBytePublicKey();
cipherPwd = encryptBytes(password, bytePublicKey);
String query = "INSERT INTO Users (email, pwd, publickey) VALUES ('" + email + "', ?, ?)";
PreparedStatement ps;
ps = conn.getConexion().prepareStatement(query);
ps.setBytes(1, cipherPwd);
ps.setBytes(2, bytePublicKey);
resultSet = ps.executeUpdate();
(............. etc etc)
我检查用户是否有效的方法:
public boolean isUserValid (String email, String typedPassword) throws Exception
byte[] storedBytesPassword;
byte[] storedBytesPublicKey;
byte[] typedPwdtoBytes;
try
storedBytesPublicKey = getByteArrays(email, "publicKey");
storedBytesPassword = getByteArrays(email, "pwd");
typedPwdtoBytes = encryptBytes(typedPassword, storedBytesPublicKey);
return Arrays.equals(typedPwdtoBytes, storedBytesPassword);
(............. etc etc)
我从 MySQL 表中获取字节数组的方法:
public byte[] getByteArrays (String email, String byteArray) throws SQLException
(..............)
try
Statement stmt=null;
ResultSet rs=null;
query = "SELECT " + byteArray + " FROM Users WHERE email = '" + email + "'";
try
stmt = (conn.getConexion()).createStatement();
rs = stmt.executeQuery(query);
while (rs.next() )
bytesArr = rs.getBytes(1);
(.................. etc etc)
【问题讨论】:
您生成一个 RSA 密钥对只是为了丢弃私钥并从本质上使加密成为一条单向街道。有更简单的替代方案,例如使用带有随机盐和大量迭代的 PBKDF2。在Information Security:How to securely hash passwords? 【参考方案1】:当您未指定完整转换时,您将获得提供程序默认值,对于 RSA 可能是 RSA/ECB/PKCS1Padding
。
PKCS1Padding
(见Padding schemes)表示加入随机数据,保证相同明文加密两次会产生不同的密文。
密码用于加密和解密。由于您不打算解密密码,因此密码不是您的正确选择。
对于单向“加密”,使用message digest,也称为Cryptographic hash function。它们通过生成明文的哈希/摘要来像 Java hashCode()
方法一样工作,但与 hashCode()
不同的是,摘要要复杂得多,以确保无法从摘要值中推断出明文。
由于它是一种单向算法,您可以将密钥和摘要值存储在一起。摘要的目的是确保您始终可以从相同的明文/密钥组合中获得相同的摘要值。
您甚至可以对所有密码使用相同的密钥。请记住,它是不可逆的,只要摘要算法足够强大,例如SHA-256
.
【讨论】:
单次调用哈希函数对于密码来说是不够的。在这种情况下应该使用 PBKDF2、bcrypt、scrypt 等方案。 非常感谢!我必须更好地了解加密世界,并将重新设计我的登录模块。 我在发布答案后找到了这个链接。信息量很大:crackstation.net/hashing-security.htm以上是关于Java RSA为啥每次密文都不一样?的主要内容,如果未能解决你的问题,请参考以下文章