2014-03-28 67 views
1

爲了序言,我沒有一個好的(讀:任何)安全背景,我認爲缺乏理解可能是這個問題的根源。試圖瞭解Java中的SSL證書

我玩弄一些代碼here處理建立RTMPS連接。基本上,我希望瞭解它是如何工作的,儘管我沒有任何關於RTMP協議的問題,但我無法理解SSL代碼是如何編寫的。它似乎會從服務器獲取證書,然後將其存儲在cacerts文件或仙文件夾中。

我一直努力遵循的連接過程,看看那裏的證書檢索和存儲,但一直沒能弄明白。以下是一段代碼,據我所知,其行爲是正確的:

注意:SavingTrustManager只是X509TrustManager的一個包裝,它也存儲證書鏈。

/** 
* Opens the socket with the default or a previously saved certificate 
* 
* @return A special TrustManager to save the certificate if necessary 
* @throws IOException 
*/ 
private SavingTrustManager openSocketWithCert() throws IOException { 
try { 
    // Load the default KeyStore or a saved one 
    KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); 
    File file = new File("certs/" + server + ".cert"); 
    if (!file.exists() || !file.isFile()) 
     file = new File(System.getProperty("java.home") + "/lib/security/cacerts"); 

    InputStream in = new FileInputStream(file); 
    ks.load(in, passphrase); 

    // Set up the socket factory with the KeyStore 
    SSLContext context = SSLContext.getInstance("TLS"); 
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 
    tmf.init(ks); 
    X509TrustManager defaultTrustManager = (X509TrustManager)tmf.getTrustManagers()[0]; 
    SavingTrustManager tm = new SavingTrustManager(defaultTrustManager); 
    context.init(null, new TrustManager[] { tm }, null); 
    SSLSocketFactory factory = context.getSocketFactory(); 

    sslsocket = (SSLSocket)factory.createSocket(server, port); 

    return tm; 
} 
catch (Exception e) { 
    // Hitting an exception here is very bad since we probably won't 
    // recover 
    // (unless it's a connectivity issue) 

    // Rethrow as an IOException 
    throw new IOException(e.getMessage()); 
} 
} 

/** 
* Downloads and installs a certificate if necessary 
* 
* @throws IOException 
*/ 
private void getCertificate() throws IOException { 
try { 
    SavingTrustManager tm = openSocketWithCert(); 

    // Try to handshake the socket 
    boolean success = false; 
    try { 
     sslsocket.startHandshake(); 
     success = true; 
    } 
    catch (SSLException e) { 
     sslsocket.close(); 
    } 

    // If we failed to handshake, save the certificate we got and try 
    // again 
    if (!success) { 
     // Set up the directory if needed 
     File dir = new File("certs"); 
     if (!dir.isDirectory()) { 
      dir.delete(); 
      dir.mkdir(); 
     } 

     // Reload (default) KeyStore 
     KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); 
     File file = new File(System.getProperty("java.home") + "/lib/security/cacerts"); 

     InputStream in = new FileInputStream(file); 
     ks.load(in, passphrase); 

     // Add certificate 
     X509Certificate[] chain = tm.chain; 
     if (chain == null) 
      throw new Exception("Failed to obtain server certificate chain"); 

     X509Certificate cert = chain[0]; 
     String alias = server + "-1"; 
     ks.setCertificateEntry(alias, cert); 

     // Save certificate 
     OutputStream out = new FileOutputStream("certs/" + server + ".cert"); 
     ks.store(out, passphrase); 
     out.close(); 
     System.out.println("Installed cert for " + server); 
    } 
} 
catch (Exception e) { 
    // Hitting an exception here is very bad since we probably won't 
    // recover 
    // (unless it's a connectivity issue) 

    // Rethrow as an IOException 
    e.printStackTrace(); 
    throw new IOException(e.getMessage()); 
} 
} 

