2009-10-16 41 views
13

Tôi đang cố sử dụng chứng chỉ PEM (X.509) (được lưu trữ trong tệp privateKey.pem trên đĩa) để ký thư được gửi qua ổ cắm trong Java, nhưng có rất nhiều rắc rối khi tìm một ví dụ gần gũi. Tôi thường là một anh chàng C++, những người vừa bước vào để giúp đỡ dự án này, do đó, nó đã được một chút khó khăn cho tôi để đặt tất cả lại với nhau thành mã hoạt động khi tôi không quen với các API. Thật không may, tôi bị giới hạn ở các phương pháp chuẩn Java (1.6.0 Update 16), vì vậy mặc dù tôi đã tìm thấy một ví dụ tương tự sử dụng BouncyCastle 's PEMReader, nó đã không giúp ích nhiều cho dự án cụ thể này.Sử dụng mã khóa PEM được mã hóa, mã hóa PEM để ký thư một cách nguyên bản

My privateKey.pem quan trọng là mật khẩu bảo vệ, theo hình thức:

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

Họ chìa khóa được tạo ra sử dụng OpenSSL:

openssl.exe genrsa -out private_key.pem 4096 

tôi không thể chuyển đổi phím này để một DER hoặc định dạng khác trước khi chạy, bất kỳ chuyển đổi nào cần thiết sẽ cần phải được thực hiện nội bộ trong mã, vì khóa cần phải được thay thế dễ dàng và định dạng sẽ vẫn là PEM.

Tôi đã nghe một sự pha trộn của những điều mà tôi không hoàn toàn chắc chắn về, và đã hy vọng tâm trí tập thể ở đây tại SO có thể giúp kéo các mảnh lại với nhau.



Tôi nghe nói rằng chứng chỉ PEM cần Base64 được giải mã để chuyển đổi nó thành chứng chỉ DER có thể được sử dụng. Tôi có một công cụ giải mã Base64 được gọi là MiGBase64 nhưng không hoàn toàn chắc chắn cách thức/khi giải mã này cần được thực hiện.


Tôi đã bị lạc trong tài liệu API Java đang cố gắng theo dõi 15 loại Khóa, Keystore, KeyGenerators, Chứng chỉ khác nhau, nhưng vẫn chưa đủ quen thuộc với bất kỳ mà tôi cần phải sử dụng và cách sử dụng chúng cùng nhau.


Các thuật toán cơ bản có vẻ khá đơn giản, đó là lý do nó đã được đặc biệt bực bội mà tôi đã không thể để viết một thực hiện bình đẳng đơn giản:

1) Đọc privateKey.pem từ file
2) Tải Key tin vào lớp XXX, sử dụng Passphrase để giải mã khóa
3) sử dụng các đối tượng chủ chốt với lớp Chữ ký để ký thông điệp



Trợ giúp với điều này, Espec mã ví dụ ially, được đánh giá cao. Tôi đã đấu tranh để tìm các ví dụ hữu ích cho vấn đề này, vì hầu hết các ví dụ 'đóng' đều tạo ra các khóa mới, sử dụng BouncyCastle, hoặc chỉ sử dụng các dạng khóa/lớp khác nhau không áp dụng ở đây.

Điều này có vẻ như là một vấn đề thực sự đơn giản nhưng nó khiến tôi phát điên, bất kỳ câu trả lời thực sự đơn giản nào?

Trả lời

19

Nếu bạn đang sử dụng BouncyCastle, hãy thử như sau:

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

Cảm ơn thời gian và nỗ lực mà đi vào câu trả lời của bạn. Tôi đã phải thực hiện một vài sửa đổi để làm cho nó hoạt động với các phím của tôi, nhưng bạn dẫn đường. Nhiều đánh giá cao! – KevenK

+1

@KevenK Tốt để nghe. Những sửa đổi nào là cần thiết? – Kevin

4

Lệnh OpenSSL tạo cặp khóa và mã hóa nó trong PKCS # 1 định dạng. Nếu bạn không sử dụng mã hóa (không cung cấp mật khẩu cho lệnh), PEM chỉ đơn giản là DER được mã hóa Base64 cho RSCSrivateKey PKCS # 1.

Thật không may, JCE của Sun không cung cấp giao diện công khai để đọc khóa ở định dạng này. Bạn có 2 tùy chọn,

  1. Nhập khóa vào kho khóa và bạn có thể đọc từ đó. Keytool không cho phép nhập khóa riêng tư. Bạn có thể tìm thấy các công cụ khác để thực hiện việc này.

  2. Thư viện OAuth có chức năng xử lý việc này. Nhìn vào mã ở đây,

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

+0

Thông tin hữu ích, cảm ơn vì đã dành thời gian để trả lời :) Tôi đã đi với BouncyCastle vì nó có vẻ phù hợp với việc sử dụng cụ thể của tôi, nhưng tôi đánh giá cao sự giúp đỡ. Cảm ơn – KevenK

Các vấn đề liên quan