java - Как генерировать PrivateKey и PublicKey, начиная с хранилища ключей (.p12) - PullRequest
1 голос
/ 14 января 2020

Сгенерируйте некоторые ключи с помощью 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

Я сохранил в своем хранилище vault.keys из wildfly: private_key_base64_en c .txt и public_key_base64_en c .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);
        }

    }

Когда я пытаюсь сгенерировать ключи, я возвращаю ноль:

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;
    }

Ответы [ 2 ]

1 голос
/ 15 января 2020

Ваш 'publi c ключ' на самом деле является сертификатом (в частности, сертификатом X.509 v1 или v3, в зависимости от вашей конфигурации openssl), который содержит публичный ключ, но отличается от публичного ключа - - и в формате PEM, даже если вы ошибочно назвали его .der - и ваш приватный ключ зашифрован.

В дополнение к подходу с использованием PKCS12, как справедливо предлагает Роберто, и, как правило, является самым простым, потому что это только один файл для управления , а все еще зашифрован и, следовательно, более безопасен:

  • Java может обрабатывать сертификат X.509, но вы используете CertificateFactory.getInstance("X.509") и дайте ему InputStream вместо KeyFactory и X509EncodedKeySpec. CertificateFactory может обрабатывать PEM или DER, в отличие от KeyFactory, который может обрабатывать только DER, поэтому вам не нужны части de-PEM (полосы BEGIN / END / EOL и декодирования base64).

  • стандарт Java не может напрямую обрабатывать зашифрованные ключи PKCS8. Если вы можете добавить стороннюю библиотеку, bcpkix BouncyCastle может сделать это; ищите дюжину или около того существующих Q, которые используют PEMParser (не PEMReader, это более старая версия) и JceOpenSSLPKCS8DecryptorBuilder. В противном случае вы можете добавить -nodes к вашей команде req -newkey -x509, чтобы сгенерировать незашифрованный файл privatekey, который после де-PEM работает в KeyFactory с PKCS8EncodedKeySpec , (Это все еще пишется -nodes, хотя шифрование, используемое без него, десятилетиями не было простым DES.) Использование незашифрованного файла privatekey, конечно, означает, что любой злоумышленник или вредоносная программа в вашей системе, которая может прочитать этот файл, может получить ваш privatekey, который во многих ситуациях является риском.

  • наконец, если вы действительно хотите только пару ключей, а не сертификат, не беспокойтесь о req -newkey -x509 , Вместо этого используйте openssl genpkey для генерации закрытого ключа или более старый, но более простой openssl genrsa -nodes , а затем (или переданный по каналу) openssl pkcs8 -topk8 -nocrypt для преобразования его в незашифрованный формат PKCS8. Затем используйте openssl pkey -pubout или более старый openssl rsa -pubout, чтобы создать отдельный файл с помощью публичной клавиши. Эти команды могут записывать (и читать обратно, где это применимо) формат DER вместо PEM; если вы сделаете это, ваш код не требует шагов de-PEM, вы можете просто передать содержимое двоичного файла в KeyFactory. Риски для незашифрованного файла такие же, как указано выше.

1 голос
/ 15 января 2020

Может быть, вы генерируете хранилище ключей без назначения действительного псевдонима, глядя на свою команду, вы не используете опцию -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;
}

Затем извлечь ключи RSAPublicKey и RSAPrivateKey из KeyPair:

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

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

Надеюсь, что это может быть полезным и удачи с вашими Json веб-токенами! : -Р

...