我需要在一個套接字中使用ssl連接進行密鑰對,而不會在客戶端中進行任何更改。在一個SSL套接字連接中使用兩個私鑰(keystore)和兩個公鑰(truststore)
爲什麼?
因爲一個客戶端在信任存儲中使用CN屬性進行連接握手,而其他客戶端使用同一屬性中的另一個值以相同的方式處理相同的任務。
所以我需要使用兩個關鍵的商店(私人)具有獨特的屬性,CN和也別名和份額兩種不同的信任存儲(公鑰)具有獨特的CN屬性也是別名也。
描述波紋管:
keyStore1
密鑰庫類型:JKS
密鑰庫提供:SUN
別名:對identity1
擁有者:CN = APP1 ...
發行人:CN = APP1 ...
trustStore1
別名:對identity1
業主:CN = APP1 ...
發行機構:CN = app1 ...
keyStore2
別名:identity2
業主:CN = APP 2 ...
發行人:CN = APP 2 ...
trustStore2
別名:identity2
業主:CN = APP 2 ...
發行人:CN = APP 2 ...
我試圖實現這種方式這個功能:
KeyStore KeyStore1;
try {
String keyStoreFile1 = "privatekey1";
String keyStoreType1 = "jks";
char[] keyStorePwd1 = "password".toCharArray();
keyStore1 = KeyStore.getInstance(keyStoreType1);
keyStore1.load(new FileInputStream(keyStoreFile1), keyStorePwd1);
} catch (java.security.GeneralSecurityException thr) {
throw new IOException("Cannot load keystore (" + thr + ")");
}
KeyStore trustStore1;
try {
String trustStoreFile1 = "publickey1";
String trustStoreType1 = "jks";
char[] trustStorePwd1 = "password".toCharArray();
trustStore1 = KeyStore.getInstance(trustStoreType1);
trustStore.load(new FileInputStream(trustStoreFile1), trustStorePwd1);
} catch (java.security.GeneralSecurityException thr) {
throw new IOException("Cannot load truststore (" + thr + ")");
}
KeyStore keyStore2;
try {
String keyStoreFile2 = "privatekey2";
String keyStoreType2 = "jks";
char[] keyStorePwd2 = "anotherpass".toCharArray();
keyStore2 = KeyStore.getInstance(key2StoreType);
keyStore2.load(new FileInputStream(keyStoreFile2), keyStorePwd2);
} catch (java.security.GeneralSecurityException thr) {
throw new IOException("Cannot load keystore (" + thr + ")");
}
KeyStore trustStore2;
try {
String trustStoreFile2 = "publickey2";
String trustStoreType2 = "jks";
char[] trustStorePwd2 = "anotherpass".toCharArray();
trustStore2 = KeyStore.getInstance(trustStoreType2);
trustStore2.load(new FileInputStream(trustStoreFile2), trustStorePwd2);
} catch (java.security.GeneralSecurityException thr) {
throw new IOException("Cannot load truststore (" + thr + ")");
}
KeyManagerFactory kmfkey1 = KeyManagerFactory
.getInstance(KeyManagerFactory.getkey1Algorithm());
kmfkey1.init(keyStore1, "password".toCharArray());
TrustManagerFactory tmfkey1 =
TrustManagerFactory.getInstance(TrustManagerFactory.getkey1Algorithm());
tmfkey1.init(trustStore1);
SSLContext ctx = SSLContext.getInstance("SSL");
ctx.init(kmfkey1.getKeyManagers(), tmfkey1.getTrustManagers(), null);
KeyManagerFactory kmfkey2 = KeyManagerFactory.
getInstance(KeyManagerFactory.getkey1Algorithm());
kmfkey2.init(keyStore2, "password".toCharArray());
TrustManagerFactory tmfkey2 = TrustManagerFactory
.getInstance(TrustManagerFactory.getkey1Algorithm());
tmfkey2.init(trustStore2);
SSLContext ctxkey2 = SSLContext.getInstance("SSL");
ctxkey2.init(kmfkey2.getKeyManagers(), tmfkey2.getTrustManagers(), null);
SSLServerSocketFactory sslSrvSockFact = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
serverSocket = sslSrvSockFact.createServerSocket(port);
... 但我收到此錯誤信息
...
java.security.KeyStoreException:在com.sun.net未初始化密鑰庫中 在java.security.KeyStore.aliases(KeyStore.java:941) .ssl.internal.ssl.SunX509KeyManagerImpl。(SunX509KeyManagerImpl.java:106) at com.sun.net.ssl.internal.ssl.KeyManagerFactoryImpl $ SunX509.engineInit(KeyManagerFactoryImpl.java:41) at javax.net.ssl。 KeyManagerFactory.init(KeyManagerFactory.java:192)
...
所以我真的很想知道是否有可能在一個套接字連接中使用密鑰對,或以不能看到或處理的不同方式解決此問題。
編輯1
布魯諾,你能不能給我一個完整的或完整的例子嗎?
因爲目前尚不清楚我....
我想兩件事情:
One解決方案就是把兩個鍵下面先前建議的私人密鑰庫裏面...但不工作我接收波紋管的消息:在螺紋
異常 「主」 java.lang.NoSuchMethodError:javax.net.ssl.SSLContext.setDefault(Ljavax /淨/ SSL /的SSLContext) 在... initialiseManager( 499)
你是對的...我還需要選擇其中一個方面或
javax.net.ssl.SSLException: 無證書或密鑰對應於啓用的SSL密碼套件。 而試圖將消息發送到服務器...
其次,我跟着你的建議......但是SSL Socket連接不啓動
我實現了這個與許多其他人是邊打邊 幫助代碼在這裏本網站...感謝所有
private KeyManager[] getKeyManagers() throws IOException, GeneralSecurityException {
// First, get the default KeyManagerFactory.
String alg = KeyManagerFactory.getDefaultAlgorithm();
KeyManagerFactory kmFact = KeyManagerFactory.getInstance(alg);
// Next, set up the KeyStore to use. We need to load the file into
// a KeyStore instance.
File keyFile = new File("privatekey1");
FileInputStream fis = new FileInputStream(keyFile);
LogManager.log("Loaded keystore privatekey1 " + keyFile.getAbsolutePath(),
LogManager.LOG_LOWEST_LEVEL);
KeyStore ks = KeyStore.getInstance("jks");
String keyStorePassword = "password";
ks.load(fis, keyStorePassword.toCharArray());
fis.close();
// Now we initialise the KeyManagerFactory with this KeyStore
kmFact.init(ks, keyStorePassword.toCharArray());
KeyManagerFactory dkmFact = KeyManagerFactory.getInstance(alg);
File keyFileTwo = new File("privatekey2");
FileInputStream fisTwo = new FileInputStream(keyFileTwo);
LogManager.log("Loaded keystore privatekey2 " + keyFileTwo.getAbsolutePath(), LogManager.LOG_LOWEST_LEVEL);
KeyStore ksTwo = KeyStore.getInstance("jks");
String keyStorePasswordTwo = "password";
ksTwo.load(fisTwo, keyStorePasswordTwo.toCharArray());
fisTwo.close();
// Now we initialise the KeyManagerFactory with this KeyStore
dkmFact.init(ksTwo, keyStorePasswordTwo.toCharArray());
// default
//KeyManagerFactory dkmFact = KeyManagerFactory.getInstance(alg);
//dkmFact.init(null, null);
// Get the first X509KeyManager in the list
X509KeyManager customX509KeyManager = getX509KeyManager(alg, kmFact);
X509KeyManager jvmX509KeyManager = getX509KeyManager(alg, dkmFact);
KeyManager[] km = {new MultiKeyStoreManager(jvmX509KeyManager, customX509KeyManager)};
LogManager.log("Number of key managers registered:" + km.length, LogManager.LOG_LOWEST_LEVEL);
return km;
}
/**
* Find a X509 Key Manager compatible with a particular algorithm
* @param algorithm
* @param kmFact
* @return
* @throws NoSuchAlgorithmException
*/
private X509KeyManager getX509KeyManager(String algorithm, KeyManagerFactory kmFact)
throws NoSuchAlgorithmException {
KeyManager[] keyManagers = kmFact.getKeyManagers();
if (keyManagers == null || keyManagers.length == 0) {
throw new NoSuchAlgorithmException("The default algorithm :" + algorithm + " produced no key managers");
}
X509KeyManager x509KeyManager = null;
for (int i = 0; i < keyManagers.length; i++) {
if (keyManagers[i] instanceof X509KeyManager) {
x509KeyManager = (X509KeyManager) keyManagers[i];
break;
}
}
if (x509KeyManager == null) {
throw new NoSuchAlgorithmException("The default algorithm :" + algorithm + " did not produce a X509 Key manager");
}
return x509KeyManager;
}
private void initialiseManager(int iPort) throws IOException, GeneralSecurityException {
// Next construct and initialise a SSLContext with the KeyStore and
// the TrustStore. We use the default SecureRandom.
// load your key store as a stream and initialize a KeyStore
File trustFile = new File("publicKey");
LogManager.log("Trust File Loaded " + trustFile.getAbsolutePath(), LogManager.LOG_LOWEST_LEVEL);
InputStream trustStream = new FileInputStream(trustFile);
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
// if your store is password protected then declare it (it can be null however)
char[] trustPassword = "password".toCharArray();
// load the stream to your store
trustStore.load(trustStream, trustPassword);
File trustFileTwo = new File("publicKeyTwo");
LogManager.log("Trust File Loaded " + trustFileTwo.getAbsolutePath(), LogManager.LOG_LOWEST_LEVEL);
InputStream trustStreamTwo = new FileInputStream(trustFileTwo);
KeyStore trustStoreTwo = KeyStore.getInstance(KeyStore.getDefaultType());
// if your store is password protected then declare it (it can be null however)
char[] trustPasswordTwo = "password".toCharArray();
// load the stream to your store
trustStoreTwo.load(trustStreamTwo, trustPasswordTwo);
// initialize a trust manager factory with the trusted store
TrustManagerFactory trustFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustFactory.init(trustStore);
trustFactory.init(trustStoreTwo);
// get the trust managers from the factory
TrustManager[] managers = trustFactory.getTrustManagers();
SSLContext context = SSLContext.getInstance("SSL");
context.init(getKeyManagers(), managers, new SecureRandom());
SSLContext.setDefault(context);
SSLServerSocketFactory sslSrvFact = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
serverSocket = sslSrvFact.createServerSocket(iPort);
// this method didn't create a Socket Connection correctly
}
class MultiKeyStoreManager implements X509KeyManager {
private final X509KeyManager jvmKeyManager;
private final X509KeyManager customKeyManager;
public MultiKeyStoreManager(X509KeyManager jvmKeyManager, X509KeyManager customKeyManager) {
this.jvmKeyManager = jvmKeyManager;
this.customKeyManager = customKeyManager;
}
@Override
public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
// try the first key manager
String alias = customKeyManager.chooseClientAlias(keyType, issuers, socket);
if (alias == null) {
alias = jvmKeyManager.chooseClientAlias(keyType, issuers, socket);
LogManager.log("Reverting to JVM CLIENT alias : " + alias, LogManager.LOG_LOWEST_LEVEL);
}
return alias;
}
@Override
public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
// try the first key manager
String alias = customKeyManager.chooseServerAlias(keyType, issuers, socket);
if (alias == null) {
alias = jvmKeyManager.chooseServerAlias(keyType, issuers, socket);
LogManager.log("Reverting to JVM Server alias : " + alias ,LogManager.LOG_LOWEST_LEVEL);
}
return alias;
}
@Override
public String[] getClientAliases(String keyType, Principal[] issuers) {
String[] cAliases = customKeyManager.getClientAliases(keyType, issuers);
String[] jAliases = jvmKeyManager.getClientAliases(keyType, issuers);
LogManager.log("Supported Client Aliases Custom: " + cAliases.length + " JVM : " + jAliases.length,
LogManager.LOG_LOWEST_LEVEL);
return (String[]) ArrayUtils.addAll(cAliases, jAliases);
}
@Override
public PrivateKey getPrivateKey(String alias) {
PrivateKey key = customKeyManager.getPrivateKey(alias);
if (key == null) {
System.out.println("Reverting to JVM Key : " + alias);
return jvmKeyManager.getPrivateKey(alias);
} else {
return key;
}
}
@Override
public String[] getServerAliases(String keyType, Principal[] issuers) {
String[] cAliases = customKeyManager.getServerAliases(keyType, issuers);
String[] jAliases = jvmKeyManager.getServerAliases(keyType, issuers);
LogManager.log("Supported Server Aliases Custom: " + cAliases.length + " JVM : " + jAliases.length,
LogManager.LOG_LOWEST_LEVEL);
return (String[]) ArrayUtils.addAll(cAliases, jAliases);
}
@Override
public java.security.cert.X509Certificate[] getCertificateChain(String string) {
java.security.cert.X509Certificate[] chain = customKeyManager.getCertificateChain("alias_key1");
if (chain == null || chain.length == 0) {
LogManager.log("Reverting to JVM Chain : " + string, LogManager.LOG_LOWEST_LEVEL);
return jvmKeyManager.getCertificateChain("alias_key2");
} else {
return chain;
}
}
}
and this gave me this status
* 2012.02.09 18時47分00秒激活SSL連接
2012.02.09 18時47分00秒[... ::運行]
2012.02.09十八時47分00秒信託文件中加載公鑰
2012.02.09十八時47分00秒信託文件加載publicKeyTwo
2012.02.09十八時47分00秒加載密鑰庫專用密鑰專用密鑰
2012.02.09 18:47:00加載密鑰庫privateKey2 privateKeyTwo
2012.02。09十八點47分00秒的關鍵管理人員的註冊數:1 *
卻沒有任何反應,當我試圖發送郵件服務器...
正在很難找到真正工作在這樣的一個例子案件。
EDIT 2
嗨布魯諾
關於這個問題,Java和SSH,我真的你先進的知識非常感謝您 幫助。所有這些信息都可以幫助我更好地理解這個問題,同時爲我顯示的方式... Thx很多
而且你是對的...我在Java 5上使用Java 6的代碼JRE
但再次下你的食譜,我得到這個代碼:
// Load the key store: change store type if needed
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream fis = new FileInputStream("keyStore1");
char[] keyPass = "passw".toCharArray();
try {
ks.load(fis, keyPass);
} finally {
if (fis != null) {
fis.close();
}
}
// Get the default Key Manager
KeyManagerFactory kmf = KeyManagerFactory.getInstance(
KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, keyPass);
final X509KeyManager origKm = (X509KeyManager) kmf.getKeyManagers()[0];
X509KeyManager km = new X509KeyManager() {
public String chooseServerAlias(String[] keyType, Principal[] issuers, Socket socket) {
String alias;
InetAddress remoteAddress = socket.getInetAddress();
if (remoteAddress.getHostAddress().equalsIgnoreCase("11.111.111.11")) {
alias = "alias1";
LogManager.log("Reverting to JVM CLIENT alias : " + alias, LogManager.LOG_LOWEST_LEVEL);
} else {
alias = "alias2";
LogManager.log("Reverting to JVM CLIENT alias : " + alias, LogManager.LOG_LOWEST_LEVEL);
}
return alias;
}
public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
// try this key manager
String alias = origKm.chooseClientAlias(keyType, issuers, socket);
LogManager.log("Reverting to JVM CLIENT alias : " + alias, LogManager.LOG_LOWEST_LEVEL);
return alias;
}
public String[] getClientAliases(String keyType, Principal[] issues) {
String[] cAliases = origKm.getClientAliases(keyType, issues);
LogManager.log("Supported Client Aliases : " + cAliases.length, LogManager.LOG_LOWEST_LEVEL);
return cAliases;
}
public String[] getServerAliases(String keyType, Principal[] issues) {
String[] sAliases = origKm.getServerAliases(keyType, issues);
LogManager.log("Supported Server Aliases: " + sAliases.length, LogManager.LOG_LOWEST_LEVEL);
return sAliases;
}
public String chooseServerAlias(String keyType, Principal[] issues, Socket socket) {
// try this key manager
String alias = origKm.chooseServerAlias(keyType, issues, socket);
LogManager.log("Reverting to JVM Server alias : " + alias, LogManager.LOG_LOWEST_LEVEL);
return alias;
}
public X509Certificate[] getCertificateChain(String keyType) {
// here I could specify my other keystore, keystore2 how I could do this?
// I'm thinking in the righ way when I implemented this code to get the correct private key?
X509Certificate[] chain = origKm.getCertificateChain("alias1");
if (chain == null || chain.length == 0) {
LogManager.log("Reverting to JVM Chain : " + keyType, LogManager.LOG_LOWEST_LEVEL);
return origKm.getCertificateChain("alias2");
} else {
return chain;
}
}
public PrivateKey getPrivateKey(String alias) {
PrivateKey key = origKm.getPrivateKey(alias);
// here I could get my other keystore according the alias, for example
// keystore2 how I could do this?
LogManager.log("Reverting to JVM Key : " + alias, LogManager.LOG_LOWEST_LEVEL);
return key;
}
};
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(new KeyManager[]{km}, null, null);
SSLServerSocketFactory sslSrvFact = sslContext.getServerSocketFactory();
objServerSocket = sslSrvFact.createServerSocket(iPort);
是正是這一點我需要實現我的目標是什麼?
編輯3
使用這種方法得到了一個客戶端和服務器之間的握手使用第二 密鑰庫與別名2利用公衆的信任,商店2客戶端
...但我還是當我的錯誤試圖用信託商店1在客戶端
...收到一則由[地址=/11.111.111.11] 恢復到JVM服務器別名:別名2 恢復到JVM密鑰:別名2 錯誤retriving: javax.net.ssl.SSLHandshakeException:收到致命警報:certificate_unknown
我的代碼不會改變使用與alias1私人key1的服務器......當客戶端使用帶有證書alias1公衆truststore1 ...
更何況是必要的解決這個做......我認爲這是最終的解決方案...附近再次
THX!
EDIT 4
布魯諾,我按照你的建議改變了getCertificateChain法顯示波紋管
public X509Certificate[] getCertificateChain(String alias) {
X509Certificate[] chain = origKm.getCertificateChain(alias);
return chain;
}
和
public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
String alias;
還我刪除重複的方法,該方法.. 。
而在網癮使用舊的信任存儲的客戶端不驗證主機名
private static final HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
但對於那些需要使用第二信任存儲做到這一點的驗證客戶端,這是一個原因,因爲我需要處理與密鑰對證書...
EDIT5
我想知道我怎麼能實現服務器套接字讓它能夠與證書根據識別和使用 正確的證書正在使用的客戶端,以繼續進行 握手通信服務器。
解釋更好,在服務器側是:
AppServerSideSocket.jar
- 專用密鑰存儲器:privateKeyApp(JKS類型,與密鑰工具生成)
- 公共密鑰庫:publicKeyApp(JKS型,與所有客戶共享)
而且在客戶端...
AppClientSideSocket.jar - 公共密鑰庫:publicKeyApp
的AppServerSideSocket.jar監聽客戶端的請求,並通過客戶端發送一次接收到的過程 信息
AppClientSideSocket.jar使用SSL使用SSL連接服務器publicKeyAppwithout 驗證服務器主機名握手後發送AppServerSideSocket應用程序的信息。
現在我的另一個客戶端應用程序,AppClientSideSocketNEW.jar,這驗證服務器的主機名,以與服務器通信 。在這種情況下,客戶端 的公共證書中使用的CN必須與AppServerSideSocket.jar所在的主機名匹配。
最初的連接被以這種方式配置服務器端:
if (usingSSLConn) {
System.setProperty("javax.net.ssl.keyStore", "privateKeyApp");
System.setProperty("javax.net.ssl.keyStorePassword", "privateKeyAppPassword");
SSLServerSocketFactory sslServerSocketFactory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
ServerSocket serverSocketApp = sslServerSocketFactory.createServerSocket(Port);
} else
serverSocketApp = new ServerSocket(Port);
}
所有的客戶收到了同樣的publicKeyApp,並沒有驗證主機服務器連接,所以不要緊 如果服務器在哪裏服務器套接字應用程序(AppServerSideSocket.jar)安裝在服務器上,主機名爲 badServer1.com,並且privateKeyApp和publicKeyApp中的密鑰的CN設置爲goodServer1.com,因爲所有的 客戶端都不驗證主機名或密鑰的CN屬性。
波紋管表現出了一塊這種連接
private static final HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
System.setProperty("javax.net.ssl.trustStore", publicKey1);
System.getProperties().setProperty("java.protocol.handler.pkgs", "javax.net.ssl.internal.www.protocol");
HttpsURLConnection.setDefaultHostnameVerifier(DO_NOT_VERIFY);
...
SOAPConnectionFactory soapConn = SOAPConnectionFactory.newInstance();
SOAPConnection connection = soapConn.createConnection();
...
URL endpoint = new URL(hostname + ":" + port);
,但新的客戶端(AppClientSideSocketNEW的。jar)強制執行此驗證,現在有必要爲此客戶端提供一個新的證書 ,其CN屬性的新值反映服務器套接字所在的正確主機名CN。
我沒有訪問第二個客戶端,我確信它進行主機名驗證。
所以我創建了兩個新的密鑰對證書(privateKeyAppNew和publicKeyAppNew),顯然通信發生 使用使用這種新的公共publicKeyAppNew鍵這個新的密鑰對和新的客戶端服務器之間成功進行配置後,服務器使用這個新的密鑰對當然。
但我需要繼續爲舊客戶端使用舊的密鑰對。我想知道我該如何處理這個問題。
使用keymanager讓我能夠在客戶端嘗試連接時驗證服務器應用程序上的客戶端證書,並選擇合適的並使用正確的證書執行握手?
或者我需要在不同的端口有不同的ssl套接字連接爲哪種客戶端?
你是說你想要根據客戶端連接提供不同的證書嗎?你也嘗試使用客戶端證書嗎? – Bruno 2012-02-09 19:38:53
[在SSL Socket Factory連接中使用多個密鑰對]的可能重複(http://stackoverflow.com/questions/9179717/using-more-than-one-key-pair-in-ssl-socket-factory - 連接) – EJP 2012-02-10 00:42:00
我認爲你真的需要改善你的問題。從一開始就很混亂,但它並沒有變得更好。請指定您的錯誤消息,並在哪一方獲得他們(客戶端或服務器)。不要嘗試額外的東西,這些東西必然會導致額外的無關問題(例如,「我在Java 5 JRE上使用Java 6的代碼」)。也許使用更有意義的日誌消息(你的「回覆到...」消息並不意味着它們是什麼,因爲你沒有「回覆」任何東西),請記錄'remoteAddress.getHostAddress()'以確保這是你所期望的,以及它需要的分支。 – Bruno 2012-02-13 21:43:30