如何使用 Java 中的 RSAPrivateKeySpec class 从字符串创建 RSA 私钥?

How to create RSA PrivateKey from string with RSAPrivateKeySpec class in Java?

我有以下信息,想在 RSA (Java) 中生成 public 和私钥。

    String pubKey = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJXFRUWMDJZ+moC/VbcAXoY5dDxOruwI2B+B+YZRHSRRTKPyd9v0HTqdLeVgufLu/cSxlZAKtZDp9mfgyNdbY9ECAwEAAQ==";
    String privKey = "MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAlcVFRYwMln6agL9VtwBehjl0PE6u7AjYH4H5hlEdJFFMo/J32/QdOp0t5WC58u79xLGVkAq1kOn2Z+DI11tj0QIDAQABAkBC1Bp71OkNAgL47edWWADVja9y9X0R70RYkst/hpQlTrjN4QxzN4k4gCqqdFkno2LfacRO7igMQuVEmYujfkbNAiEA/lU1CyW9J65FXSObsBLxqlNpFw79B2EKUkT6o7b2Ez8CIQCWwJlIyDaoXt7CrvTrPDpok7U93ZKmDPiRjjXDPsGU7wIhAJ6pITIXFO2QNg1ojVNGPiR3bHPKEedsjjfMeF9xYAmZAiAQnfpHg4pC1PJJE2/73g+yJ1X7E8ludE+R+9MBSpGcEQIgBWaVSyTx4e+gSulT93vnMpVsYmiwe53e5t4Uxs+cgSE=";
    String nModulusPublic = "AJXFRUWMDJZ-moC_VbcAXoY5dDxOruwI2B-B-YZRHSRRTKPyd9v0HTqdLeVgufLu_cSxlZAKtZDp9mfgyNdbY9E=";
    String eExponentPublic = "AQAB";
    String nModulusPrivate = "AJXFRUWMDJZ-moC_VbcAXoY5dDxOruwI2B-B-YZRHSRRTKPyd9v0HTqdLeVgufLu_cSxlZAKtZDp9mfgyNdbY9E=";
    String eExponentPrivate = "QtQae9TpDQIC-O3nVlgA1Y2vcvV9Ee9EWJLLf4aUJU64zeEMczeJOIAqqnRZJ6Ni32nETu4oDELlRJmLo35GzQ==";

这些是主要的加密和解密函数:

static String Decrypt(String encodedString,PrivateKey privKey) {
    try {
        Cipher cipher = Cipher.getInstance(cipherInstancename);
        cipher.init(Cipher.DECRYPT_MODE, privKey);
        byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(encodedString));
        return new String(decrypted, "UTF-8");
    } catch (Exception err) {
        return err.fillInStackTrace().toString();
    }
}

static String Encrypt(String encodedString,PublicKey pubKey) {
    try {
        Cipher cipher = Cipher.getInstance(cipherInstancename);
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
        byte[] plainBytes = new String(encodedString).getBytes("UTF-8");
        byte[] cipherData = cipher.doFinal(plainBytes);
        String encryptedString = Base64.getEncoder().encodeToString(cipherData);
        return encryptedString;
    } catch (Exception err) {
        return err.fillInStackTrace().toString();
    }
}

如果我使用以下函数创建 PrivateKey 和 PublicKey,一切正常:

 static PrivateKey firstPrivateKey(String privateKeyStr) throws Exception {
    byte[] keyBytes = Base64.getDecoder().decode(privateKeyStr.getBytes(StandardCharsets.UTF_8));
    PKCS8EncodedKeySpec specPrivate = new PKCS8EncodedKeySpec(keyBytes);
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    PrivateKey fileGeneratedPrivateKey = keyFactory.generatePrivate(specPrivate);
    RSAPrivateKey rsaPrv  = (RSAPrivateKey)(fileGeneratedPrivateKey);
    return  rsaPrv;
}
 static PublicKey firstPublicKey(String publicKeyStr) throws Exception {
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");

    byte[] keyBytes = Base64.getDecoder().decode(publicKeyStr.getBytes(StandardCharsets.UTF_8));
    X509EncodedKeySpec specPublic = new X509EncodedKeySpec(keyBytes);
    PublicKey fileGeneratedPublicKey = keyFactory.generatePublic(specPublic);
    RSAPublicKey rsaPub  = (RSAPublicKey)(fileGeneratedPublicKey);
    return  rsaPub;
}

