2011-07-11 13 views
5

Tôi đang làm việc trên IBM iSeries cũ hơn (IBM-i, i5OS, AS/400, v.v.), với Java 5 JVM (Classic, chứ không phải ITJ J9) trên phiên bản O/S V5R3M0.Tại sao tôi nhận được lỗi "Không thể lưu trữ khóa không phải riêng tư" khi tạo một Socket SSL trong Java?

Dưới đây là kịch bản trong một nutshell:

  1. Tôi tạo ra một chìa khóa cửa hàng kiểu JKS sử dụng Portecle 1.7 (Lưu ý: Tôi đã thử chuyển đổi của tôi khóa cửa hàng để JCEKS nhưng điều đó đã bị từ chối như một định dạng được hỗ trợ JKS là lựa chọn duy nhất với máy iSeries (ít nhất là phiên bản tôi đang sử dụng)
  2. Sau đó, tôi đã tạo một cặp khóa và CSR và gửi CSR tới Thawte để được ký.
  3. I đã nhập chứng chỉ đã ký từ Thawte thành công bằng định dạng PKCS # 7 để nhập toàn bộ chuỗi chứng chỉ, bao gồm chứng chỉ của tôi icate, trung gian Thawte và gốc máy chủ Thawte.

Tất cả đều hoạt động như mong đợi.

Tuy nhiên, khi tôi chạy JVM, được định cấu hình đúng để trỏ đến cửa hàng và cung cấp mật khẩu (mà tôi đã thực hiện trước đây với chứng chỉ tự ký được tạo trong Portecle để thử nghiệm) và cố gắng khởi động web của tôi máy chủ trên 443, tôi nhận được ngoại lệ bảo mật sau:

java.security.KeyStoreException: Cannot store non-PrivateKeys 

Bất cứ ai có thể cho tôi biết nơi tôi đã đi sai hoặc những gì tôi nên kiểm tra tiếp theo?

Trả lời

3

Thay vì sử dụng một keystore phù du, bạn có thể xử lý tất cả mọi thứ trong một đơn SSLContext.

Bạn cần phải khởi tạo SSLContext bằng cách sử dụng tùy chỉnh X509KeyManager thay vì sử dụng tùy chọn được cung cấp theo mặc định KeyManagerFactory. Trong số X509KeyManager, chooseServerAlias(String keyType, Principal[] issuers, Socket socket) này sẽ trả về một bí danh khác tùy thuộc vào địa chỉ cục bộ thu được từ ổ cắm. Bằng cách này, bạn sẽ không phải lo lắng về việc sao chép khóa riêng từ kho khóa này sang kho khóa khác, và điều này thậm chí sẽ hoạt động đối với các loại kho khóa mà bạn không thể trích xuất (và do đó sao chép) nhưng chỉ sử dụng riêng tư chìa khóa, ví dụ PKCS # 11.

+0

Cảm ơn; Tôi sẽ xem xét điều này. –

28

Thông báo lỗi "Không thể lưu trữ khóa riêng tư" thường cho biết bạn đang cố gắng sử dụng các khóa đối xứng bí mật với loại khóa cửa hàng JKS. Loại kho khóa JKS chỉ hỗ trợ khóa không đối xứng (công khai/riêng tư). Bạn sẽ phải tạo một kho khóa mới của loại JCEKS để hỗ trợ các khóa bí mật.

+0

Cảm ơn, vâng. Điều bí ẩn là * tại sao * tạo một ổ cắm SSL đã cố gắng thêm * bất kỳ * khóa nào vào kho khóa. Nó chỉ ra tôi đã cố gắng để thêm một phím null để một cửa hàng chính - chi tiết là trong câu trả lời tự của tôi cho câu hỏi này. –

3

Khi nó quay ra, đây là một vấn đề tinh tế, và nó có giá trị đưa ra câu trả lời ở đây trong trường hợp người khác có một cái gì đó tương tự.

Câu trả lời TLDR là tôi không kiểm tra khóa và chứng chỉ của tôi không rỗng và kết quả là đã thêm khóa và chứng chỉ rỗng vào kho khóa. Câu trả lời dài hơn sau. Cách chúng tôi có máy chủ web được thiết lập để sử dụng SSL, đặc biệt để hỗ trợ cấu hình điển hình của người dùng nơi địa chỉ IP được sử dụng để định cấu hình địa chỉ nghe trang web thay vì tên DNS, là nó xác định chứng chỉ khóa-lưu trữ chính sử dụng bí danh và tạo khóa lưu trữ tạm thời chỉ chứa chứng chỉ cho trang web đó, sử dụng khóa khóa đó để định cấu hình ngữ cảnh SSL và nhà máy socket SSL, như vậy:

// CREATE EPHEMERAL KEYSTORE FOR THIS SOCKET USING THE DESIRED CERTIFICATE 
try { 
    final char[]  BLANK_PWD=new char[0]; 
    SSLContext  ctx=SSLContext.getInstance("TLS"); 
    KeyManagerFactory kmf=KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 
    Key    ctfkey=mstkst.getKey(svrctfals,BLANK_PWD); 
    Certificate[]  ctfchn=mstkst.getCertificateChain(svrctfals); 
    KeyStore   sktkst; 

    sktkst=KeyStore.getInstance("jks"); 
    sktkst.load(null,BLANK_PWD); 
    sktkst.setKeyEntry(svrctfals,ctfkey,BLANK_PWD,ctfchn); 
    kmf.init(sktkst,BLANK_PWD); 
    ctx.init(kmf.getKeyManagers(),null,null); 
    ssf=ctx.getServerSocketFactory(); 
    } 
catch(java.security.GeneralSecurityException thr) { 
    throw new IOException("Cannot create server socket factory using ephemeral keystore ("+thr+")",thr); 
    } 

Lưu ý rằng nó sử dụng mật khẩu trống để trích xuất khóa cá nhân và chứng chỉ từ kho khóa chính. Đó là vấn đề của tôi - tôi đã có, từ thói quen sử dụng keytool, tạo cặp khóa riêng với mật khẩu (cùng mật khẩu với kho khóa).

Bởi vì tôi đã có một mật khẩu trên giấy chứng nhận, chìa khóa và giấy chứng nhận không được tách ra, và null được thông qua để sktkst.setKeyEntry(svrctfals,ctfkey,BLANK_PWD,ctfchn); Tuy nhiên, setKeyEntry kiểm tra các thông qua Key sử dụng instanceof và kết luận (chính xác) rằng null không phải là một instanceof PrivateKey, dẫn đến lỗi lầm mà tôi đã thấy.

Mã điều chỉnh kiểm tra rằng một chìa khóa và chứng chỉ được tìm thấy và gửi lỗi thích hợp:

// CREATE EPHEMERAL KEYSTORE FOR THIS SOCKET USING THE DESIRED CERTIFICATE 
try { 
    final char[]  BLANK_PWD=new char[0]; 
    SSLContext  ctx=SSLContext.getInstance("TLS"); 
    KeyManagerFactory kmf=KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 
    Key    ctfkey=mstkst.getKey(svrctfals,BLANK_PWD); 
    Certificate[]  ctfchn=mstkst.getCertificateChain(svrctfals); 
    KeyStore   sktkst; 

    if(ctfkey==null) { 
     throw new IOException("Cannot create server socket factory: No key found for alias '"+svrctfals+"'"); 
     } 
    if(ctfchn==null || ctfchn.length==0) { 
     throw new IOException("Cannot create server socket factory: No certificate found for alias '"+svrctfals+"'"); 
     } 

    sktkst=KeyStore.getInstance("jks"); 
    sktkst.load(null,BLANK_PWD); 
    sktkst.setKeyEntry(svrctfals,ctfkey,BLANK_PWD,ctfchn); 
    kmf.init(sktkst,BLANK_PWD); 
    ctx.init(kmf.getKeyManagers(),null,null); 
    ssf=ctx.getServerSocketFactory(); 
    } 
catch(java.security.GeneralSecurityException thr) { 
    throw new IOException("Cannot create server socket factory using ephemeral keystore ("+thr+")",thr); 
    } 
Các vấn đề liên quan