/** 
* Attempts to connect given the previous connection information 
* 
* @throws IOException 
*/ 
public void connect() throws IOException { 
try { 
    sslsocket = (SSLSocket)SSLSocketFactory.getDefault().createSocket(server, port); 
    in = new BufferedInputStream(sslsocket.getInputStream()); 
    out = new DataOutputStream(sslsocket.getOutputStream()); 

    doHandshake(); 
} 
catch (IOException e) { 
    // If we failed to set up the socket, assume it's because we needed 
    // a certificate 
    getCertificate(); 
    // And use the certificate 
    openSocketWithCert(); 

    // And try to handshake again 
    in = new BufferedInputStream(sslsocket.getInputStream()); 
    out = new DataOutputStream(sslsocket.getOutputStream()); 

    doHandshake(); 
} 

// Start reading responses 
pr = new RTMPPacketReader(in); 

// Handle preconnect Messages? 
// -- 02 | 00 00 00 | 00 00 05 | 06 00 00 00 00 | 00 03 D0 90 02 

// Connect 
Map<String, Object> params = new HashMap<String, Object>(); 
params.put("app", app); 
params.put("flashVer", "WIN 10,1,85,3"); 
params.put("swfUrl", swfUrl); 
params.put("tcUrl", "rtmps://" + server + ":" + port); 
params.put("fpad", false); 
params.put("capabilities", 239); 
params.put("audioCodecs", 3191); 
params.put("videoCodecs", 252); 
params.put("videoFunction", 1); 
params.put("pageUrl", pageUrl); 
params.put("objectEncoding", 3); 

byte[] connect = aec.encodeConnect(params); 

out.write(connect, 0, connect.length); 
out.flush(); 

while (!results.containsKey(1)) 
    sleep(10); 
TypedObject result = results.get(1); 
DSId = result.getTO("data").getString("id"); 

connected = true; 
} 

我想我不能關注如何收集服務器的證書。從我的角度來看,似乎getCertificate()方法從未被調用過。這是因爲證書已存儲在某個地方嗎?沒有certs目錄,所以我會假設它在重音文件中。如果我清除了我的cacerts文件,它會被調用嗎?我覺得這是一個壞主意。

對不起,問這樣一個模糊的問題,我剛剛被琢磨過去幾天這個代碼,並沒有與現有的資源多少運氣(javadoc文檔等)。謝謝!

+1

SO不是一個模糊問題的好地方,當然也不是我們對話時問你的問題「來衡量你的理解」。你基本上是要求一對一的教程,雖然這裏有人可以這樣做,但他們不會通過SO來完成。我建議您退出代碼,思考(或閱讀)關於SSL的總體內容以及代碼所需的內容,然後嘗試在相關API文檔的幫助下解釋上述內容。然後形成一個具體的問題,比如「他們爲什麼做A而不是B?」。 – arcy

+0

我會盡量減少。 – Kyle

回答

2

的SSLSocket的對象訪問,因爲它被創建,間接,由已被初始化,間接,由具有本地可信證書密鑰庫對象中的SSLContext它應該信任的證書。當在SSLSocket對象上調用connect()時,它會從服務器檢索證書,然後可以將其與KeyStore對象中的證書進行比較,以確定證書鏈是否合法。所有這些都是在封面下發生的,所以作爲應用程序員,您從不需要擔心代碼中的證書。這就是爲什麼在正常情況下不會調用示例代碼的getCertificate()方法。

然而,當認證失敗時,例如代碼,然後,在其getCertificate()方法,取得服務器的證書並將其安裝在本地密鑰庫中。下一次嘗試連接到服務器通常會成功,因爲服務器將發送的證書的新副本將與剛剛安裝在密鑰庫中的副本相匹配。

當然,這違背了整個認證方案,因爲最終這個示例代碼信任任何證書的服務器未經覈實就發送。這個代碼的作者很可能不關心認證,只是想使用SSL連接,即使它是一個虛假的服務器。這當然是大多數人在瀏覽到具有無法識別的證書的網站時所做的事情,並且只接受證書。

代碼的壞處在於它將未經驗證的證書安裝在本地計算機的主JRE密鑰庫中。這意味着在機器上運行的所有其他Java應用程序都將相信未經驗證的證書,這是一個重大的安全漏洞。您可能希望避免在自己的代碼中執行此操作。

+0

好的!我認爲這爲我清除了很多。如果他們創建了一個簡單地接受任何連接的TrustManager,它會更好/相等嗎? – Kyle

+0

如果「他們」是指編寫示例代碼的人,如果他們遇到不受信任的服務器證書,而不是以編程方式信任它,則至少需要用戶干預纔會是最好的。但是,創建和使用接受所有證書的TrustManager,同時將它們暴露給中間人攻擊的人,至少不會將其他應用程序暴露給這些攻擊。 –

+0

這就是我的意思。感謝您幫助澄清一些事情。 – Kyle

0
Below key points you can google and understand. 

1. Trusted certificates and keystores/truststores 
2. Concept of private and public key. How browser send the certificate key to server and it gets authenticated. 
3. Keytool utility, to convert certificates from one form to another. 
4. Importing the trusted certificates and keys to trustore, though java has its "cacerts" file by default, but you can create you own and import certificates into it. 
5. Explore certificates, read about thumbprint, algorithms used 
6. Create you own custom factory as well.