Java中将RSA公钥和私钥作为字符串变量

3

出于显而易见的安全原因,我需要使用RSA私钥和公钥加密和解密用户的PIN码。我找到了一个可行的解决方案,如下:

KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
    kpg.initialize(512);
    KeyPair rsaKeyPair = kpg.genKeyPair();
    byte[] txt = "This is a secret message.".getBytes();
    System.out.println("Original clear message: " + new String(txt));

    // encrypt
    Cipher cipher;
    try {
        cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, rsaKeyPair.getPublic());
        txt = cipher.doFinal(txt);
    } catch (Throwable e) {
        e.printStackTrace();
        return;
    }
    System.out.println("Encrypted message: " + new String(txt));

    // decrypt
    try {
        cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, rsaKeyPair.getPrivate());
        txt = cipher.doFinal(txt);
    } catch (Throwable e) {
        e.printStackTrace();
        return;
    }
    System.out.println("Decrypted message: " + new String(txt));

}

一切都正常,但在这个例子中,密钥对不是静态的,每次都会生成新值,但我需要使用相同的键,这些键表示为字符串变量:

public static final String PrivateKey = "MIICXAIBAAKBgQDx0PSJr6zEP9914k1eM+sS8/eW+FenhBQI/jf6ARe8kZHFig9Y"
            + bla bla bla
            + "wdK3jBzObK319yNFr/2LukNZ9Bgv7fS78roBvxbe2gI=";

    public static final String PublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDx0PSJr6zEP9914k1eM+sS8/eW"
            + bla bla bla
            + "jYo5w2Nhxe2cukCQMQIDAQAB";

有没有办法将这些变量转换为 PublicKey 和 PrivateKey 类?

PublicKeyPrivateKey不是类,它们是你实现的接口。 - Ceiling Gecko
1
你永远不应该加密密码。密码应该被加盐并进行哈希处理。 - JB Nizet
@EddyG 我看不出有什么问题。在这种情况下,“静态密钥”明显指的是一个加密密钥(对),它在每次运行和/或调用程序时都不会改变;它与Java“静态”字段、方法或嵌套类没有直接关系。将“长期”密钥放入源代码中本身就是可疑的,但如果你这样做,使用static final String是合理的。 - dave_thompson_085
你可能想阅读关于https://en.wikipedia.org/wiki/PBKDF2的内容。 - rkosegi
3个回答

3

如果我理解你的意思,你想从静态变量中获取PublicKey和PrivateKey实例,你可以按照以下方式操作:

private static final String privateKeyString = "...";
private static PrivateKey privateKey;
private static final String publicKeyString = "...";
private static PublicKey publicKey;
static {
    KeyFactory kf;
    try {
        kf = KeyFactory.getInstance("RSA");
        byte[] encodedPv = Base64.decodeBase64(privateKeyString);
        PKCS8EncodedKeySpec keySpecPv = new PKCS8EncodedKeySpec(encodedPv);
        privateKey = kf.generatePrivate(keySpecPv);

        byte[] encodedPb = Base64.decodeBase64(publicKeyString);
        X509EncodedKeySpec keySpecPb = new X509EncodedKeySpec(encodedPb);
        publicKey = kf.generatePublic(keySpecPb);

    } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {

    }
}

0

在与@JB的大部分一致意见中,密码(通常)不应该被加密,而应该使用“哈希”——使用专门设计的方法来“拉伸”和盐化,例如scrypt,而不是像SHA-1这样的快速哈希。同时还注意到,您原始代码中使用的RSA-512已经被破解,即使是您修改后似乎使用的RSA-1024也被认为是薄弱的:

从其开头来看,您的PrivateKey值似乎是纯PKCS#1 DER编码的base64,这基本上只被OpenSSL和使用OpenSSL(格式)的东西使用,如旧版本的OpenSSH。Java标准的“Sun”提供程序不处理此内容,尽管我认为BouncyCastle可能会处理,如果您想探索一下。对于Sun,您需要将其从base64转换为二进制DER;将其包装成PKCS#8格式(在二进制中仅添加标题和可能的EOC尾随,因为RSA的PKCS#8算法特定部分就是PKCS#1);并将其放入一个RSA类型的KeyFactory的PKCS8EncodedKeySpec中,并通过generatePrivate运行它。请参见

或者添加头/尾以使其成为正确的PEM,使用OpenSSL将其转换为PKCS#8(未加密),并在同一时间选择二进制,然后通过generatePrivate运行它。

你的公钥看起来也是一个X.509 SubjectPublicKeyInfo编码的base64,OpenSSL(但不是OpenSSH)使用它,标准Java也支持它,名称为“X.509”。因此,只需将其从base64转换为二进制,放入X509EncodedKeySpec中,并通过RSA KeyFactorygeneratePublic运行即可。请注意,如果您的加密将在远程或分布式环境中进行,这通常是公钥加密的情况下,加密器必须确保使用正确的公钥;如果攻击者可以替换错误的公钥,则他们可以解密并窃取您的一些被认为是安全的数据。这就是为什么真正的PK系统不使用普通的公钥,而是使用证书,例如像SSL/TLS和S/MIME一样的X.509,或者像PGP一样的信任网络。


0

我按照以下步骤使其运行:

 public Key loadPrivateKey(String stored) throws GeneralSecurityException {
    PKCS8EncodedKeySpec keySpec =
        new PKCS8EncodedKeySpec(
            Base64.getDecoder().decode(stored.getBytes(StandardCharsets.UTF_8)));
    KeyFactory kf = KeyFactory.getInstance("RSA");
    return kf.generatePrivate(keySpec);
  }

  public Key loadPublicKey(String stored) throws GeneralSecurityException {
    byte[] data = Base64.getDecoder().decode(stored.getBytes(StandardCharsets.UTF_8));
    X509EncodedKeySpec spec = new X509EncodedKeySpec(data);
    KeyFactory fact = KeyFactory.getInstance("RSA");
    return fact.generatePublic(spec);
  }

您还需要从包含密钥的字符串中删除-----BEGIN PRIVATE KEY----------END PRIVATE KEY-----和所有\n


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接