Публични и частни ключове

Чувствителна операция, области на сигурност
black-and-white и  shades-of-gray политики.
Java 1 - аплети и апликации, sandbox
Java 2 - потребител - откъде се зарежда и цифров подпис (кой го е правил и че не е променен)
мениджър на сигурноста - общи политики и политики към конкретен клас
Архитектурата за сигурност в SDK е зададена в файл за конфигуриране на параметрите file://$java.home}/lib/security/java.security
променянна чрез конфигурационен параметър java.security.properties 

Основната цел на цифровото подписване на код е идентифицирането на доставчика на класовете и е реализирано като обща услуга (service) в езика.
Криптографската архитектура в Java -доставчици на криптографски услуги (пакет от класове или множество пакети, които осигуряват конкретна реализация на подмножество от криптографски функции на езика Java)

Стандартни средства - частни и публични ключове, криптиране декриптиране и подписване
сертификат задължително включва следната информация:
•    публичен ключ на субекта на сертификата (не шифриран);
•    идентификация (име, принадлежност ...) на субекта – не шифрирана информация;
•    цифров подпис на тези данни от издателя на сертификата.
За да може този сертификат да бъде валиден цифровият му подпис трябва да бъде издаден от удостоверяващ център (Certification authority, CA), чийто публичен ключ е достатъчно известен и разпространен за да може да му се има доверие например VeriSign (http://www.verisign.com)




Генериране на двойка публичен-частен ключ
keytool -genkey -alias mykey -keypass privatePass -keystore keystore.bin -storepass publicPass

What is your first and last name?
  [Unknown]: Ivan Momtchev
What is the name of your organizational unit?
  [Unknown]:  prof
What is the name of your organization?
  [Unknown]:  Tu-Sofia
What is the name of your City or Locality?
  [Unknown]:  Sofia
What is the name of your State or Province?
  [Unknown]:  Sofia
What is the two-letter country code for this unit?
  [Unknown]:  BG
Is CN=Ivan Momtchev, OU=prof, O=Tu-Sofia, L=Sofia, ST=Sofia, C=BG correct?
  [no]:  y


Съдържанието на хранилището може да се разгледа с
keytool -list -v -keystore keystore.bin -storepass publicPass

Keystore type: JKS
Keystore provider: SUN

Your keystore contains 1 entry

Alias name: mykey
Creation date: 2012-2-7
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Ivan Momtchev, OU=prof, O=Tu-Sofia, L=Sofia, ST=Sofia, C=BG
Issuer: CN=Ivan Momtchev, OU=prof, O=Tu-Sofia, L=Sofia, ST=Sofia, C=BG
Serial number: 4f316d6f
Valid from: Tue Feb 07 20:29:03 EET 2012 until: Mon May 07 21:29:03 EEST 2012
Certificate fingerprints:
         MD5:  A8:E7:F5:D3:46:38:81:35:B4:12:D2:61:F4:D0:AE:23
         SHA1: 1D:90:58:8B:EE:45:E9:D8:FC:6D:50:E2:D0:0E:CD:40:08:1B:48:62
         Signature algorithm name: SHA1withDSA
         Version: 3

*******************************************
*******************************************


Извличане съгласно стандарта Distinguished Encoding Rules(DER). Версия годна за визуализиране може да се получи с опцията –rfc, която извежда във формат Privacy Enhanced Mail (PEM).
keytool -export -alias mykey -rfc -keypass privatePass -storepass publicPass
-file ivan.pem -keystore keystore.bin


разглеждането на сертификата:
keytool -printcert -file ivan.pem

Owner: CN=Ivan Momtchev, OU=prof, O=Tu-Sofia, L=Sofia, ST=Sofia, C=BG
Issuer: CN=Ivan Momtchev, OU=prof, O=Tu-Sofia, L=Sofia, ST=Sofia, C=BG
Serial number: 4f316d6f
Valid from: Tue Feb 07 20:29:03 EET 2012 until: Mon May 07 21:29:03 EEST 2012
Certificate fingerprints:
         MD5:  A8:E7:F5:D3:46:38:81:35:B4:12:D2:61:F4:D0:AE:23
         SHA1: 1D:90:58:8B:EE:45:E9:D8:FC:6D:50:E2:D0:0E:CD:40:08:1B:48:62
         Signature algorithm name: SHA1withDSA
         Version: 3

Въвеждане на сертификата в хранилище за сертификати с публични ключове:
keytool -import -alias mykey -file ivan.pem -keystore cacert -storepass publicPass

Owner: CN=Ivan Momtchev, OU=prof, O=Tu-Sofia, L=Sofia, ST=Sofia, C=BG
Issuer: CN=Ivan Momtchev, OU=prof, O=Tu-Sofia, L=Sofia, ST=Sofia, C=BG
Serial number: 4f316d6f
Valid from: Tue Feb 07 20:29:03 EET 2012 until: Mon May 07 21:29:03 EEST 2012
Certificate fingerprints:
         MD5:  A8:E7:F5:D3:46:38:81:35:B4:12:D2:61:F4:D0:AE:23
         SHA1: 1D:90:58:8B:EE:45:E9:D8:FC:6D:50:E2:D0:0E:CD:40:08:1B:48:62
         Signature algorithm name: SHA1withDSA
         Version: 3
Trust this certificate? [no]:  y
Certificate was added to keystore

Разглеждане на полученото хранилище:

keytool -list -v -keystore cacert -storepass publicPass

Keystore type: JKS
Keystore provider: SUN

Your keystore contains 1 entry

Alias name: mykey
Creation date: 2012-2-7
Entry type: trustedCertEntry

Owner: CN=Ivan Momtchev, OU=prof, O=Tu-Sofia, L=Sofia, ST=Sofia, C=BG
Issuer: CN=Ivan Momtchev, OU=prof, O=Tu-Sofia, L=Sofia, ST=Sofia, C=BG
Serial number: 4f316d6f
Valid from: Tue Feb 07 20:29:03 EET 2012 until: Mon May 07 21:29:03 EEST 2012
Certificate fingerprints:
         MD5:  A8:E7:F5:D3:46:38:81:35:B4:12:D2:61:F4:D0:AE:23
         SHA1: 1D:90:58:8B:EE:45:E9:D8:FC:6D:50:E2:D0:0E:CD:40:08:1B:48:62
         Signature algorithm name: SHA1withDSA
         Version: 3


*******************************************
*******************************************


За допълнителна информация:
http://docs.oracle.com/javase/6/docs/technotes/tools/solaris/keytool.html
Beginning Cryptography in Java  by David Hook  Wrox Press © 2005 (480 pages)  ISBN:0764596330

 SSL сокети




·         конфиденциалност и цялостност на обменяните в двете посоки данни;

·         идентификация на сървъра;

·         идентификация на клиента.




import java.io.*;
import java.net.*;
public class HTTPClient {
    public static void main(String[] args) {
        int port = 80; // подразбиращ се http порт
        String host = "www.verisign.com";
        try {
            InetAddress addr = InetAddress.getByName(host);
            Socket socket = new Socket(addr, port);
            Writer out = new OutputStreamWriter(socket.getOutputStream());
            out.write("GET http://" + host + "/ HTTP/1.1\r\n");
            out.write("Host: " + host + "\r\n");
            out.write("\r\n");
            out.flush();
            BufferedReader in = new BufferedReader(new InputStreamReader(
                    socket.getInputStream()));
            // четене на заглавната част ( header)
            String s;
            while (!(s = in.readLine()).equals("")) {
                System.out.println(s);
            }
            System.out.println();
            // четене на броя символи
            String contentLength = in.readLine();
            int length = Integer.MAX_VALUE;
            try {
                length = Integer.parseInt(contentLength.trim(), 16);
            } catch (NumberFormatException ex) {
                System.out.println("This server doesn't send the content-length");
                socket.close();
                return;
            }
            System.out.print("Content Length = ");
            System.out.println(contentLength+ " ("+length+" characters)");
            int c;
            int i = 0;
            while ((c = in.read()) != -1 && i++ < length) {
                System.out.write(c);
            }
            System.out.println();
            socket.close();    
        } catch (IOException ex) {
            System.out.println(ex);
        }
    }



В най-честияслучай, SSL се използва между идентифициран сървър и клиент


Ако протоколът е RSA, след първоначалната заявка на клиента, сървърът изпраща на клиента сертификационна верига. Клиентът използва последният сертификат във веригата за да криптира подходящ   pre-master ключ , който се ипрaща обратно на сървъра. След това той се преобразува и в двете страни в симетричен криптиращ ключ, който в последствие се използва за криптиране.






import java.io.*;
import javax.net.ssl.*;
public class HTTPSClient {
    public static void main(String[] args) {
        int port = 443; // подразбиращ се  https порт
        String host = "www.verisign.com";;
        try {
            SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
            SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
            Writer out = new OutputStreamWriter(socket.getOutputStream());
            socket.startHandshake();
            out.write("GET http://" + host + "/ HTTP/1.1\r\n");
            out.write("Host: " + host + "\r\n");
            out.write("\r\n");
            out.flush();
            BufferedReader in = new BufferedReader(new InputStreamReader(
                    socket.getInputStream()));
                    // четене на заглавната част ( header)
            String s;
            while (!(s = in.readLine()).equals("")) {
                System.out.println(s);
            }
            System.out.println();
                    // четене на броя символи
            String contentLength = in.readLine();
            int length = Integer.MAX_VALUE;
            try {
                length = Integer.parseInt(contentLength.trim(), 16);
            } catch (NumberFormatException ex) {
                System.out.println("This server doesn't send the content-length");
                return;
            }
            System.out.println("will send "+contentLength+ " = "+length+"characters\n");
            int c;
            int i = 0;
            while ((c = in.read()) != -1 && i++ < length) {
                System.out.write(c);
            }
            System.out.println();
            socket.close();    
        } catch (IOException ex) {
            System.err.println(ex);
        }
    }
}

С идентификация на сървъра и клиента





Необходимо е:
Тестови публични сертификати (mycacert) както и частни ключове за сървър (server/testkey) и за клиент (client/testkey) са предоставени от компанията Oracle за тестване на подобни програми и могат да бъдат свалени от тук.
Тези ключове са подписани от VeriSign но са с относително малка дължина и могат да бъдат ползвани за илюстрация, не за реална работа. Както всички останали ключове и те имат срок на валидност. Предоставени са две хранилища за частни и публични ключове ( два файла с име testkey) – едно за клиент Duke  и едно за сървър localhost. Паролата за тези хранилища е „passphrase”. Файлът samplecacerts представлява хранилище за публични сертификати, който е подобен на стандартния SDK cacert файл (намиращ се в директория file://${java.home}/lib/security) и включва сертификати от различни доставчици. Освен това включва сертификати за гореспоменатите Duke и localhost. Паролата за хранилището е changeit.


За създаване на идентифициращ се сървър необходимо е:
•    Да се създаде SSLContext обект за избрания алгоритъм.
•    Да се създаде обект TrustManagerFactory на базата на хранилището на сертификати.
•    Да се създаде обект KeyManagerFactory на базата на хранилището на частните и публични ключове.
•    Да се създаде KeyStore обект за ключовете и сертификатите ( с подразбираща се стойност JKS.
•    Да се запълни KeyStore обекта с ключовете и сертификатите.
•    Да се инициализира KeyManagerFactory с KeyStore и неговата парола.
•    Да се инициализира SSLContext обекта с необходимото управление на ключовете от създадения обект KeyManagerFactory,управлението на сертификатите от създадения обект TrustManagerFactory и избран алгоритъм за генериране на случайни числа



import java.util.Properties;
import java.io.*;
import java.net.*;
import java.security.KeyStore;
import javax.net.*;
import javax.net.ssl.*;
class ServeOneClient extends Thread {
    private Socket socket;
    private BufferedReader in;
    private PrintWriter out;
    public ServeOneClient(Socket s)  throws IOException {
        socket = s;
        in = new BufferedReader(
                new InputStreamReader(
                        socket.getInputStream()));
        // Enable auto-flush:
            out = new PrintWriter( new BufferedWriter(
                    new OutputStreamWriter(socket.getOutputStream())),true);
            System.out.println ("new client call");
            start();    // Calls run()
    }
    public void run() {
        try {
            while (true) {
                String str = in.readLine();
                System.out.println("Client sent: " + str);
                if (str.equalsIgnoreCase("END")){
                    out.println("client sent \"End\" - closing connection...");
                    System.out.println("Closing connection...");
                    break;
                }else {
                    out.println("the client's line /"+str+ "/ has "+str.length()+" characters");
                }         
            }
            System.out.println("client sent \"End\" -closing...");
        } catch (IOException e) {  }
        finally {
            try {
                socket.close();
            } catch(IOException e) {}
        }
    }
}
public class MultiClientSslServer {
    private static int port = 2001;

    public static void main(String args[])throws IOException {
        Properties props = System.getProperties();       
        props.put("javax.net.ssl.trustStore","samplecacerts"); 
        props.put("javax.net.ssl.trustStorePassword","changeit");
        ServerSocket ss=null;
        boolean auth = false;
        if (args.length >= 1) {
            if(args[0].equalsIgnoreCase("auth")){
                auth=true;
            }
        }
        try {
            ServerSocketFactory ssf = getServerSocketFactory();
            ss = ssf.createServerSocket(port);
            System.out.print("SslServer started");
            if(auth){
                ((SSLServerSocket)ss).setNeedClientAuth(true);
                System.out.println(" with client authentication");
            }
            else{
                System.out.println(" without client authentication");
            }
        } catch (IOException e) {
            System.out.println("Unable to start MultiClientSslServer: " +
                    e.getMessage());
            e.printStackTrace();
        }
        try {
            while(true) {
                // Blocks until a connection occurs:
                Socket socket = ss.accept();
                try {
                    new ServeOneClient(socket);
                } catch(IOException e) {
                    socket.close();
                }
            }
        } finally {
            ss.close();
        }
    }
    private static ServerSocketFactory getServerSocketFactory() {
        SSLServerSocketFactory ssf = null;
        try {
            // set up key manager to do server authentication
            SSLContext ctx;
            KeyManagerFactory kmf;
            KeyStore ks;
            char[] passphrase = "passphrase".toCharArray();
            ctx = SSLContext.getInstance("TLS");
            kmf = KeyManagerFactory.getInstance("SunX509");
            ks = KeyStore.getInstance("JKS");
            ks.load(new FileInputStream("testkeys"), passphrase);
            kmf.init(ks, passphrase);
            ctx.init(kmf.getKeyManagers(), null, null); //подразбиращи се TrustManagerFactory
            ssf = ctx.getServerSocketFactory();
            return ssf;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }       
    }

java
       -Djavax.net.ssl.trustStore=samplecacerts
      -Djavax.net.ssl.trustStorePassword=changeit MultiClientSslServer


Клиент без идентификация  (в програмата не е посочен примерния  samplecacerts)

import java.io.*;
import javax.net.ssl.*;

public class SSLClient {
    public static void main(String[] args) throws Exception {
        String message = "";
        try {
            SSLSocketFactory factory =
                    (SSLSocketFactory)SSLSocketFactory.getDefault();
            SSLSocket socket =
                    (SSLSocket)factory.createSocket("127.0.0.1", 2001);
           
            socket.startHandshake(); 
            BufferedReader sin = new BufferedReader(
                    new InputStreamReader(System.in));
            PrintWriter out = new PrintWriter(
                    new BufferedWriter(
                            new OutputStreamWriter(
                                    socket.getOutputStream())));
            BufferedReader in = new BufferedReader(
                    new InputStreamReader(
                            socket.getInputStream()));
            while(!message.equalsIgnoreCase("END")){
                System.out.print("input a line (\"END\" to finish): ");
                message = sin.readLine();
                out.println(message);
                out.flush();
                if (out.checkError())
                    System.out.println(
                            "SSLSocketClient:  java.io.PrintWriter error");
               
                /* read response */
                String inputLine;
                if ((inputLine = in.readLine()) != null){
                    System.out.println("Server sends: "+inputLine);
                }
                else {
                    System.out.println("No response from server");
                }
            }
            in.close();
            out.close();
            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


Клиент с идентификация

import java.util.Properties;
import java.security.KeyStore;
import java.io.*;
import javax.net.ssl.*;
public class SSLAuthClient {
    public static void main(String[] args) throws Exception {
        String message = "";
        Properties props = System.getProperties();       
        props.put("javax.net.ssl.trustStore","samplecacerts"); 
        props.put("javax.net.ssl.trustStorePassword","changeit");
        SSLSocketFactory factory = null;
        try {
            factory = getSocketFactory();
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
        SSLSocket socket =
                (SSLSocket)factory.createSocket("127.0.0.1", 2001);
        socket.startHandshake();
        BufferedReader sin = new BufferedReader(
                new InputStreamReader(System.in));
        PrintWriter out = new PrintWriter(
                new BufferedWriter(
                        new OutputStreamWriter(socket.getOutputStream())));
        BufferedReader in = new BufferedReader(
                new InputStreamReader(socket.getInputStream()));
        while(!message.equalsIgnoreCase("END")){
            System.out.print("input a line (\"END\" to finish): ");
            message = sin.readLine();
            out.println(message);
            out.flush();
            if (out.checkError())
                System.out.println(
                        "SSLSocketClient:  java.io.PrintWriter error");
            String inputLine;
            if ((inputLine = in.readLine()) != null){
                System.out.println("Server sends: "+inputLine);
            }
            else {
                System.out.println("No response from server");
            }
        }
        socket.close();
    }
    private static SSLSocketFactory getSocketFactory() {
        SSLSocketFactory factory = null;
        try {
            // set up key manager to do server authentication
            SSLContext ctx;
            KeyManagerFactory kmf;
            KeyStore ks;
            char[] passphrase = "passphrase".toCharArray();
            ctx = SSLContext.getInstance("TLS");
            kmf = KeyManagerFactory.getInstance("SunX509");
            ks = KeyStore.getInstance("JKS");
            ks.load(new FileInputStream("testkeys"), passphrase);
            kmf.init(ks, passphrase);
            ctx.init(kmf.getKeyManagers(), null, null);
            factory = ctx.getSocketFactory();
            return factory;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }       
    }
}

java
       -Djavax.net.ssl.trustStore=samplecacerts
      -Djavax.net.ssl.trustStorePassword=changeit 
SSLAuthClient


Създайте две хранилища за частни ключове, които да заместят тестовите( aliases - localhost, client; като паролите на aliases трябва да съвпадат с паролата на хранилището (например localhostPass и clientPass).  Създайте хранилище за публични сертификати cacert, което да съхранява публичните сертификати на двете хранилища. Стартирайте сървъра и клиента с така генерираните хранилища.