Java 和 OPENSSL

Java and OPENSSL

我需要帮助来使用 openssl 解密加密文本的 base64 字符串。

在linux命令行中,我做了以下操作:

  1. 生成了 public / 私钥:
openssl genrsa -out private_key.pem 1024
openssl rsa -in private_key.pem -out public_key.pem -outform PEM -pubout

我的 private_key.pem 看起来像:

-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDDu/0Fcq5/4M2t9Y+R4QvoDADQ+VaGSR3VEQiCV9xXcpkKAda+
nO2PrljIMcUO76O5a4twxxl1YT1dzKmj7T8i+EqYF1Y/1TxxWjIdo8hELD2Wug4d
ey3pMjI+5MXVraMyl5zEgfB64eTiu48LWygKi57Cfg41wKekSn2S/SvETQIDAQAB
AoGAFPgVwjioAyElR8av69PtP53RlJGxuE8q+AGMJNKe02t+g7jwtZkARk1KS6Ax
WUlJA/tGg/2Ad7fEKEFdxycKhVeF0ephOo8Xje6pMDDPpYSj/RlevUb60NEZZma1
PbA9TqS5Ys533jLYRbJUeNpufi0qQbRyB0QBJOUwbBHWDoECQQDsTYCoolsBHutY
YPZYBszOZhtxiDYqJ+B6/GoW/Y9h4HVg7CkKxaMdQBHbjTm+2YOH4SNHnwl7MVk3
cagbC/RhAkEA1AzKatUc7pUnkAO7VPM5luLnV/0ltpp6eveSgwxVs618a2BfFvS1
d8PAoKFCuCawTVd/GbY/fFKQab3hGiAXbQJBAOvIlZUukyG2KVzBO20wM9HK/p01
Ld64dXwiOvV/wj8GifjRDE7MT+rS0D7DVxhAz8aYdex0GzDKV9xD01pRfmECQBwi
9ljmnkgqEm3RkPHctC+JPBk4xeBM7yOR5ibtZBHLW08EIpnxLoMNvmmR/EBjIdGh
YoQO4q2kZvUg9NV6nKECQCFHa7lwlstxDqLgTzKHnRIK2hbUufrWKp+nDb18aqmF
eHvC4MZkixR+rykGRHsYjmwGKnXpbchKjC2iPFn1XY0=
-----END RSA PRIVATE KEY-----

我的 public 键看起来像:

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDu/0Fcq5/4M2t9Y+R4QvoDADQ
+VaGSR3VEQiCV9xXcpkKAda+nO2PrljIMcUO76O5a4twxxl1YT1dzKmj7T8i+EqY
F1Y/1TxxWjIdo8hELD2Wug4dey3pMjI+5MXVraMyl5zEgfB64eTiu48LWygKi57C
fg41wKekSn2S/SvETQIDAQAB
-----END PUBLIC KEY-----
  1. 我可以使用我的 public_key.pem:
  2. 创建一个 base64 字符串
echo "Hola Mundo" | openssl rsautl -encrypt -pubin -inkey public_key.pem | base64 -w 0
gQO7XwvkClncfTpsv0jZ1Ptm2tI7YppHWdqwdrH2sZrG3ah5pcWt+PJ1q2ADPhZo8EURVmiYLO+q/P475/eUKY0h/T+IWQmV4lFCoXZfBzD3TzkN3nSmvj9HiR4psevYUa9HeOpECCXLJ1z8K4ut978zHtYKDi89k3VIdZUT6uY=
  1. 我可以使用我的 private_key.pem:
  2. 解密字符串
echo "gQO7XwvkClncfTpsv0jZ1Ptm2tI7YppHWdqwdrH2sZrG3ah5pcWt+PJ1q2ADPhZo8EURVmiYLO+q/P475/eUKY0h/T+IWQmV4lFCoXZfBzD3TzkN3nSmvj9HiR4psevYUa9HeOpECCXLJ1z8K4ut978zHtYKDi89k3VIdZUT6uY=" | base64 -d | openssl rsautl -decrypt -inkey private_key.pem

Hola Mundo

现在,我的问题是,在 JAVA 中,像这样加载私钥:

