Saturday, March 30, 2013

Using ssdata.p12 to retrieve our white-box key

Now that we have found out where ccdata was used, let's find out where ssdata.p12 was used.

ssdata.p12 usage

As we saw earlier, the Transcoder "didReceiveAuthenticationChallenge" delegate method was what used the ssdata, but what is the NSUrl that was called that returned this response?

I suspect it is the request made in "fetchWhiteBoxKey:withPort" which accesses "https:/{TIVO_STREAM_IP}:49151/live-streaming/XX_DUMMY_TSN_XX/key.scbin?uuid={UNIQUE_IDENTIFIER}".

Knowing that I could not successfully hit this URL before, I am almost certain that the issue was that I did not have the ssdata.p12 client certificate, so the Tivo Stream was ignoring me.

Converting ssdata.p12 into Java keystore

keytool -importkeystore -srckeystore ssdata.p12 -srcstoretype pkcs12 -srcstorepass WiReKYd6bEDu -destkeystore ssdata.jks -deststoretype jks -deststorepass "changeit"

Writing the program to retrieve our white box key

Let me write a quick application to test this theory:

import java.net.MalformedURLException;
import java.net.URL;
import java.security.cert.Certificate;
import java.io.*;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLPeerUnverifiedException;

import java.security.KeyStore;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.KeyManager;
import javax.net.ssl.TrustManager;
import java.security.SecureRandom;
import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException;

import java.util.Enumeration;
public class TestHTTP{
   public static void main(String[] args) throws Exception
   {
        new TestHTTP().testIt();
   }
   private void testIt() throws Exception {
      String https_url = "https://10.0.0.109:49151/live-streaming/XX_DUMMY_TSN_XX/key.scbin?uuid=001122334455667788";
      URL url;
      try {
 
KeyStore ks = this.getKeyStore();
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, "WiReKYd6bEDu".toCharArray());
KeyManager[] kms = kmf.getKeyManagers();
// Custom Trust manager that lets anything in!
X509TrustManager easyTrustManager = new X509TrustManager() {

public void checkClientTrusted(
X509Certificate[] chain,
String authType) throws CertificateException {
// Oh, I am easy!
}

public void checkServerTrusted(
X509Certificate[] chain,
String authType) throws CertificateException {
// Oh, I am easy!
}

public X509Certificate[] getAcceptedIssuers() {
return null;
}

};
// Bypass validation of authorities, this is what the iphone app does
TrustManager[] tms = new TrustManager[] {easyTrustManager};

        SSLContext sslContext = null;
        sslContext = SSLContext.getInstance("TLS");
        sslContext.init(kms, tms, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
// Since the alias is "1" it will not match " 10.0.0.109" unless we override the hostname verifier
HttpsURLConnection.setDefaultHostnameVerifier(
new javax.net.ssl.HostnameVerifier(){
 
public boolean verify(String hostname, javax.net.ssl.SSLSession sslSession) {

System.out.println("Warning: URL Host: " + hostname + " vs. " + sslSession.getPeerHost());
return true;
}
}
);
        
    url = new URL(https_url);
    HttpsURLConnection con = (HttpsURLConnection)url.openConnection();
 
    //dumpl all cert info
    print_https_cert(con);
    //dump all the content
    print_content(con);
      } catch (MalformedURLException e) {
    e.printStackTrace();
      } catch (IOException e) {
    e.printStackTrace();
      }
   }
   
   private KeyStore getKeyStore() throws Exception {

KeyStore ks = KeyStore.getInstance("JKS");

    java.io.FileInputStream fis = new java.io.FileInputStream("ssdata.jks");
    ks.load(fis, "changeit".toCharArray());
    fis.close();
return ks;
   }
   private void print_https_cert(HttpsURLConnection con){
    if(con!=null){
      try {
System.out.println("Response Code : " + con.getResponseCode());
System.out.println("Cipher Suite : " + con.getCipherSuite());
System.out.println("\n");
Certificate[] certs = con.getServerCertificates();
for(Certificate cert : certs){
  System.out.println("Cert Type : " + cert.getType());
  System.out.println("Cert Hash Code : " + cert.hashCode());
  System.out.println("Cert Public Key Algorithm : " + cert.getPublicKey().getAlgorithm());
  System.out.println("Cert Public Key Format : " + cert.getPublicKey().getFormat());
  System.out.println("\n");
}
} catch (SSLPeerUnverifiedException e) {
e.printStackTrace();
} catch (IOException e){
e.printStackTrace();
}
     }
   }
   private void print_content(HttpsURLConnection con){
if(con!=null){
try {
InputStream inputStream  = con.getInputStream();
OutputStream out = new FileOutputStream(new File("whiteBox.key"));
int read = 0;
byte[] bytes = new byte[1024];
 
while ((read = inputStream.read(bytes)) != -1) {
out.write(bytes, 0, read);
}
 
inputStream.close();
out.flush();
out.close();
 
} catch (IOException e) {
  e.printStackTrace();
}
   
}

}

Running the program

D:\Projects\TivoStream\Program\RetrieveWhiteBoxKey>java TestHTTP
Warning: URL Host: 10.0.0.109 vs. 10.0.0.109
Response Code : 200
Cipher Suite : SSL_RSA_WITH_RC4_128_SHA

Cert Type : X.509
Cert Hash Code : 1681350
Cert Public Key Algorithm : RSA
Cert Public Key Format : X.509

It worked! The TiVo stream stopped ignoring us and sent us back a 200 code and populated our "whiteBox.key" file with 149 bytes.

But what is in this file??




No comments:

Post a Comment