# Uhrapplet - Zeit per NTP holen, wie? (Security)



## schmeckzilla (3. Feb 2006)

Hallo,

auch ich habe mich an einem Uhrapplet versucht. Mein JApplet holt per ntp die aktuelle Zeit und zeigt die deutsche und australische Uhrzeit mit Datum an.

*[Edit:]*
Mein Problem mit dem Jar erzeugen habe ich mittlerweile gelöst. Jetzt bekomme ich jedoch AccessContolException (java.security.AccessControlException: access denied (java.net.SocketPermission pool.ntp.org resolve)). Das Applet soll auf der Webseite nur die deutsche und australische Uhrzeit anzeigen. Signieren scheidet aus meiner Sicht aus, da nicht beim Laden der Webseite immer nach der Zustimmung gefragt werden sollte.

Gibt es noch andere Lösungen? Nur die lokale Zeit abfragen und entsprechende TimeZone setzen funktioniert nur, wenn der User seine Uhrzeit und Zeitzone korrekt gesetzt hat, oder?
*[/Edit]*

Als Hilfsklasse verwende ich org.apache.commons.net.ntp zum Holen der Zeit. Mein Code ist in 2 Klassen in 2 verschiedenen Packages aufgeteilt. 

Das Applet "SimpleWorldClock" ist im Package "de.schmeckzilla.applet" und die Hilfsklasse "NTPTime" ist im Package "de.schmeckzilla.de". Die Commons-Net-Class-Dateien habe ich auch in das JAR gepackt. 

Entwickelt habe ich es mit Eclipse und dort läuft es auch. Um die Dateigröße zu reduzieren habe ich die Classdatei von commons-net aus dem commons-net.jar gelöst und in den entsprechenden Ordnern (wegen den Packages) gelöst. 

Nun meine Fragen:
 1. Was sollte oder könnte ich noch am Code ändern? 
    1.1 Wie realisiert man sonst eine Uhr?
    1.2 Wie kann ich erreichen das der Thread genau eine 1 Sekunde schläft und nicht länger?
    1.3 Sollte in run() auch auch noch myThread.yield() aufgerufen werden und falls ja warum?

 2. Beim Versuch mein gebautes JAR als Applet zu laden habe ich Probleme. de.schmeckzilla.applet.SimpleWorldClock wird gestartet, aber de.schmeckzilla.util.NTPTime wird nicht gefunden. Woran kann das liegen?

*de.schmeckzilla.util.NTPTime.java*

```
package de.schmeckzilla.util;

import java.io.IOException;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.*;
import org.apache.commons.net.ntp.*;

public class NTPTime {

    static private String default_time_server = "pool.ntp.org";

    private String time_server;

    private boolean errorflag = true;

    private String error_name;

    private Date date;

    public NTPTime() {
        this(default_time_server); // calls the other constructor
    }

    public NTPTime(String time_server_by_name) {
        time_server = time_server_by_name;
    }

    public String getTimeserverByName() {
        return time_server;
    }

    public boolean fetchTime() {

        NTPUDPClient timeclient = new NTPUDPClient();
        TimeInfo timeinfo;

        try {
            timeclient.open();
        } catch (SocketException e) {
            // TODO Auto-generated catch block
            setErrorVariables(e);
            return false;
        }

        try {
            timeinfo = timeclient.getTime(InetAddress.getByName(time_server),
                    NTPUDPClient.DEFAULT_PORT);
            NtpV3Packet timepacket = timeinfo.getMessage();
            TimeStamp tstamp = timepacket.getReceiveTimeStamp();
            date = tstamp.getDate();

        } catch (UnknownHostException e) {

            setErrorVariables(e);
            return false;
        } catch (IOException e) {
            setErrorVariables(e);
            return false;
        }

        timeclient.close();

        return true;

    }

    public Date getDate() {
        return date;
    }

    private void setErrorVariables(Exception e) {
        error_name = e.toString();
        System.err.println("Error: " + error_name);
        errorflag = true;
    }

    public String getErrorMessage() {
        return error_name;
    }

}
```

*de.schmeckzilla.applet.SimpleWorldClock.java*

