试图了解Java中的SSL证书

时间:2014-03-28 02:14:59

标签: java ssl rtmp

前言,我在安全方面没有良好的(阅读:任何)背景,我认为缺乏理解可能是这个问题的根源。

我正在使用来自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文件会被调用吗?我觉得那会是一个坏主意。

很抱歉提出这么模糊的问题,过去几天我一直在思考这段代码,并且对现有资源(javadocs等)运气不佳。谢谢!

2 个答案:

答案 0 :(得分:2)

SSLSocket对象可以访问它应该信任的证书,因为它是由间接由具有本地可信证书的KeyStore对象间接初始化的SSLContext创建的。在SSLSocket对象上调用connect()时,它会从服务器检索证书,然后可以将其与KeyStore对象中的证书进行比较,以确定证书链是否合法。所有这些都发生在幕后,所以作为应用程序员,你不必担心代码中的证书。这就是在正常情况下不调用示例代码的getCertificate()方法的原因。

但是,当身份验证失败时,示例代码会在其getCertificate()方法中获取服务器的证书并将其安装在本地密钥库中。下一次连接到服务器的尝试通常会成功,因为服务器将发送的证书的新副本将与刚刚安装在密钥库中的副本匹配。

当然,这会破坏整个身份验证方案,因为最终这个示例代码信任服务器发送的任何证书而不验证它。很可能这段代码的作者并不关心身份验证,只是想使用SSL连接,即使它是伪造的服务器。这当然是大多数人在浏览具有无法识别的证书的网站时所做的事情,只是接受证书。

代码的不好之处在于它将未经验证的证书安装在本地计算机的主JRE密钥库中。这意味着在机器上运行的所有其他Java应用程序将从此信任未经验证的证书,这是一个主要的安全漏洞。您可能希望避免在自己的代码中执行此操作。

答案 1 :(得分: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.