但我想用下面的函数通过这种方式创建它们,但解密不起作用:

 static PublicKey secondPublicKey(String publicKString,String publicExponentStr) throws Exception {
    byte[] modulusBytes = Base64.getDecoder().decode(publicKString);
    byte[] exponentBytes = Base64.getDecoder().decode(publicExponentStr);
    BigInteger modulus = new BigInteger(1, modulusBytes);
    BigInteger exponent = new BigInteger(1, exponentBytes);

    RSAPublicKeySpec rsaPubKey = new RSAPublicKeySpec(modulus, exponent);
    KeyFactory fact = KeyFactory.getInstance("RSA");
    PublicKey pubKey = fact.generatePublic(rsaPubKey);
    return pubKey;
}

static PrivateKey secondPrivateKey(String privateKString,String privateExponentStr) throws Exception {
    byte[] modulusBytes = Base64.getDecoder().decode(privateKString);
    byte[] exponentBytes = Base64.getDecoder().decode(privateExponentStr);
    BigInteger modulus = new BigInteger(1, modulusBytes);
    BigInteger exponent = new BigInteger(1, exponentBytes);

    RSAPrivateKeySpec privSpec = new RSAPrivateKeySpec(modulus,exponent);
    KeyFactory fact = KeyFactory.getInstance("RSA");
    PrivateKey privKey = fact.generatePrivate(privSpec);

    return privKey;
}

使用 firstPrivateKey 和 firstPublicKey 密钥生成器并且工作良好:

   String a = Encrypt("test",firstPrivateKey(pubKey));
   String b = Decrypt(a, firstPublicKey(privKey));
   System.out.println(b);

使用第二个函数但不起作用:

 String a = Encrypt("test",secondPublicKey(pubKey,eExponentPublic));
 String b = Decrypt(a, secondPrivateKey(privKey,eExponentPrivate));
 System.out.println(b);

secondPublicKey 和 secondPrivateKey 函数有什么问题?

更新:

我从这段代码生成了所有密钥和模数,看来我的解码不正常:

    KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
    // RSAKeyGenParameterSpec spec = new RSAKeyGenParameterSpec(1024, RSAKeyGenParameterSpec.F4);
    kpg.initialize( 512 );
    KeyPair kp = kpg.generateKeyPair();
    String publicKeyStr = Base64.getEncoder().encodeToString(kp.getPublic().getEncoded());
    String privateKeyStr = Base64.getEncoder().encodeToString(kp.getPrivate().getEncoded());

    KeyFactory keyFactory = KeyFactory.getInstance("RSA");

    byte[] keyBytes = Base64.getDecoder().decode(publicKeyStr.getBytes(StandardCharsets.UTF_8));
    X509EncodedKeySpec specPublic = new X509EncodedKeySpec(keyBytes);
    PublicKey fileGeneratedPublicKey = keyFactory.generatePublic(specPublic);
    RSAPublicKey rsaPub  = (RSAPublicKey)(fileGeneratedPublicKey);

    keyBytes = Base64.getDecoder().decode(privateKeyStr.getBytes(StandardCharsets.UTF_8));
    PKCS8EncodedKeySpec specPrivate = new PKCS8EncodedKeySpec(keyBytes);
    PrivateKey fileGeneratedPrivateKey = keyFactory.generatePrivate(specPrivate);
    RSAPrivateKey rsaPrv  = (RSAPrivateKey)(fileGeneratedPrivateKey);


    BigInteger publicKeyModulus = rsaPub.getModulus();
    BigInteger publicKeyExponent  = rsaPub.getPublicExponent();
    BigInteger privateKeyModulus = rsaPrv.getModulus();
    BigInteger privateKeyExponent  = rsaPrv.getPrivateExponent();


    String nModulusPublic=Base64.getUrlEncoder().encodeToString(publicKeyModulus.toByteArray());
    String eExponentPublic=Base64.getUrlEncoder().encodeToString(publicKeyExponent.toByteArray());
    String nModulusPrivate=Base64.getUrlEncoder().encodeToString(privateKeyModulus.toByteArray());
    String eExponentPrivate=Base64.getUrlEncoder().encodeToString(privateKeyExponent.toByteArray());

    System.out.println(publicKeyStr);
    System.out.println(privateKeyStr);
    System.out.println( nModulusPublic);
    System.out.println(eExponentPublic);
    System.out.println( nModulusPrivate);
    System.out.println(eExponentPrivate);

