2013-08-05 25 views

Odpowiedz

5

Kolejny przykład, zbudowany na bazie odpowiedzi uwierzytelniającej tylko na serwerze: TLS z samopodpisanymi certyfikatami z uwierzytelnianiem klienta (pokazuję tylko zmienione części). Jest to część serwera:

tlsServerProtocol.accept(new DefaultTlsServer() { 
    protected TlsSignerCredentials getRSASignerCredentials() throws IOException { 
     return tlsSignerCredentials(context); 
    } 
    public void notifyClientCertificate(Certificate clientCertificate) throws IOException { 
     validateCertificate(clientCertificate); 
    } 
    public CertificateRequest getCertificateRequest() { 
     return new CertificateRequest(new short[] { ClientCertificateType.rsa_sign }, new Vector<Object>()); 
    } 
});   

I to jest część klient:

tlsClientProtocol.connect(new DefaultTlsClient() {    
    public TlsAuthentication getAuthentication() throws IOException { 
     return new TlsAuthentication() {      
      public void notifyServerCertificate(Certificate serverCertificate) throws IOException { 
       validateCertificate(serverCertificate); 
      } 
      public TlsCredentials getClientCredentials(CertificateRequest certificateRequest) throws IOException { 
       return tlsSignerCredentials(context); 
      } 
     }; 
    } 
}); 
10

Jest to bardzo prosty przykład, z uwierzytelniania na serwerze tylko i podpisem własnym cert. Kod jest oparty na BC 1.49, głównie leightweight API:

ServerSocket serverSocket = new ServerSocket(SERVER_PORT); 
final KeyPair keyPair = ... 
final Certificate bcCert = new Certificate(new org.spongycastle.asn1.x509.Certificate[] { 
    new X509V3CertificateStrategy().selfSignedCertificateHolder(keyPair).toASN1Structure()}); 
while (true) { 
    Socket socket = serverSocket.accept(); 
    TlsServerProtocol tlsServerProtocol = new TlsServerProtocol(
    socket.getInputStream(), socket.getOutputStream(), secureRandom); 
    tlsServerProtocol.accept(new DefaultTlsServer() { 
     protected TlsSignerCredentials getRSASignerCredentials() throws IOException { 
      return tlsSignerCredentials(context); 
     }    
    });  
    new PrintStream(tlsServerProtocol.getOutputStream()).println("Hello TLS"); 
} 

gdzie

private TlsSignerCredentials tlsSignerCredentials(TlsContext context) throws IOException { 
    return new DefaultTlsSignerCredentials(context, bcCert, 
      PrivateKeyFactory.createKey(keyPair.getPrivate().getEncoded()));     
} 

Jest to kod klienta:

Socket socket = new Socket(<server IP>, SERVER_PORT); 
TlsClientProtocol tlsClientProtocol = new TlsClientProtocol( 
    socket.getInputStream(), socket.getOutputStream()); 
tlsClientProtocol.connect(new DefaultTlsClient() {   
    public TlsAuthentication getAuthentication() throws IOException { 
     return new ServerOnlyTlsAuthentication() {     
      public void notifyServerCertificate(Certificate serverCertificate) throws IOException { 
       validateCertificate(serverCertificate); 
      } 
     }; 
    } 
}); 
String message = new BufferedReader(
    new InputStreamReader(tlsClientProtocol.getInputStream())).readLine(); 

Trzeba użyć strumień wejściowy i wyjściowy z tlsClient/ServerProtocol do odczytywania i zapisywania zaszyfrowanych danych (np. tlsClientProtocol.getInputStream()). W przeciwnym razie, jeśli użyłeś np. socket.getOutputStream(), wystarczy napisać niezaszyfrowane dane.

Jak zaimplementować validateCertificate? Używam certyfikatów z podpisem własnym. Oznacza to, że po prostu sprawdzam je w kluczowym sklepie bez żadnych łańcuchów certyfikatów. W ten sposób utworzyć magazyn kluczy:

KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); 
keyStore.load(null, password); 
X509Certificate certificate = ...; 
keyStore.setCertificateEntry(alias, certificate); 

I to jest walidacja:

private void validateCertificate(org.spongycastle.crypto.tls.Certificate cert) throws IOException, CertificateException, KeyStoreException { 
    byte[] encoded = cert.getCertificateList()[0].getEncoded(); 
    java.security.cert.Certificate jsCert = 
     CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(encoded)); 
    String alias = keyStore.getCertificateAlias(jsCert); 
    if(alias == null) { 
     throw new IllegalArgumentException("Unknown cert " + jsCert); 
    } 
} 