```
package de.schmeckzilla.applet;

import javax.swing.*;
import de.schmeckzilla.util.*;
import java.util.*;
import java.text.*;
import java.awt.*;

public class SimpleWorldClock extends JApplet implements Runnable {

    private NTPTime ntpTime;

    private DateFormat dfTimeMelbourne;

    private DateFormat dfTimeGermany;

    private DateFormat dfDateMelbourne;

    private DateFormat dfDateGermany;

    private Date ntpDate;

    private Date localDate;

    private GregorianCalendar calendar;

    private boolean ntpViaNTP;

    private JLabel jlMelbourne;

    private JLabel jlGermany;

    private JLabel jlMelbourneTime;

    private JLabel jlGermanyTime;

    private JLabel jlMelbourneDate;

    private JLabel jlGermanyDate;

    private Thread myThread;

    private long updateInterval = 1000;

    public void run() {
        while (true) {
            calendar.add(GregorianCalendar.SECOND, 1);
            jlMelbourneTime.setText(dfTimeMelbourne.format(calendar.getTime()));
            jlMelbourneDate.setText(dfDateMelbourne.format(calendar.getTime()));
            jlGermanyTime.setText(dfTimeGermany.format(calendar.getTime()));
            jlGermanyDate.setText(dfDateGermany.format(calendar.getTime()));
            try {
                Thread.sleep(updateInterval);

            } catch (InterruptedException e) {
                System.out.println("interrupted...");
                return;
            }
        }
    }

    public void init() {

        // create and configure the DateFormatter
        dfTimeMelbourne = DateFormat.getTimeInstance(DateFormat.MEDIUM,
                Locale.GERMANY);
        dfTimeGermany = DateFormat.getTimeInstance(DateFormat.MEDIUM,
                Locale.GERMANY);
        dfDateMelbourne = DateFormat.getDateInstance(DateFormat.SHORT,
                Locale.GERMANY);
        dfDateGermany = DateFormat.getDateInstance(DateFormat.SHORT,
                Locale.GERMANY);

        dfTimeMelbourne.setTimeZone(TimeZone.getTimeZone("Australia/Melbourne"));
        dfTimeGermany.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));
        dfDateMelbourne.setTimeZone(TimeZone.getTimeZone("Australia/Melbourne"));
        dfDateGermany.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));

        // get Date and Time from the local pc
        localDate = new Date();

        // get Date and Time via NTP
        ntpTime = new NTPTime();
        if (ntpTime.fetchTime()) {
            ntpDate = ntpTime.getDate();
            ntpViaNTP = true;
        } else {
            ntpViaNTP = false;
            System.err.println("Es ist ein Fehler aufgetreten!");
            System.err.println(ntpTime.getErrorMessage());
            System.err.println("Verwende lokale Uhrzeit!");
            ntpDate = localDate;

        }

        calendar = new GregorianCalendar();
        calendar.setTime(ntpDate);

        // making the GUI
        jlMelbourne = new JLabel("Melbourne/Australia");
        jlGermany = new JLabel("Bonn/Germany");
        jlMelbourneTime = new JLabel(dfTimeMelbourne.format(ntpDate));
        jlGermanyTime = new JLabel(dfTimeGermany.format(ntpDate));
        jlMelbourneDate = new JLabel(dfDateMelbourne.format(ntpDate));
        jlGermanyDate = new JLabel(dfDateGermany.format(ntpDate));

        this.setSize(300, 100);
        this.getContentPane().setLayout(new GridLayout(3, 2));
        this.getContentPane().add(jlMelbourne);
        this.getContentPane().add(jlGermany);
        this.getContentPane().add(jlMelbourneTime);
        this.getContentPane().add(jlGermanyTime);
        this.getContentPane().add(jlMelbourneDate);
        this.getContentPane().add(jlGermanyDate);

        myThread = new Thread(this);
        myThread.start();

    }

    public void destroy() {
        myThread.stop();
    }

}
```


----------



## Bleiglanz (6. Feb 2006)

du musst es signieren, ein Applet darf keine Sockets zu "fremden" Servern aufmachen

