爲了序言,我沒有一個好的(讀:任何)安全背景,我認爲缺乏理解可能是這個問題的根源。試圖瞭解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文檔等)。謝謝!
SO不是一個模糊問題的好地方,當然也不是我們對話時問你的問題「來衡量你的理解」。你基本上是要求一對一的教程,雖然這裏有人可以這樣做,但他們不會通過SO來完成。我建議您退出代碼,思考(或閱讀)關於SSL的總體內容以及代碼所需的內容,然後嘗試在相關API文檔的幫助下解釋上述內容。然後形成一個具體的問題,比如「他們爲什麼做A而不是B?」。 – arcy
我會盡量減少。 – Kyle