String key = "MIICXAIBAAKBgQDDu/0Fcq5/4M2t9Y+R4QvoDADQ+VaGSR3VEQiCV9xXcpkKAda+\n" +
        "nO2PrljIMcUO76O5a4twxxl1YT1dzKmj7T8i+EqYF1Y/1TxxWjIdo8hELD2Wug4d\n" +
        "ey3pMjI+5MXVraMyl5zEgfB64eTiu48LWygKi57Cfg41wKekSn2S/SvETQIDAQAB\n" +
        "AoGAFPgVwjioAyElR8av69PtP53RlJGxuE8q+AGMJNKe02t+g7jwtZkARk1KS6Ax\n" +
        "WUlJA/tGg/2Ad7fEKEFdxycKhVeF0ephOo8Xje6pMDDPpYSj/RlevUb60NEZZma1\n" +
        "PbA9TqS5Ys533jLYRbJUeNpufi0qQbRyB0QBJOUwbBHWDoECQQDsTYCoolsBHutY\n" +
        "YPZYBszOZhtxiDYqJ+B6/GoW/Y9h4HVg7CkKxaMdQBHbjTm+2YOH4SNHnwl7MVk3\n" +
        "cagbC/RhAkEA1AzKatUc7pUnkAO7VPM5luLnV/0ltpp6eveSgwxVs618a2BfFvS1\n" +
        "d8PAoKFCuCawTVd/GbY/fFKQab3hGiAXbQJBAOvIlZUukyG2KVzBO20wM9HK/p01\n" +
        "Ld64dXwiOvV/wj8GifjRDE7MT+rS0D7DVxhAz8aYdex0GzDKV9xD01pRfmECQBwi\n" +
        "9ljmnkgqEm3RkPHctC+JPBk4xeBM7yOR5ibtZBHLW08EIpnxLoMNvmmR/EBjIdGh\n" +
        "YoQO4q2kZvUg9NV6nKECQCFHa7lwlstxDqLgTzKHnRIK2hbUufrWKp+nDb18aqmF\n" +
        "eHvC4MZkixR+rykGRHsYjmwGKnXpbchKjC2iPFn1XY0=";

加密后的字符串是这样加载的:

String data = "I3E5pjXE0chhtmkFBa56PGtWv5XDcIXfi2h5e3Bi44CCMvaIQ7UT7XBwkGZTRR11wfQIl7MswcEebXDmmw/C6JobrqQHQ0rU7zLPOU8j24JCFiccxclq5efMAcIO/ZcSO34uObrFQwQ4L2ex/3xL7b/YKujCQDTtzQkxE2N1JPU=";

如何取回原来的“Hola Mundo”字符串?你能给我指明正确的方向吗?

提前致谢。

您遇到的问题是私钥的format/encoding。

您使用 OpenSSL 生成了 RSA 密钥对并收到了“旧”PKCS1 编码的私钥,在 pem 字符串的开头可以清楚地看到:

-----BEGIN RSA PRIVATE KEY-----

不幸的是 Java 无法“开箱即用”地使用此编码(您可以使用例如 Bouncy Castle 来读取此类密钥)。但是有一个解决方案,那就是将密钥转换为(Java 可用的)PKCS8 编码。只需将密钥转换为

openssl pkcs8 -topk8 -nocrypt -in rsaprivatekeypkcs1.pem -out rsaprivatekeypkcs8.pem

您将收到此私钥:

-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMO7/QVyrn/gza31
j5HhC+gMAND5VoZJHdURCIJX3FdymQoB1r6c7Y+uWMgxxQ7vo7lri3DHGXVhPV3M
qaPtPyL4SpgXVj/VPHFaMh2jyEQsPZa6Dh17LekyMj7kxdWtozKXnMSB8Hrh5OK7
jwtbKAqLnsJ+DjXAp6RKfZL9K8RNAgMBAAECgYAU+BXCOKgDISVHxq/r0+0/ndGU
kbG4Tyr4AYwk0p7Ta36DuPC1mQBGTUpLoDFZSUkD+0aD/YB3t8QoQV3HJwqFV4XR
6mE6jxeN7qkwMM+lhKP9GV69RvrQ0RlmZrU9sD1OpLliznfeMthFslR42m5+LSpB
tHIHRAEk5TBsEdYOgQJBAOxNgKiiWwEe61hg9lgGzM5mG3GINion4Hr8ahb9j2Hg
dWDsKQrFox1AEduNOb7Zg4fhI0efCXsxWTdxqBsL9GECQQDUDMpq1RzulSeQA7tU
8zmW4udX/SW2mnp695KDDFWzrXxrYF8W9LV3w8CgoUK4JrBNV38Ztj98UpBpveEa
IBdtAkEA68iVlS6TIbYpXME7bTAz0cr+nTUt3rh1fCI69X/CPwaJ+NEMTsxP6tLQ
PsNXGEDPxph17HQbMMpX3EPTWlF+YQJAHCL2WOaeSCoSbdGQ8dy0L4k8GTjF4Ezv
I5HmJu1kEctbTwQimfEugw2+aZH8QGMh0aFihA7iraRm9SD01XqcoQJAIUdruXCW
y3EOouBPMoedEgraFtS5+tYqn6cNvXxqqYV4e8LgxmSLFH6vKQZEexiObAYqdelt
yEqMLaI8WfVdjQ==
-----END PRIVATE KEY-----

