【问题标题】:java - How to generate PrivateKey and PublicKey starting from a keystore (.p12)java - 如何从密钥库 (.p12) 开始生成私钥和公钥
【发布时间】:2020-05-01 14:30:36
【问题描述】:

使用 OpenSSL 生成一些密钥,然后在 Base64 中对其进行编码并获取它们并尝试生成它们以验证使用 JWT 的身份验证。这是发生在我身上的代码和描述 使用以下命令生成:

  openssl req -x509 -newkey rsa:4096 -keyout private_key.pem -out public_key.der

  openssl pkcs12 -export -out keyStore.p12 -inkey private_key.pem -in public_key.der

  base64 –w 0 private_key.pem > private_key_base64_enc.txt 

  base64 –w 0 public_key.der > public_key_base64_enc.txt

我从 Wildfly 保存在我的 vault.keystore 中:private_key_base64_enc.txt 和 public_key_base64_enc.txt

然后在我的 java 类中我写了以下内容:

 private void jwtSignedAuthentication(String token, PropName vaultBlockName) throws Exception
    {


        String rsa512Alias = vaultBlockName.getDefaultValue();

        String rsa512pvt = VaultReader.getValue(rsa512Alias, "privateKey");
        String rsa512pbc = VaultReader.getValue(rsa512Alias, "publicKey");

        KeyFactory keyfatc = null;
        PrivateKey privateKey = null;
        PublicKey publicKey = null;

        try {
            keyfatc = KeyFactory.getInstance("RSA");

        } catch (NoSuchAlgorithmException e) {
             logger.error(e);
        }


        StringBuilder pkcs8Lines = new StringBuilder();
        BufferedReader rdr = new BufferedReader(new StringReader(new String(Base64.getDecoder().decode(rsa512pvt.getBytes()))));

        String line;
        while ((line = rdr.readLine()) != null) {
            pkcs8Lines.append(line);
        }

        // Remove the "BEGIN" and "END" lines, as well as any whitespace

        String pkcs8Pem = pkcs8Lines.toString();
        pkcs8Pem = pkcs8Pem.replace("-----BEGIN ENCRYPTED PRIVATE KEY-----", "");
        pkcs8Pem = pkcs8Pem.replace("-----END ENCRYPTED PRIVATE KEY-----", "");
        pkcs8Pem = pkcs8Pem.replaceAll("\\s+","");

        byte[] dataPvt = Base64.getDecoder().decode(pkcs8Pem.getBytes());
        PKCS8EncodedKeySpec specPvt = new PKCS8EncodedKeySpec(dataPvt);

        byte[] dataPbc = Base64.getDecoder().decode(rsa512pbc.getBytes());

        StringBuilder publicLinesBuilder = new StringBuilder();
        BufferedReader readerPlubKey = new BufferedReader(new StringReader(new String(dataPbc)));

        String lineP;
        while ((lineP = readerPlubKey.readLine()) != null) {
            publicLinesBuilder.append(lineP);
        }


        String pubK = publicLinesBuilder.toString();
        pubK = pubK.replace("-----BEGIN CERTIFICATE-----", "");
        pubK = pubK.replace("-----END CERTIFICATE-----", "");
        pubK = pubK.replaceAll("\\s+","");
        X509EncodedKeySpec specPbc = new X509EncodedKeySpec(Base64.getDecoder().decode(pubK.getBytes()));

        try {

            privateKey = keyfatc.generatePrivate(specPvt);
            publicKey = keyfatc.generatePublic(specPbc);

        } catch (InvalidKeySpecException e) {
            logger.error(e);

        }

        Algorithm algorithm = Algorithm.RSA512((RSAPublicKey) publicKey, (RSAPrivateKey) privateKey);

        // Creación de un verificador JWT
        JWTVerifier verifier = JWT.require(algorithm).withIssuer(JWT_CLAIM_ISSUER).acceptLeeway(2).build();

        UserContext userContext = new UserContext();
        userContext.setUserName(JWT_CLAIM_ISSUER);

        try {
            // Decode JWT, verificación del token.
            @SuppressWarnings("unused")
            DecodedJWT decodeJwt = verifier.verify(token);

        } catch (JWTDecodeException e) {
        logger.error(e);
        }

    }

当我尝试生成密钥时,我返回 null:

privateKey = keyfatc.generatePrivate(specPvt);
publicKey = keyfatc.generatePublic(specPbc);

任何人都知道这会发生什么。提前致谢