und wegen der "Zertifikatsrückfrage": da kannst du nix mache, ausser dir (für einige Euronen) ein Zertifikat von einer CA besorgen (Versisign, Thawte, ...)


----------



## schmeckzilla (6. Feb 2006)

Hi Bleiglanz,

habe ich mittlerweile auch schon festgestellt.  Aber signieren halte ich bei einem simplen Uhrenapplet für unpraktikabel. 

Ich habe mir jetzt folgende Lösung überlegt:

1. Ich schreibe mir ein Perl CGI-Skript, dass die Uhrzeit per NTP holt und diese in Millisekunden zurückgibt.

2. In meinem Applet rufe ich das CGI-Skript auf und wandle die Zahl in ein Date um. 

Das Skript liegt auf meinem Server und wird über die selbe Domain aufgerufen werden. Das sollte die Sicherheitsprobleme beim Applet lösen, oder?


----------



## schmeckzilla (6. Feb 2006)

Mein CGI-Skript ist jetzt fertig:


```
#!/usr/bin/perl -T

use Net::NTP;

my %response = get_ntp_response("pool.ntp.org");
# Ergebnis muss mit 1000 multipliziert werden, da die UTC bei 1970 beginnt
# rest wird abgeschnitten
print int($response{"Receive Timestamp"}*1000);
```

Als Ergebnis kriege ich einen Longwert der die Zeit darstellt. Wenn ich nur die Zahl beim CGI-Skript zurückgebe, wie kann ich das dann in das Java-Applet einlesen? 

[OFFTOPIC] Muss das CGI-Skript einen Header zurücksenden? Das Skript wird nicht von einem Browser ausgeführt, sondern nur von meinem Applet.


----------



## Sky (7. Feb 2006)

Erstelle Dir ein URL-Objekt, welches auf dein CGI zeigt und lasse Dir den InputStream zurückgeben und dann einfach: Lesen.


----------



## Bleiglanz (7. Feb 2006)

schöner Workararound, aber irgendwie beknackt

=> wesentlicher Teil beim NTP ist ja das kunstvolle "Wegrechnen" der Network-Latency, und jetzt machst du das am Server und gibst es mit X Millisekunden Verzögerung wieder an das Applet zurück?

aber wenn du nicht signieren willst (verständlich), dann ist das die einzige Möglichkeit, besser als gar nix


----------



## schmeckzilla (7. Feb 2006)

Danke für Eure Antworten. Das mit dem URL-Objekt werde ich mal ausprobieren.

@Bleiglanz
Ist schon ein bißchen umständlich. Aber auf einer Website will ich einfach die 2 Zeiten anzeigen, also die Genauigkeit reicht mir im Sekundenbereich. Die Uhrzeit dient eigentlich nur als Eyecatcher und das Applet wird natürlich noch schöner gestaltet.

Die lokale Zeit zu nehmen scheitert schon dann, wenn jemand seine Timezone und/oder die Uhr nicht richtig gestellt hat. 


Hier noch eine Designfrage:
Ich möchte jetzt beim Start die lokale Uhrzeit nehmen und die Differenz zur NTP-Zeit berechnen. In meinem Thread zähle ich dann meine Sekunden nicht mehr selbst hoch, sondern ich nehme dann wieder die lokale Zeit und addiere die Differenz hoch. Ist das Ok?

Andere Vorschläge? Ist meine Vorgehensweise threadsafe? (Damit meine ich das Ändern der JLabels in meinem eigenen Thread?)


----------



## Bleiglanz (8. Feb 2006)

wenn du mehr als einen Thread (nämlich den vom Applet) hast, dann ist erst mal gar nix threadsafe

=> Änderungen an der GUI aus einem "anderen Thread" immer mit invokeLater



> Ich möchte jetzt beim Start die lokale Uhrzeit nehmen und die Differenz zur NTP-Zeit berechnen. In meinem Thread zähle ich dann meine Sekunden nicht mehr selbst hoch, sondern ich nehme dann wieder die lokale Zeit und addiere die Differenz hoch. Ist das Ok?


würd ich schon so machen, wenns eh nicht so supergenau sein soll;  du kannst ja nicht millionenmal den Server nach der Zeit fragen (verwende einen Timer)


----------

