92 votes

Comment lire un fichier .pem pour obtenir une clé privée et une clé publique ?

J'écris un petit morceau de code qui lit les clés publiques et privées stockées dans le fichier .pem. J'utilise les commandes suivantes pour générer les clés.

La commande ci-dessous permet de générer une paire de clés.

   $openssl genrsa -out mykey.pem 2048

Cette commande permet de générer la clé privée

$openssl pkcs8 -topk8 -inform PEM -outform PEM -in mykey.pem \
    -out private_key.pem -nocrypt

et cette commande pour obtenir la clé publique.

$ openssl rsa -in mykey.pem -pubout -outform DER -out public_key.der

J'ai écrit deux méthodes qui lisent respectivement la clé privée et la clé publique.

   public  PrivateKey getPemPrivateKey(String filename, String algorithm) throws Exception {
      File f = new File(filename);
      FileInputStream fis = new FileInputStream(f);
      DataInputStream dis = new DataInputStream(fis);
      byte[] keyBytes = new byte[(int) f.length()];
      dis.readFully(keyBytes);
      dis.close();

      String temp = new String(keyBytes);
      String privKeyPEM = temp.replace("-----BEGIN PRIVATE KEY-----\n", "");
      privKeyPEM = privKeyPEM.replace("-----END PRIVATE KEY-----", "");
      //System.out.println("Private key\n"+privKeyPEM);

      Base64 b64 = new Base64();
      byte [] decoded = b64.decode(privKeyPEM);

      PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);
      KeyFactory kf = KeyFactory.getInstance(algorithm);
      return kf.generatePrivate(spec);
      }

   public  PublicKey getPemPublicKey(String filename, String algorithm) throws Exception {
      File f = new File(filename);
      FileInputStream fis = new FileInputStream(f);
      DataInputStream dis = new DataInputStream(fis);
      byte[] keyBytes = new byte[(int) f.length()];
      dis.readFully(keyBytes);
      dis.close();

      String temp = new String(keyBytes);
      String publicKeyPEM = temp.replace("-----BEGIN PUBLIC KEY-----\n", "");
      publicKeyPEM = publicKeyPEM.replace("-----END PUBLIC KEY-----", "");

      Base64 b64 = new Base64();
      byte [] decoded = b64.decode(publicKeyPEM);

      X509EncodedKeySpec spec =
            new X509EncodedKeySpec(decoded);
      KeyFactory kf = KeyFactory.getInstance(algorithm);
      return kf.generatePublic(spec);
      }

J'ai l'impression que c'est une façon naïve de faire. Je n'ai pas trouvé de meilleure façon de le faire sur Internet. Quelqu'un peut-il me suggérer la meilleure façon d'écrire le même code pour gérer les cas génériques. Je ne veux pas utiliser de bibliothèque tierce.
Je n'ai qu'une connaissance très basique du chant/chiffrage et n'utilise pratiquement aucune API de sécurité java. Donc, si je ne comprends pas quelque chose, veuillez me le signaler.

3voto

user5632199 Points 31

Je pense que dans la définition de votre clé privée, vous devez remplacer :

X509EncodedKeySpec spec = new X509EncodedKeySpec(decoded);

con:

PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);

Regardez votre openssl commandement :

$openssl **pkcs8** -topk8 -inform PEM -outform PEM -in mykey.pem \ -out private_key.pem -nocrypt

Et l'exception java :

Only PCKS8 codification

2voto

Andries Points 51

Les librairies Java permettent de lire le certificat public, tel qu'il est généré par openssl, d'une seule traite :

val certificate: X509Certificate = ByteArrayInputStream(
        publicKeyCert.toByteArray(Charsets.US_ASCII))
        .use {
            CertificateFactory.getInstance("X.509")
                    .generateCertificate(it) as X509Certificate
        }

Mais, o hell, la lecture de la clé privée était problématique :

  1. Il a d'abord fallu supprimer les balises de début et de fin, ce qui n'est pas nécessaire pour lire la clé publique.
  2. Ensuite, j'ai dû enlever toutes les nouvelles lignes, sinon il crève !
  3. Ensuite, j'ai dû décoder les octets en utilisant l'octet 64.
  4. Ensuite, j'ai pu produire un RSAPrivateKey .

voir ça : Solution finale en kotlin

1voto

auntyellow Points 985

Si un PEM ne contient qu'une seule clé privée RSA sans cryptage, il doit s'agir d'une structure de séquence ASN.1 comprenant 9 chiffres pour présenter une clé privée RSA. Théorème du reste de la Chine (CRT) :

  1. version (toujours 0)
  2. module (n)
  3. exponent public (e, toujours 65537)
  4. exposant privé (d)
  5. prime p
  6. prime q
  7. d mod (p - 1) (dp)
  8. d mod (q - 1) (dq)
  9. q^-1 mod p (qinv)

Nous pouvons mettre en œuvre un RSAPrivateCrtKey :

class RSAPrivateCrtKeyImpl implements RSAPrivateCrtKey {
    private static final long serialVersionUID = 1L;

    BigInteger n, e, d, p, q, dp, dq, qinv;

    @Override
    public BigInteger getModulus() {
        return n;
    }

    @Override
    public BigInteger getPublicExponent() {
        return e;
    }

    @Override
    public BigInteger getPrivateExponent() {
        return d;
    }

    @Override
    public BigInteger getPrimeP() {
        return p;
    }

    @Override
    public BigInteger getPrimeQ() {
        return q;
    }