用于生成我的 JWT:

 public ResteasyWebTarget getClientWebAgent(String host, String blockName) throws KeyStoreException
    {
        ResteasyClient clientBuilder = new ResteasyClientBuilder().establishConnectionTimeout(10, TimeUnit.SECONDS).socketTimeout(5, TimeUnit.SECONDS).build();
        ResteasyWebTarget target = clientBuilder.target(host);
        KeyPair keys = null;
        try {
            keys = keyStore.getKeys();
            /*logger.infov(new String(Base64.getEncoder().encode(keys.getPrivate().getEncoded())));
            logger.infov("****PUBLIC KEY ******");
            logger.infov(new String(keys.getPublic().getEncoded()));*/
        } catch (IOException e) {
            logger.error(e);
        }
          Algorithm algorithm = Algorithm.RSA512((RSAPublicKey) keys.getPublic(), (RSAPrivateKey) keys.getPrivate());
          Map<String, Object> headerClaims = new HashMap<>();
          headerClaims.put("alg", "RS512");
          headerClaims.put("typ", "JWT");

          JWTCreator.Builder jwtCreator = JWT.create();
          jwtCreator.withHeader(headerClaims);
          jwtCreator.withIssuer(JWT_CLAIM_ISSUER);
          jwtCreator.withIssuedAt(LocalDate.now().toDate());
          jwtCreator.withExpiresAt(LocalDate.now().toDateTimeAtCurrentTime().plusSeconds(30).toDate());
          String jwtToken = jwtCreator.sign(algorithm);
          target.register(new BearerAuthenticator(jwtToken));
          target.register(new LanguageHeaderToken(Locale.getDefault()));

        return target;
    }

【问题讨论】:

  • 只需将 .p12 加载到 PKCS#12 类型的 KeyStore 中并使用 API。这一切都不需要。

标签: java private-key pkcs#12 pkcs#8


【解决方案1】:

也许您在没有分配有效别名的情况下生成密钥库,查看您的命令,您没有使用-name 选项。
命令应该是这样的:

openssl pkcs12 -export -out keyStore.p12 -inkey private_key.pem -in public_key.der -name "alias"  

在 java 中使用键的更聪明的方法是创建一个KeyPair

KeyPair loadKeyPair() throws Exception {
    // Read keystore from resource folder
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    URL resource = classLoader.getResource("keyStore.p12");
    File file = new File(Objects.requireNonNull(resource).toURI());

    char[] keyPass = "1234".toCharArray();
    String alias = "alias";

    KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
    try (FileInputStream is = new FileInputStream(file)) {
        keystore.load(is, keyPass);
    }

    Key key = keystore.getKey(alias, keyPass);
    if (key instanceof PrivateKey) {
        // Get certificate of public key
        Certificate cert = keystore.getCertificate(alias);

        // Get public key
        PublicKey publicKey = cert.getPublicKey();

        // Return a key pair
        return new KeyPair(publicKey, (PrivateKey) key);
    }

    return null;
}

然后从 KeyPair 中提取 RSAPublicKeyRSAPrivateKey 密钥:

void loadKeys() throws Exception{
    KeyPair keyPair = loadKeyPair();

    if (null != keyPair) {
        RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
    }
}

希望它对您的 Json Web 令牌有所帮助并祝您好运! :-p

【讨论】:

  • 我用KeyPair在JWT中生成算法,明天分享代码,不过那部分我觉得还可以。谢谢,@Roberto Manfreda
【解决方案2】:

您的“公钥”实际上是一个证书(特别是 X.509 v1 或 v3 证书,具体取决于您的 openssl 配置),它包含一个公钥,但与公钥不同——并且即使您错误地将其命名为 .der,它也是 PEM 格式——并且您的私钥已加密。

除了使用 PKCS12 的方法之外,正如 Roberto 的有效建议并且通常是最简单的,因为它只需管理一个文件并且仍然是加密的,因此更安全:

  • Java 可以处理 X.509 证书,但您使用 CertificateFactory.getInstance("X.509") 并为其指定 InputStream 而不是 KeyFactoryX509EncodedKeySpecCertificateFactory 可以处理 PEM 或 DER,不像 KeyFactory 只能处理 DER,因此您不需要 de-PEM(剥离 BEGIN/END/EOL 和解码 base64)部分。

  • 标准 Java 无法直接处理加密的 PKCS8 密钥。如果可以添加第三方库,BouncyCastle 的 bcpkix 可以 这样做;搜索使用PEMParser(不是PEMReader,这是旧版本)和JceOpenSSLPKCS8DecryptorBuilder 的十几个现有Q。否则,您可以将-nodes 添加到您的req -newkey -x509 命令中以生成一个未加密 私钥文件,在您解除PEM 之后它确实KeyFactory 和@ 中工作987654334@。 (它仍然拼写为-nodes,尽管几十年来没有使用它的加密并不是简单的,也就是单一的DES。)当然,使用未加密的私钥文件意味着系统上任何可以读取该文件的入侵者或恶意软件都可以获取您的私钥,在很多情况下是有风险的。

  • 最后,如果您真的只想要密钥对而不是证书,请不要理会req -newkey -x509。而是使用openssl genpkey 生成私钥,或者使用较旧但更简单的openssl genrsa -nodes 后跟(或通过管道传送到)openssl pkcs8 -topk8 -nocrypt 将其转换为PKCS8 未加密的格式。然后使用openssl pkey -pubout 或旧的openssl rsa -pubout 使用公钥创建一个单独的文件。这些命令可以写入(并在适用的情况下回读)DER 格式而不是 PEM;如果你这样做,你的代码不需要 de-PEM 步骤,你可以将二进制文件内容传递给KeyFactory。未加密文件的风险与上述相同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-15
    • 2019-08-15
    • 1970-01-01
    • 1970-01-01
    • 2022-01-13
    • 2019-10-12
    • 1970-01-01
    相关资源
    最近更新 更多