我想允许Alice创建一个公钥/私钥对,以便Bob可以向她发送机密消息。然而,我希望爱丽丝能够在任何地方查看她的信息,如果她不得不随身携带一个装有私钥的记忆棒,那将是一种痛苦。有没有什么方法可以让Alice根据她记忆中的密码创建一个公钥/私钥对?通过这种方式,她可以随时生成私钥(和公钥)
这个问题的简短版本是:在哪里可以找到cryptico.js.的Java等价物
此外,这里还有关于堆栈溢出的相同问题,但针对javascript。
编辑:这是我第一次尝试解决方案:
SecureRandom saltRand = new SecureRandom(new byte[] { 1, 2, 3, 4 });
byte[] salt = new byte[16];
saltRand.nextBytes(salt);
int keyLength = 3248;
SecretKeyFactory factory = SecretKeyFactory
.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 8192, keyLength);
SecretKey key = factory.generateSecret(spec);
SecureRandom keyGenRand = SecureRandom.getInstance("SHA1PRNG");
keyGenRand.setSeed(key.getEncoded());
KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
gen.initialize(keyLength, keyGenRand);
java.security.KeyPair p = gen.generateKeyPair();
当谈到RSA时:您可以使用PBKDF2的结果来种子化伪随机数生成器,该生成器又可以用于生成密钥对。请注意,使用SecureDrandom将不起作用,因为它将将种子添加到池中,而不是完全重新初始化rng。RSA需要一个PRNG来找到一个随机素数。
如果你能使用椭圆曲线密码,你会过得更好。你可以选择一个标准的NIST或Brainpool曲线在F(p)上。然后,您可以使用PBKDF2的32字节输出作为私钥,并计算公钥。ECC只需要一个随机私钥,并且由于PBKDF2的输出应该与随机不可区分,所以输出会很好。您不仅不需要额外的PRNG,还可以为自己节省计算RSA密钥对的时间,这可能非常重要。
请注意,没有什么能阻止对使用所述计算密钥加密的东西进行暴力攻击,因此您最好要求使用16个字符或更多的密码短语,其中包含非字典单词、数字和符号。任何不足之处都可能失败,尤其是在用户没有意识到可能的攻击的情况下。请注意,如果您没有存储空间,则不能使用随机盐。如果你不能使用随机盐,你就不能防御彩虹表(对于你的特定应用,你当然可以使用特定应用的盐)。此外,具有相同密码短语的人将生成相同的私钥。
当然,默认方式(例如在PGP中)是存储使用基于密码的加密加密的私钥。然而,这需要一个存储器。这种方法的优点是你可以拥有一个完全随机的密钥,这意味着如果没有对密钥存储的访问,对密文的暴力攻击是不可能的。它增加了一个重要的额外层。
您没有提供太多详细信息,但如果您想使用java.security.KeyPairGenerator
生成密钥对,则必须定义自己的类来扩展SecureRandom
,但仅使用提供的密码作为熵源。
您不需要实现SecureRandomSpi
类,只需使用(null, null)
参数调用超类的受保护构造函数即可。
RSA密钥长度通常为1024或2048位。这就是128或256个字节。
密码通常为8字节长(并且只使用大约64个不同的字节)。
如果RSA密钥是从密码中派生出来的,那么您将失去算法的大部分强度。攻击者只需要猜测或强行使用8字节的密码,而不是128或256字节长的密钥。
为什么不允许通过密码访问您的web应用程序(因为这实际上是您正在做的事情),只使用https?
使用共享密钥。
String encryptionKey = "53616d706c6550617373776f726453616d706c6550617373776f726453616d70"; // string to hex of "SamplePasswordSamplePasswordSamp"
String sampleText = "sampletext";
String encrypted = null;
String decrypted = null;
加密使用:
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(Hex.decodeHex(encryptionKey.toCharArray()), "AES"));
encrypted = Hex.encodeHexString(cipher.doFinal((sampleText.toString()).getBytes()));
要解密,请使用:
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(Hex.decodeHex(encryptionKey.toCharArray()), "AES"));
decrypted = new String(cipher.doFinal(Hex.decodeHex(enc.toCharArray())));
注意十六进制为:import org.apache.mons.codec.binary.Hex;在maven:
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.6</version>
</dependency>
免责声明:这种天真的方法是不安全的,因此不建议用于生产系统。然而,对于测试来说,它非常适合。
代码
private KeyPair getKeyPair(String password) throws GeneralSecurityException {
/*// https://stackoverflow.com/a/992413/2078908
byte[] salt = new byte[]{1, 2, 3};
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec seedSpec = new PBEKeySpec(password.toCharArray(), salt, 65536, 256);
byte[] seed = factory.generateSecret(seedSpec).getEncoded();*/
byte[] seed = password.getBytes(UTF_8);
SecureRandom rnd = SecureRandom.getInstance("SHA1PRNG");
rnd.setSeed(seed);
RSAKeyGenParameterSpec spec = new RSAKeyGenParameterSpec(2048, RSAKeyGenParameterSpec.F4);
KeyPairGenerator pairGenerator = KeyPairGenerator.getInstance("RSA");
pairGenerator.initialize(spec, rnd);
return pairGenerator.generateKeyPair();
}
测试
@Test
public void testPrivateKeys() throws Exception {
System.out.println("private key 1: " + md5(getKeyPair("pwd-1").getPrivate().getEncoded()));
System.out.println("private key 1: " + md5(getKeyPair("pwd-1").getPrivate().getEncoded()));
System.out.println("private key 2: " + md5(getKeyPair("pwd-2").getPrivate().getEncoded()));
System.out.println("private key 2: " + md5(getKeyPair("pwd-2").getPrivate().getEncoded()));
}
private String md5(byte[] data) throws GeneralSecurityException {
return javax.xml.bind.DatatypeConverter.printHexBinary(
MessageDigest.getInstance("md5").digest(data));
}
测试输出
private key 1: 5A2009E6DC8B25321C6304F62BE45398
private key 1: 5A2009E6DC8B25321C6304F62BE45398
private key 2: 2ACB65656AF9AF7036F40ACF0CFE7CA3
private key 2: 2ACB65656AF9AF7036F40ACF0CFE7CA3