    @Override
    public BigInteger getPrimeExponentP() {
        return dp;
    }

    @Override
    public BigInteger getPrimeExponentQ() {
        return dq;
    }

    @Override
    public BigInteger getCrtCoefficient() {
        return qinv;
    }

    @Override
    public String getAlgorithm() {
        return "RSA";
    }

    @Override
    public String getFormat() {
        throw new UnsupportedOperationException();
    }

    @Override
    public byte[] getEncoded() {
        throw new UnsupportedOperationException();
    }
}

Ensuite, lisez la clé privée à partir d'un fichier PEM :

import sun.security.util.DerInputStream;
import sun.security.util.DerValue;

static RSAPrivateCrtKey getRSAPrivateKey(String keyFile) {
    RSAPrivateCrtKeyImpl prvKey = new RSAPrivateCrtKeyImpl();
    try (BufferedReader in = new BufferedReader(new FileReader(keyFile))) {
        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = in.readLine()) != null) {
            // skip "-----BEGIN/END RSA PRIVATE KEY-----"
            if (!line.startsWith("--") || !line.endsWith("--")) {
                sb.append(line);
            }
        }
        DerInputStream der = new DerValue(Base64.
                getDecoder().decode(sb.toString())).getData();
        der.getBigInteger(); // 0
        prvKey.n = der.getBigInteger();
        prvKey.e = der.getBigInteger(); // 65537
        prvKey.d = der.getBigInteger();
        prvKey.p = der.getBigInteger();
        prvKey.q = der.getBigInteger();
        prvKey.dp = der.getBigInteger();
        prvKey.dq = der.getBigInteger();
        prvKey.qinv = der.getBigInteger();
    } catch (IllegalArgumentException | IOException e) {
        logger.warn(keyFile + ": " + e.getMessage());
        return null;
    }
}

1voto

Kristo Aun Points 141

Lire la clé publique de pem (PK ou Cert). Dépend de Bouncycastle.

private static PublicKey getPublicKeyFromPEM(Reader reader) throws IOException {

    PublicKey key;

    try (PEMParser pem = new PEMParser(reader)) {
        JcaPEMKeyConverter jcaPEMKeyConverter = new JcaPEMKeyConverter();
        Object pemContent = pem.readObject();
        if (pemContent instanceof PEMKeyPair) {
            PEMKeyPair pemKeyPair = (PEMKeyPair) pemContent;
            KeyPair keyPair = jcaPEMKeyConverter.getKeyPair(pemKeyPair);
            key = keyPair.getPublic();
        } else if (pemContent instanceof SubjectPublicKeyInfo) {
            SubjectPublicKeyInfo keyInfo = (SubjectPublicKeyInfo) pemContent;
            key = jcaPEMKeyConverter.getPublicKey(keyInfo);
        } else if (pemContent instanceof X509CertificateHolder) {
            X509CertificateHolder cert = (X509CertificateHolder) pemContent;
            key = jcaPEMKeyConverter.getPublicKey(cert.getSubjectPublicKeyInfo());
        } else {
            throw new IllegalArgumentException("Unsupported public key format '" +
                pemContent.getClass().getSimpleName() + '"');
        }
    }

    return key;
}

Lire la clé privée du PEM :

private static PrivateKey getPrivateKeyFromPEM(Reader reader) throws IOException {

    PrivateKey key;

    try (PEMParser pem = new PEMParser(reader)) {
        JcaPEMKeyConverter jcaPEMKeyConverter = new JcaPEMKeyConverter();
        Object pemContent = pem.readObject();
        if (pemContent instanceof PEMKeyPair) {
            PEMKeyPair pemKeyPair = (PEMKeyPair) pemContent;
            KeyPair keyPair = jcaPEMKeyConverter.getKeyPair(pemKeyPair);
            key = keyPair.getPrivate();
        } else if (pemContent instanceof PrivateKeyInfo) {
            PrivateKeyInfo privateKeyInfo = (PrivateKeyInfo) pemContent;
            key = jcaPEMKeyConverter.getPrivateKey(privateKeyInfo);
        } else {
            throw new IllegalArgumentException("Unsupported private key format '" +
                pemContent.getClass().getSimpleName() + '"');
        }
    }

    return key;
}

0voto

bestrocker221 Points 17

Pour obtenir la clé publique, il suffit de faire :

public static PublicKey getPublicKeyFromCertFile(final String certfile){

     return new X509CertImpl(new FileInputStream(new File(certfile))).getPublicKey();

Pour obtenir la clé privée est plus délicat, vous pouvez :

public static PrivateKey getPrivateKeyFromKeyFile(final String keyfile){
    try {
        Process p;
        p = Runtime.getRuntime().exec("openssl pkcs8 -nocrypt -topk8 -inform PEM " +
                "-in " + keyfile + " -outform DER -out " + keyfile + ".der");

        p.waitFor();
        System.out.println("Command executed" + (p.exitValue() == 0 ? " successfully" : " with error" ));
    } catch ( IOException | InterruptedException e) {
        e.printStackTrace();
        System.exit(1);
    }

    PrivateKey myPrivKey = null;
    try {
        byte[] keyArray = Files.readAllBytes(Paths.get(keyfile + ".der"));
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyArray);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        myPrivKey = keyFactory.generatePrivate(keySpec);
    } catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException e){
        e.printStackTrace();
        System.exit(1);
    }

    return myPrivKey;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X