Co jest mylące, to trzy różne klasy certyfikatów. Musisz dokonać konwersji między nimi, jak pokazano powyżej.

6

Scenariusz: nasz serwer produkcyjny korzysta z JDK1.6. Jednak serwer klienta jest aktualizowany do komunikowania się tylko w TLS 1.2. SSL Komunikacja między obydwoma serwerami jest zepsuta. Ale nie możemy po prostu zaktualizować JDK6 do 8 (co domyślnie obsługuje TLS 1.2), ponieważ spowoduje to problemy ze zgodnością innych bibliotek.

Poniższy przykładowy kod wykorzystuje jdk1.6.0_45 i bcprov-jdk15on-153.jar (BIONCY Castle SIGNED JAR FILES) do połączenia z dowolnym serwerem za pomocą TLS.

import java.io.IOException; 
import java.io.BufferedReader; 
import java.io.InputStreamReader; 
import java.net.Socket; 

import org.bouncycastle.crypto.tls.CertificateRequest; 
import org.bouncycastle.crypto.tls.DefaultTlsClient; 
import org.bouncycastle.crypto.tls.TlsAuthentication; 
import org.bouncycastle.crypto.tls.TlsClientProtocol; 
import org.bouncycastle.crypto.tls.TlsCredentials; 

public class TestHttpClient { 
    // Reference: http://boredwookie.net/index.php/blog/how-to-use-bouncy-castle-lightweight-api-s-tlsclient/ 
    //   bcprov-jdk15on-153.tar\src\org\bouncycastle\crypto\tls\test\TlsClientTest.java 
    public static void main(String[] args) throws Exception { 
     java.security.SecureRandom secureRandom = new java.security.SecureRandom(); 
     Socket socket = new Socket(java.net.InetAddress.getByName("www.google.com"), 443); 
     TlsClientProtocol protocol = new TlsClientProtocol(socket.getInputStream(), socket.getOutputStream(),secureRandom); 
     DefaultTlsClient client = new DefaultTlsClient() { 
      public TlsAuthentication getAuthentication() throws IOException { 
       TlsAuthentication auth = new TlsAuthentication() { 
        // Capture the server certificate information! 
        public void notifyServerCertificate(org.bouncycastle.crypto.tls.Certificate serverCertificate) throws IOException { 
        } 

        public TlsCredentials getClientCredentials(CertificateRequest certificateRequest) throws IOException { 
         return null; 
        } 
       }; 
       return auth; 
      } 
     }; 
     protocol.connect(client); 

     java.io.OutputStream output = protocol.getOutputStream(); 
     output.write("GET/HTTP/1.1\r\n".getBytes("UTF-8")); 
     output.write("Host: www.google.com\r\n".getBytes("UTF-8")); 
     output.write("Connection: close\r\n".getBytes("UTF-8")); // So the server will close socket immediately. 
     output.write("\r\n".getBytes("UTF-8")); // HTTP1.1 requirement: last line must be empty line. 
     output.flush(); 

     java.io.InputStream input = protocol.getInputStream(); 
     BufferedReader reader = new BufferedReader(new InputStreamReader(input)); 
     String line; 
     while ((line = reader.readLine()) != null) 
     { 
      System.out.println(line); 
     } 
    } 
} 

Przykładowy wynik pokazuje, że JDK 6 można uzyskać na stronie serwera w TLS, a nie jakiś wyjątek SSL:

HTTP/1.1 302 Found 
Cache-Control: private 
Content-Type: text/html; charset=UTF-8 
Location: https://www.google.com.sg/?gfe_rd=cr&ei=WRgeVovGEOTH8Afcx4XYAw 
Content-Length: 263 
Date: Wed, 14 Oct 2015 08:54:49 GMT 
Server: GFE/2.0 
Alternate-Protocol: 443:quic,p=1 
Alt-Svc: quic="www.google.com:443"; p="1"; ma=600,quic=":443"; p="1"; ma=600 
Connection: close 

<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8"> 
<TITLE>302 Moved</TITLE></HEAD><BODY> 
<H1>302 Moved</H1> 
The document has moved 
<A HREF="https://www.google.com.sg/?gfe_rd=cr&amp;ei=WRgeVovGEOTH8Afcx4XYAw">here</A>. 
</BODY></HTML> 
+0

siedzę z Java 5 i próbuje dostać tę pracę przez BouncyCastle ale kodzie daje wyjątek dla innego adresu URL: "Wewnętrzny błąd TLS, może to być atak", jakikolwiek pomysł, jak go rozwiązać? – Saky