2014-03-28 41 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.