是的,它看起来是一样的,但一开始它只是“...BEGIN PRIVATE KEY...”。

因为 OpenSSL 端的默认 RSA 填充是 PKCS1.5 填充

openssl rsautl -help
-pkcs Use PKCS#1 v1.5 padding (default)

你应该在 Java 端使用这个实例:

Cipher decryptCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");

您将收到(使用 PKCS8 密钥)加密的字符串 Hola Mundo

在@michael-fehr 的帮助下,我可以让它工作;这是我所做的,以防其他人需要它:

  1. 生成 openssl PKCS8 密钥对:
openssl genrsa -out keypair.pem 2048
openssl rsa -in keypair.pem -pubout -out publickey.crt
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in keypair.pem -out pkcs8.key

现在我有 2 个文件,publickey.crt:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxlfy+juJwTDtw8XolfSK
QB+JaKTgZMl+MjWo+m9n8Kt64ygqtfGvDda1UvT3t9e2ZpOvlsmfSIN0SUMhsq+T
/O8+Xyr87/sUU7tYocQ6adGh+zO58EecuSUtgutDdwh9/WkVqHhdCjeZXN5310/s
afaxJJzBemMjvmc/1yiMtBSVrCl71CloR6J1lnz+QZK+zqaNlKIQdQay9PoQlGEL
RrGgqo8fHdK3OQVUd6Ifzh5G1Mmnv67esCAKyeW8yeb6lQ7dtsJQJEC8M9yn4n1D
bz0+OwWWTzqHBo8b5JYi6xXnb/0WsaRX/ooWk5BEykmkkBhSmDYx0ZtLZcA2/Pj2
jwIDAQAB
-----END PUBLIC KEY-----

和pkcs8.key:

-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDGV/L6O4nBMO3D
xeiV9IpAH4lopOBkyX4yNaj6b2fwq3rjKCq18a8N1rVS9Pe317Zmk6+WyZ9Ig3RJ
QyGyr5P87z5fKvzv+xRTu1ihxDpp0aH7M7nwR5y5JS2C60N3CH39aRWoeF0KN5lc
3nfXT+xp9rEknMF6YyO+Zz/XKIy0FJWsKXvUKWhHonWWfP5Bkr7Opo2UohB1BrL0
+hCUYQtGsaCqjx8d0rc5BVR3oh/OHkbUyae/rt6wIArJ5bzJ5vqVDt22wlAkQLwz
3KfifUNvPT47BZZPOocGjxvkliLrFedv/RaxpFf+ihaTkETKSaSQGFKYNjHRm0tl
wDb8+PaPAgMBAAECggEAWpirnI77cioWQJkyjuQ/DeEZ05mUAZVjti16fMHMWUsK
e53KPIcjbY/IDRdl2yyF3T7SMm7v+aBJynkGeGboktz4wWGSXU9zTnfBmUpXYCRn
96T95nnfZVZM/oLVsxZG7ixEv0oTPWp9+8SGOuv/0brj2RRc77k/B2aD65RTCKGc
iHMqK/mzeLedXrthaBwd0ppkQw1M6YvPw+nx8Mhs/sADKMLUTNkqpu16d+2pOLeU
DKsvnA8ns6/jfFMMdkJ3mhoIeCfqYJJhosydMebLnt8yUo/3NlNmeRKcqH9qx8zC
em+0wx9+a1fEgzPvadF1pnmI4n/5VlIn9moZXmO0MQKBgQDkg+4QqJT1upAg2NSQ
QMagoSECGoCM54aMpTCTDlhrzphW7tms5AjDmfrN/2NQ+LodjBHpqNd6E44D9U+6
EHBJ/PGOJE1CWdVckAUX+a51g67lmPNrfrFolTT1zrTJNq4VCKFffLUDW9yw+B+0
9QlWN36LG/jYr3Ia4D59mAgCRwKBgQDeMwTCJDfCMOPtiLT5Xl4J9H76aHU8por0
4C3fkq/zWCPxxU3XAO6eJQNzEhM8I+Yo/b3WbyzyqM2Ww9Y8qSOD/cpr2ftSdT60
l7jPSGjAIyudAd3QjkVC+OgwX1cSrfyuiRoyuQiHPyzAoiqK4AKxxUa0EFD8Ec42
X+mHT3eFeQKBgH13TmvJE9iDcYUHaFY3qpchQO1VvcUfjcmFHVFwq+2tRgldJRj/
LyyuS311PoODvTRh5qfjM0Psnqnfs8GWKwEEp2AC4ISQrEwhKxrJ1RbikVGwk94u
dpvUaHqZ3rsnkZcs4OV7pCtO8bIc2dPSQikbWRhp6EyYr86/1q/AnI0bAoGAb5G4
gu8CnFxGJkAtdsUefOsqUvveWhzZywlBn3AdLxgDvGMwqZOLPRciu0XJKLpx2AVI
rAJY1GNUD663xO+8qIrnd+4VFptOaAmCv3oBNvCx9n04bn7xYiZvF9LXesaoCM9I
u01Tbe2XwAXtTJwcXjzLOqCyuU8LdxwDu3B4eCkCgYEAsTQ8Rn6YaCy2cNl/7BZy
Jfr8tqwKprOeu+tcRg/MxUjjo72PVsK8WiahHHKxpFNlsDa0/u6GlICKUaY9Emul
Chu20kYr6xFFiczLo9enwiAJ5TDOz8yqOyv/1wALiTayOz2DVTwiax/D45yLJxQ6
dV0YXCvoOcDjM1c/vDVOK7w=
-----END PRIVATE KEY-----
  1. 现在,让我们使用 publickey.crt 文件将“Hola Mundo”加密为 base64 字符串:
