2009-10-16 4 views
13

J'essaie d'utiliser un certificat PEM (X.509) (stocké dans un fichier privateKey.pem sur le disque) pour signer les messages envoyés via des sockets en Java, mais j'ai beaucoup de mal à trouver un exemple proche. Normalement, je suis un gars C++ qui intervient sur ce projet, donc ça a été un peu difficile pour moi de tout mettre dans un code qui fonctionne quand je ne suis pas familier avec les API. Malheureusement, je suis limité aux méthodes standard avec Java (1.6.0 Update 16), donc bien que j'ai trouvé un exemple similaire en utilisant de BouncyCastle, cela n'a pas aidé beaucoup sur ce projet particulier.Utilisation d'une clé privée cryptée codée PEM pour signer un message en mode natif

Ma clé privateKey.pem est protégée par une phrase, sous la forme de:

-----BEGIN RSA PRIVATE KEY----- 
Proc-Type: 4,ENCRYPTED DEK-Info: 
DES-EDE3-CBC,63A862F284B1280B 
[...] 
tsjQI4H8lhOBuk+NelHu7h2+uqbBQzwkPoA8IqbPXUz+B/MAGhoTGl4AKPjfm9gu 
OqEorRU2vGiSaUPgDaRhdPKK0stxMxbByUi8xQ2156d/Ipk2IPLSEZDXONrB/4O5 
[...] 
-----END RSA PRIVATE KEY----- 

La clé a été générée en utilisant OpenSSL:

openssl.exe genrsa -out private_key.pem 4096 

Je suis incapable de convertir cette clé d'une DER ou autre format avant l'exécution, toutes les conversions nécessaires devront être effectuées en interne dans le code, car la clé doit être facilement remplaçable et le format restera PEM.

J'ai entendu un mélange de choses dont je ne suis pas tout à fait sûr, et j'espérais que les esprits collectifs ici à SO pourraient aider à rassembler les pièces. J'ai entendu dire que le certificat PEM nécessite Base64 Decoded pour le convertir en un certificat DER qui peut être utilisé. J'ai un outil de décodage Base64 appelé MiGBase64 mais je ne suis pas tout à fait sûr comment/quand ce décodage a besoin d'être fait. Je me suis perdu dans les documents de l'API Java essayant de retrouver les 15 différents types de clés, de keystores, de keygenerators, de certificats, etc. qui existent, mais je ne suis pas assez familier avec aucun d'entre eux pour identifier correctement que je dois utiliser, et comment les utiliser ensemble.


L'algorithme de base semble assez simple, ce qui est la raison pour laquelle il a été particulièrement frustrant que je ne l'ai pas été en mesure d'écrire une implémentation tout aussi simple:

1) Lire le privateKey.pem de fichier
2) Chargez la clé privée en classe XXX, en utilisant le mot de passe pour déchiffrer la clé
3) Utilisez l'objet clé avec la classe Signature pour signer le message



Aide à cela, spéc Exemple de code ially, est grandement apprécié. J'ai eu du mal à trouver des exemples utiles pour ce problème, car la plupart des exemples «proches» génèrent de nouvelles clés, en utilisant BouncyCastle, ou tout simplement en utilisant différentes formes de clés/classes non applicables ici.

Cela semble être un problème très simple, mais ça me rend fou, des réponses vraiment simples?

Répondre

19

Si vous utilisez BouncyCastle, procédez comme suit:

import java.io.File; 
import java.io.FileReader; 
import java.io.IOException; 
import java.security.KeyPair; 
import java.security.Security; 
import java.security.Signature; 
import java.util.Arrays; 

import org.bouncycastle.jce.provider.BouncyCastleProvider; 
import org.bouncycastle.openssl.PEMReader; 
import org.bouncycastle.openssl.PasswordFinder; 
import org.bouncycastle.util.encoders.Hex; 

public class SignatureExample { 

    public static void main(String [] args) throws Exception { 
     Security.addProvider(new BouncyCastleProvider()); 

     String message = "hello world"; 
     File privateKey = new File("private.pem"); 
     KeyPair keyPair = readKeyPair(privateKey, "password".toCharArray()); 

     Signature signature = Signature.getInstance("SHA256WithRSAEncryption"); 
     signature.initSign(keyPair.getPrivate()); 
     signature.update(message.getBytes()); 
     byte [] signatureBytes = signature.sign(); 
     System.out.println(new String(Hex.encode(signatureBytes))); 

     Signature verifier = Signature.getInstance("SHA256WithRSAEncryption"); 
     verifier.initVerify(keyPair.getPublic()); 
     verifier.update(message.getBytes()); 
     if (verifier.verify(signatureBytes)) { 
      System.out.println("Signature is valid"); 
     } else { 
      System.out.println("Signature is invalid"); 
     } 
    } 

    private static KeyPair readKeyPair(File privateKey, char [] keyPassword) throws IOException { 
     FileReader fileReader = new FileReader(privateKey); 
     PEMReader r = new PEMReader(fileReader, new DefaultPasswordFinder(keyPassword)); 
     try { 
      return (KeyPair) r.readObject(); 
     } catch (IOException ex) { 
      throw new IOException("The private key could not be decrypted", ex); 
     } finally { 
      r.close(); 
      fileReader.close(); 
     } 
    } 

    private static class DefaultPasswordFinder implements PasswordFinder { 

     private final char [] password; 

     private DefaultPasswordFinder(char [] password) { 
      this.password = password; 
     } 

     @Override 
     public char[] getPassword() { 
      return Arrays.copyOf(password, password.length); 
     } 
    } 
} 
+0

Merci pour le temps et l'effort qui est entré dans votre réponse. J'ai dû faire quelques modifications pour le faire fonctionner avec mes clés, mais vous montrez la voie. Très appréciée! – KevenK

+1

@KevenK Bon à entendre. Quelles modifications étaient nécessaires? – Kevin

4

La commande OpenSSL génère paire de clés et code au format PKCS # 1. Si vous n'utilisez pas le cryptage (n'a pas fourni de mot de passe pour la commande), le PEM est simplement DER codé en Base64 pour la clé RSAPrivateKey PKCS # 1.

Malheureusement, le JCE de Sun ne fournit pas d'interface publique pour lire la clé dans ce format. Vous avez 2 options,

  1. Importez la clé dans le magasin de clés et vous pourrez la lire à partir de là. Keytool ne permet pas l'importation de clés privées. Vous pouvez trouver d'autres outils pour le faire.

  2. La bibliothèque OAuth a une fonction pour gérer cela. Regardez le code ici,

http://oauth.googlecode.com/svn/code/java/core/commons/src/main/java/net/oauth/signature/pem/PKCS1EncodedKeySpec.java

+0

Informations utiles, merci d'avoir pris le temps de répondre :) Je suis allé avec BouncyCastle parce que cela semblait convenir à mon usage particulier, mais j'apprécie l'aide. Merci – KevenK

Questions connexes