2016-01-21 70 views
2

編輯:我修復了代碼。請參閱我的解答。帶有在運行時導入的自簽名證書的SSL客戶端服務器在握手時失敗

我想創建一個android應用程序,使用SSLSocket將數據發送到服務器。證書是自簽名的,我試圖使用SSLContext將其添加到服務器KeyManager和客戶端TrustManager中,按照我在其他線程上找到的說明進行操作。

但是,當我嘗試發送數據時,客戶端和服務器都會返回異常。

請記住,這是我第一次處理SSL連接。我使用Java keytool創建並創建了'server.crt'證書。

客戶例外

javax.net.ssl.SSLHandshakeException: Handshake failed 
at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:390) 
at com.android.org.conscrypt.OpenSSLSocketImpl.waitForHandshake(OpenSSLSocketImpl.java:623) 
at com.android.org.conscrypt.OpenSSLSocketImpl.getOutputStream(OpenSSLSocketImpl.java:609) 
... 
Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x73c6ac00: Failure in SSL library, usually a protocol error 

服務器異常

Exception in thread "main" javax.net.ssl.SSLHandshakeException: no cipher suites in common 
... 
at java.io.InputStream.read(InputStream.java:101) 

固定的客戶端代碼(運行在Android上)

AssetManager assetManager = getAssets(); 
InputStream keyStoreInputStream = assetManager.open("keystore.bks"); 

KeyStore keyStore = KeyStore.getInstance("BKS"); 
keyStore.load(null,null); 
keyStore.load(keyStoreInputStream, KEY_PASSWORD); 

TrustManagerFactory trustManagerFactory = 
    TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 
trustManagerFactory.init(keyStore); 

SSLContext sslContext = SSLContext.getInstance("TLS"); 
sslContext.init(null, trustManagerFactory.getTrustManagers(), null); 

SSLSocketFactory sslsocketfactory = sslContext.getSocketFactory(); 
SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket("192.168.1.212", 4444); 

InputStream inputStream = assetManager.open(MainActivity.FILE_NAME); 

OutputStream outputStream = sslsocket.getOutputStream(); 

int count; 
byte[] buffer = new byte[12 * 1024]; 
while ((count = inputStream.read(buffer)) > 0) { 
    outputStream.write(buffer, 0, count); 
} 

固定服務器代碼(在Windows上運行)

FileInputStream keyStoreInputStream = new FileInputStream(KEYSTORE_PATH); 

KeyStore keyStore = KeyStore.getInstance("JKS"); 
keyStore.load(null, null); 
keyStore.load(keyStoreInputStream, KEY_PASSWORD); 

KeyManagerFactory keyManagerFactory = 
    KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 
keyManagerFactory.init(keyStore, KEY_PASSWORD); 

SSLContext sslContext = SSLContext.getInstance("TLS"); 
sslContext.init(keyManagerFactory.getKeyManagers(), null, null); 

SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory(); 
SSLServerSocket sslServerSocket = 
    (SSLServerSocket) sslServerSocketFactory.createServerSocket(4444); 

while (true) { 
    SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept(); 

    InputStream inputStream = sslSocket.getInputStream(); 

    FileOutputStream outputStream = new FileOutputStream(OUTPUT_PATH,false); 

    byte[] bytes = new byte[12 * 1024]; 
    int count; 
    while ((count = inputStream.read(bytes)) > 0) { 
     outputStream.write(bytes, 0, count); 
    } 
} 

回答

1

看來問題出在我使用的證書上。 我試圖導入用keytool創建的'* .crt'文件,然後以編程方式將其添加到密鑰庫。 儘管我仍然不確定爲什麼它沒有工作,我通過跳過證書導入並直接導入密鑰庫(在Windows服務器上'keystore.jks'和在Android客戶端上'keystore.bks')來解決這個問題。

這是我創建的keystore.jks:

keytool -genkeypair -alias serveralias -keyalg RSA -keysize 4096 -keystore keystore.jks 
     -keypass changeit -storepass changeit -validity 10950 

由於JKS不支持在Android上我做了密鑰庫的副本,並改名爲擴展.bks上新的。 然後,我使用一個名爲'KeyStore Explorer'的程序將keystore.bks轉換爲'BKS-V1'格式,以便它可以在android上運行。

我更新了原始問題以包含工作代碼。

0

我認爲關鍵信息是服務器端異常:Exception in thread "main" javax.net.ssl.SSLHandshakeException: no cipher suites in common。您的自簽證書可能沒有問題;看起來您在密碼配置中存在不匹配。您需要確保客戶端和服務器都有共同的密碼套件。 SSLSocket in Android的默認支持套件取決於版本。請參閱release notes for Android 5.0

+0

我已經檢查getEnabledCipherSuites()在客戶端和服務器上,並確認確實有一些常見的密碼套件。我也嘗試setEnabledCipherSuites()並選擇了客戶端和服務器都有共同的密碼套件。我也有同樣的例外。 – HomeIsWhereThePcIs

0

出於測試目的,我需要創建一個HTTPS服務器。由於證書並不重要,我需要創建一個。當我們可以用java完成所有工作時,爲什麼要離開JVM並使用keytool。

這需要BouncyCastle。

static SSLContext createSSLContext() throws Exception { 
    final KeyStore ks = createSelfSignedKeyStore(); 
    final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 
    kmf.init(ks, new char[0]); 

    final TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 
    tmf.init(ks); 

    final SSLContext sslContext = SSLContext.getInstance("TLS"); 
    sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 
    return sslContext; 
} 

private static KeyStore createSelfSignedKeyStore() throws Exception { 
    final KeyStore ks = KeyStore.getInstance("JKS"); 
    ks.load(null); 

    final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); 
    keyGen.initialize(1024); 
    final KeyPair keyPair = keyGen.generateKeyPair(); 
    final Certificate selfSignedCert = generate(InetAddress.getLocalHost().getCanonicalHostName(), keyPair); 

    ks.setCertificateEntry("alias.cert", selfSignedCert); 
    ks.setKeyEntry("alias.key", keyPair.getPrivate(), new char[0], new Certificate[] { selfSignedCert }); 
    return ks; 
} 

private static Certificate generate(final String fqdn, final KeyPair keypair) throws Exception { 
    final Instant now = Instant.now(); 
    final Date notBefore = Date.from(now.minus(24, ChronoUnit.HOURS)); 
    final Date notAfter = Date.from(now.plus(24, ChronoUnit.HOURS)); 
    final ContentSigner selfSigner = new JcaContentSignerBuilder("SHA256WithRSAEncryption") 
     .build(keypair.getPrivate()); 
    final X500Name owner = new X500Name("CN=" + fqdn); 
    final X509CertificateHolder certHolder = new JcaX509v3CertificateBuilder(
     /*issuer*/owner, 
     /*serial*/new BigInteger(64, new SecureRandom()), 
     /*cert start*/notBefore, 
     /*cert end*/notAfter, 
     /*subject*/owner, 
     /*subject public key*/keypair.getPublic()) 
     .build(selfSigner); 
    final X509Certificate selfSignedCert = new JcaX509CertificateConverter() 
     .getCertificate(certHolder); 
    selfSignedCert.verify(keypair.getPublic()); // Certificate is good! 
    return selfSignedCert; 
} 
相關問題