一个相关的public和私钥有相同的模数。因此 nModulusPublicnModulusPrivate 之间的区别实际上是没有必要的(不像 public 和私有指数)。

secondPublicKey()secondPrivateKey() 期望模数(nModulusPublic 或相同的 nModulusPrivate)作为第一个参数。相反,传递了整个密钥(pubKeyprivKey),这会导致错误。如果改为传递模数,则有效。

顺便说一句,模数和指数是 Base64url 编码的,必须先转换为标准的 Base64 编码(- -> +_ -> /).或者,可以在 secondPublicKey()secondPrivateKey() (Base64.getUrlDecoder()).

中使用 Base64url 解码器

由于两个 public 或私钥相同(只是格式不同),任何 public/private 组合键都可以用于 encryption/deryption:

String pubKey = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJXFRUWMDJZ+moC/VbcAXoY5dDxOruwI2B+B+YZRHSRRTKPyd9v0HTqdLeVgufLu/cSxlZAKtZDp9mfgyNdbY9ECAwEAAQ==";
String privKey = "MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAlcVFRYwMln6agL9VtwBehjl0PE6u7AjYH4H5hlEdJFFMo/J32/QdOp0t5WC58u79xLGVkAq1kOn2Z+DI11tj0QIDAQABAkBC1Bp71OkNAgL47edWWADVja9y9X0R70RYkst/hpQlTrjN4QxzN4k4gCqqdFkno2LfacRO7igMQuVEmYujfkbNAiEA/lU1CyW9J65FXSObsBLxqlNpFw79B2EKUkT6o7b2Ez8CIQCWwJlIyDaoXt7CrvTrPDpok7U93ZKmDPiRjjXDPsGU7wIhAJ6pITIXFO2QNg1ojVNGPiR3bHPKEedsjjfMeF9xYAmZAiAQnfpHg4pC1PJJE2/73g+yJ1X7E8ludE+R+9MBSpGcEQIgBWaVSyTx4e+gSulT93vnMpVsYmiwe53e5t4Uxs+cgSE=";
String nModulusPublic = "AJXFRUWMDJZ-moC_VbcAXoY5dDxOruwI2B-B-YZRHSRRTKPyd9v0HTqdLeVgufLu_cSxlZAKtZDp9mfgyNdbY9E=".replace("-", "+").replace("_","/");
String eExponentPublic = "AQAB".replace("-", "+").replace("_","/");
String nModulusPrivate = "AJXFRUWMDJZ-moC_VbcAXoY5dDxOruwI2B-B-YZRHSRRTKPyd9v0HTqdLeVgufLu_cSxlZAKtZDp9mfgyNdbY9E=".replace("-", "+").replace("_","/");
String eExponentPrivate = "QtQae9TpDQIC-O3nVlgA1Y2vcvV9Ee9EWJLLf4aUJU64zeEMczeJOIAqqnRZJ6Ni32nETu4oDELlRJmLo35GzQ==".replace("-", "+").replace("_","/");    

String a, b;

// first/first
a = Encrypt("test", firstPublicKey(pubKey));
b = Decrypt(a, firstPrivateKey(privKey));
System.out.println(b);

// second/second
a = Encrypt("test", secondPublicKey(nModulusPublic, eExponentPublic));
b = Decrypt(a, secondPrivateKey(nModulusPrivate, eExponentPrivate));
System.out.println(b);

// first/second
a = Encrypt("test", firstPublicKey(pubKey));
b = Decrypt(a, secondPrivateKey(nModulusPrivate, eExponentPrivate));
System.out.println(b);

// second/first
a = Encrypt("test", secondPublicKey(nModulusPublic, eExponentPublic));
b = Decrypt(a, firstPrivateKey(privKey));
System.out.println(b);

请注意,发布的代码中有一个拼写错误:EncryptfirstPrivateKey() 结合使用,DecryptfirstPublicKey() 结合使用。