为了序言,我没有一个好的(读:任何)安全背景,我认为缺乏理解可能是这个问题的根源。试图了解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