echo "Hola Mundo" | openssl rsautl -encrypt -pubin -inkey publickey.crt | base64 -w 0
jeqdFnlj5Rxl8iQcoLc8o0u3KV4950OjSeqJW2ba0P5hcfdkIbtff/WW6rPdFudSYBOr1i+NwQ2jEpRGzOVrJ6mEZI4r/iFAJHkL7a3kQTs+pC1i5nig8MzO+5IU615HZhOykQ1FJfv3+OVOtTj8C5HLDZEehr1ggFPIp10YrYls6Gffxww5xlOZHBbg5J6JBmItmwFmvCl8O5ZI7N9hp5ynqqKYwAHxqRj0UUe1MXyGjEiL9aQdNiYJFCUL5V/0Vq6iP4sLRrU3Ir35fkMAWlQHt4vbmhgX5XUjv1804BIa7HlMytwnN7ZuKnJ6WKnOoJznpYbZOw92yXtKhPfUsA==
  1. 现在,通过使用 Bouncy Castle 库,java 找回“Hola Mundo”的代码:
import java.io.File;
import java.io.FileReader;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;

import javax.crypto.Cipher;

import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;

public class App {
    public static RSAPrivateKey readPrivateKey(File file) throws Exception {
        KeyFactory factory = KeyFactory.getInstance("RSA");
     
        try (FileReader keyReader = new FileReader(file);
          PemReader pemReader = new PemReader(keyReader)) {
     
            PemObject pemObject = pemReader.readPemObject();
            byte[] content = pemObject.getContent();
            PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(content);
            return (RSAPrivateKey) factory.generatePrivate(privKeySpec);
        }
    }
    
    public static String getDecrypted(String data, RSAPrivateKey key) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        
        cipher.init(Cipher.DECRYPT_MODE, key);
        
        byte[] encryptedbytes = cipher.doFinal(Base64.getDecoder().decode(data.getBytes()));
        
        return new String(encryptedbytes);
    }
    
    public static void main(String[] args) {
        String data = "GjoUq7pz6ZV231MlZmBNiWnB7FlOHsGKFmAiMGhSUa9aIkXswnP8Ow4WTYR12tFIdyIeevQWCmEJgebiQ9wpBDLODtT4U00wTxPAfIjulgVhj0MA56b1nDkhCfL6gwzwsD2JxFO8/eMhqY1Wptn/fYn6USfbt7XFRyV73IeFBUvp0OisJ3DXxZsHfpfdL/yQcbGCboiiYEDkJ3DnLhGiaeMuLIedmABLgqWJv8EwPIBT0x/jgIMhaxQSwcs0w/fAYIEt/E4gkOpcFVkiTp6IKDsmiYgzDnXTnPko059Be9mQ3eJ3tIZ+U6Tfm1RCFY7Jr1oTIoZxOgHspgAIQb3U+w==";
        System.out.println("TEXTO CIFRADO:");
        System.out.println(data);
        System.out.println();

        
        try {
            File file = new File("/home/gbasisty/pruebas_openssl/pkcs8.key");
            RSAPrivateKey key = readPrivateKey(file);

            String texto = getDecrypted(data, key);
            System.out.println("TEXTO DESCIFRADO:");
            System.out.println(texto);
            System.out.println();
        } catch(Exception e) {
            System.out.println(e);
        }
    }
}

...它就像一个魅力。

再次感谢迈克尔!