Monday, November 25, 2013

Yet another InstallCert for Java, now with STARTTLS support

Java

⇓ ... now forked at GitHub

added on 2017-10-21



Many of the Java folks, who ever dealt with SSL-enabled protocols and self-made SSL certificates, know of the InstallCert tool. This simple command-line tool, published in 2006 by Andreas Sterbenz at the official Sun blog, allows obtaining SSL certificates as they are presented by the hosts we connect to, with further optional saving them to the system trust store.

Sun's blog is not with us any more, but a copy of the original InstallCert publication and code is still available from some of the users' blogs, like this one, or archives like that one. Curiously, one of the current blogs at Oracle mentions this tool, but without reference to the original author, and with a reference (currently somewhat outdated) to the mentioned user blog instead...

Well, the original Andreas' tool served faithfully to me for quite a while, but every good thing has its limitations... In particular, the original InstallCert could not deal with hosts that operate using STARTTLS technique.

The new code


Diving into STARTTLS required quite a refactoring of the original code, though the main parts of it are still in place . In particular, modular approach was taken to deal with STARTTLS implementations for different protocols, so the code does not fit in single Java file any more, but is rather packaged as an executable .jar.

It is now possible to obtain certificates from hosts that not only speak plain SSL/TLS, but also expose their certificates via STARTTLS over IMAP, POP3, SMTP and LDAP.

For new application-level protocols with STARTTLS extension to be supported, an abstract STARTTLS handler is defined as a StarttlsHandler interface. This interface needs to be implemented by every new protocol handler, and the latter is to be registered with the Starttls wrapper class. This registration needs to be hard-coded so far. But keeping in mind the small number of STARTTLS-compatible application-level protocols yet to be implemented, this should not be a problem .

The certificates collected by the program are now stored at two locations:
  • the standard jssecacerts keystore in the lib/security folder of your JRE;
  • in an extracerts keystore in your current directory; the latter may be handy in order to save collected certificates in pure form for further redistribution.
One of the new features is also the new template for collected certificates' aliases. They are now named like "host - certificate_subject" for better human readability .

Downloads


The following downloads are available:
Please feel free to use and modify. The original license looks like 3-clause BSD one.

Usage – HOW-TO


Prerequisites

  1. Download the binary distribution archive from here or here.
  2. Unzip it to a location of your choice.

Obtaining a certificate from a plain SSL/TLS or an LDAP/STARTTLS server

Run the program like this:
java -jar installcert-usn-20140115.jar host_name
or
java -jar installcert-usn-20140115.jar host_name:port
or
java -jar installcert-usn-20140115.jar host_name:port truststore_password

The default port is 443 (for HTTPS). The default truststore password is "changeit" as per JSSE convention.

Obtaining a certificate from an IMAP / POP3 / SMTP server with STARTTLS extension

In this case you will need the JavaMail library, and make sure you have it on your classpath. Please also keep in mind that it is necessary to indicate the main class explicitly in the command line if you have more than one jar.

To make things easier, two shell scripts are provided: run-with-javamail-starttls.sh for Unix and run-with-javamail-starttls.cmd for Windows. You will have to edit at least one of them first, so to reflect the actual location of the JavaMail .jar file.

General notes and final housekeeping

If the program succeeds in obtaining a certificate (or several of them), and the certificates are not known yet, it will ask you whether you wish to save them. Upon successful run the program saves the new certificate(s) to two files, as mentioned above:
  • the standard jssecacerts keystore in the lib/security folder of your JRE;
  • an extracerts keystore in your current directory; this one may be handy in order to save collected certificates in pure form for further redistribution.
The first one will be needed by your software for normal JSSE operation. The second one is a good candidate for "clean" storage of your selected certificates.

Please keep in mind that in order to have the standard jssecacerts keystore file in the lib/security folder of your JRE successfully created/modified, you will most likely need to have administrative (superuser, root) privileges.

Enjoy!



... rebuilt for Java 1.6 and fixed

added on 2014-01-15

The download links and examples were updated to reflect the new build made to be compatible with Java 1.6 as per Eric's comment. The certificate handling logic was also improved for better discrimination of new certificates vs known ones.


... now forked at GitHub

added on 2017-10-21

This code is now forked and available at Github: https://github.com/spyhunter99/installcert . The fork was created with an intention to achieve embeddability, and looks actively developed...

Monday, June 17, 2013

Look Ma, no ResourceBundle :) Step 2: Dealing with message arguments

Java
In the previous post an approach to internationalization of Java applications was suggested that does not need a ResourceBundle to do the job. Collections of localized messages may be arranged to be referenced as static class members rather than string aliases for properties in a ResourceBundle. The main benefit is gained from getting references to messages compiler verifiable, with programmer's IDE autocompletion aids available for localized messages as a bonus .

This approach was described and implemented previously at initial level, being still more of a concept than of a tool for real life applications. The main feature missing was an ability to supply arguments to localized messages, similar to MessageFormat.

Changes


Getting message arguments into the game resulted in certain refactoring, particularly in substantial separation of code from data and coining a concept of I18nHandler. Another change in concept was due to the fact that it is not possible to get a string value of an object via its toString() method if you are to supply formatting arguments. Hence, only methods like s(TArg1 arg1, TArg2 arg2) are available in these cases. Some less important change is that I18nItem no more extends HashMap but rather has more Map instances as members . And the former p(String language, String message) method for instantiating multi-language messages was refactored to lm(String localeTag, String message) in a separate class. One more notable change is that differentiation of messages by language turned into differentiation by locale language tag. And finally, the package was renamed to usn.i18n.nobundle.

New design


Data

LocalizedMessage is the basic container for localized messages. Internally it is a tuple of a locale language tag and a corresponding localized message. In case you do not care about locale-specific formatting of dates and numbers, feel free to reduce the full language tag to just the language subtag like "en". The localized message may be a mere string or a formatting pattern as per MessageFormat convention. The standard way to construct a LocalizedMessage instance is via its static factory method lm(String localeTag, String message). Normally there is no need to construct an isolated LocalizedMessage instance unless as a component of an I18nItem.

I18nItem is the basic class to represent an internationalized message. Internally is has a collection of LocalizedMessage instances for a necessary set of locales. I18nItem is not intended for direct use by applications but rather serves as the base class for an hierarchy of more specific subclasses.

Special care was taken to reduce the risk of wrong use of arguments for messages, e.g. providing arguments of wrong type or in wrong number. With this in mind the following I18nItem subclasses are offered for use:
  • I18nItem0 – the class to use for messages without arguments;
  • I18nItem1<TArg1> – a generic class to use for messages with exactly one argument;
  • I18nItem2<TArg1,TArg2> – a generic class to use for messages with exactly two arguments;
  • I18nItem2<TArg1,TArg2,TArg3> – a generic class to use for messages with exactly three arguments;
  • I18nItemAny – a class to use for messages with arbitrary number of arguments, as the last resort for cases that did not fit into previous subclasses;
A snippet for declaring some set of internationalized messages might look like this:
...
import usn.i18n.nobundle.I18nItem;

// get 'lm()' available as shorthand method without being prefixed by
// 'LocalizedMessage'
import static usn.i18n.nobundle.LocalizedMessage.lm;

...

static I18nItem0 MESSAGE_1 = new I18nItem0
    (lm ("en", "That's cool!"),
     lm ("fr", "C'est le pied!"),
     lm ("ru", "Круто!"));
static I18nItem1<Integer> MESSAGE_2 = new I18nItem1<Integer>
    (lm ("en", "I know {0,number,integer} guys seeking something" +
               " like this..."),
     lm ("fr", "Je sais que {0,number,integer} gars à la recherche" +
               " de quelque chose comme ceci..."),
     lm ("ru", "Я знаю ещё {0,number,integer} ребятишек, которые" +
               " хотели чего-то подобного..."));

Handlers

The next important topic to take care of is an approach to obtaining user language / locale preferences during run time. In anticipation of vast variety of application architectures, this topic is resolved in the most general way with the help of the I18nHandler class. This class has absorbed almost all the code that is responsible for handling of methods like s(TArg1 arg1, TArg2 arg2), and meanwhile it allows applications to achieve necessary behavior by its subclassing and overriding getDefaultLocaleTag() and most importantly getUserLocaleTags() methods. The only thing an application needs to do is:
1) subclass I18nHandler or select from its existing subclasses as necessary;
2) create an instance of the selected I18nHandler subclass; this instance will establish itself as a singleton and take care of all the rest.

I18nHandlerForSingleUser is an almost ready to use I18nHandler subclass that is intended for use by applications like desktop ones, with single user per application instance, when it is possible to define all user preferences upon application startup. You may only still wish to override the getDefaultLocaleTag() method.

Context based internationalization

There are also cases to be taken care of specially, when user preferences are conveniently available from some execution context rather than from the application directly. A standard example is a web application, where some primitive user preferences may be retrieved from a ServletRequest instance. Or maybe even better from an associated HttpSession instance. These cases are taken care of by I18nHandlerInContext<TContext> and I18nHandlerForServletRequest subclasses, to be subclassed further as necessary.

And  I18nHandlerInContext<TContext> subclasses require a special family of I18nItem subclasses, that take a TContext instance as the first argument for all their methods:

Internationalization for logging

Finally, to bring the renovated package closer to real world application needs, two more classes are added that get this internationalization technology available for logging: I15dLogger and I15dLoggerFactory. These two bridge I18nItem with SLF4J in hope that is going to be sufficient these days .

Behind the scenes


So what happens when an I18nItem instance's s() or similar method is called?

  1. The I18nHandler singleton is located.
  2. Its getUserLocaleTags() method is called. This method is expected to be overridden by a subclass to do something meaningful . An array of user preferred locale language tags is the result.
  3. A negotiation between user preferences and locales available for given I18nItem instance takes place in the handler's findBestLocaleTag(I18nItem,String[]) method. For every user's preferred locale two attempts are made: for exact match and for approximate match via language subtag. If no luck, then the system default locale is attempted. If again no luck, then plain English is attempted. And if not successful again, the first locale available for given I18nItem instance is selected.
  4. The localized message (message pattern) is retrieved from given I18nItem instance for the locale language tag selected on the previous step.
  5. Message formatting arguments are applied if required.

Downloads


Both jar and source downloads are available. And the project is now hosted at Github. Please feel free to use and modify. The license is BSD.


Usage – HOW-TO


  1. Download the .jar file .
  2. Add it to your application's class path.
  3. Make a decision on the I18nHandler subclass you need: whether it should depend on some context for obtaining user's locale preferences or should it get these preferences from the application directly; hence select an existing I18nHandler subclass or make a subclass of your own.
  4. Create an instance of the chosen I18nHandler subclass in your application; no need to care about its further fate; feel free to mark it with @SuppressWarnings ("unused").
  5. Create as many of I18nItem sublasses' instances as you need for all the messages that require internationalization.
  6. Use your messages via corresponding s(TArg1 arg1) and similar methods.
  7. Refer to the modest example on the package summary javadocs page and to API docs in general when necessary.
  8. Enjoy .

Package name


I personally feel not very comfortable having a negative word like "nobundle" in the package name. But got no better idea so far. So positive ideas on naming are welcome!

Comments welcome


Though the library is already tested in a real life application, it is probably undercooked and may contain bugs. It may also ask for improvement here and there. So any comments are welcome too!..

Saturday, February 23, 2013

Look Ma, no ResourceBundle :) Or yet another approach to i18n in Java

Java
Everyone knows the traditional way to make a Java application international: one should give every text message a symbolic name, place all the strings in various languages in a set of special resource files, and then get message strings used via their symbolic names in the prescribed manner. This is not all of course, and one furthermore has to face different locales, plurals, genders, etc. ... And ResourceBundle has always been an important part of these internationalization facilities.

This approach is well developed, and lots of tools are available to help using it, and still there is an issue about it. Java code is linked to string resources via symbolic names that are mere strings, and it is pretty easy to get them out of sync in the course of changes. And as these links are not checked by compiler, a programmer is almost certain to feel happy until a surprise at run time...

A colleague of mine has once mentioned that it would be more robust and convenient to have text resources linked to Java code via class member names. Then these links would be checked by the compiler, and a programmer could also benefit from autocompletion facilities provided by modern IDE's.

It took some time to get the idea crystallized, and here is the result. It is all about a class named I18nItem that extends HashMap (that might be any other Map) and has an overridden toString() method. Every instance of such a class would wrap one text message in different languages, indexed by language. One more task was to implement a convenient way to construct such collections as members of some class, so to allow easy referring to them from other parts of code.

The new approach in action


Let's have a look at what a standard use case might look like.

First an application would typically have I18nItem subclassed:
...
import usn.util.i18n.I18nItem;

public class AppSpecificI18nItem extends I18nItem
  {
    private static final long serialVersionUID = 1L;

    // define a simple subclass constructor to be used
    AppSpecificI18nItem (I18nItem.LanguagePair... data)
      {
        super (data);
      }

    // let's assume the default application language is going to be
    // French, so override the appropriate method
    @Override
    protected String getDefaultLanguage ()
      {
        return "fr";
      }

    // then the application needs to define the way to obtain current
    // user's language preferences as string array, most preferred
    // ones coming first
    @Override
    protected String [] getUserLanguages ()
      {
        // normally we are expected to do something more interesting
        // here...
        return super.getUserLanguages ();
      }
  } // class AppSpecificI18nItem

Next we need to construct our international string resources without using ResourceBundle – in any Java class you may find appropriate:
...
import usn.util.i18n.I18nItem;

// get 'p()' available as shorthand method without being prefixed by
// 'I18nItem'
import static usn.util.i18n.I18nItem.p;

public class MyMessages
  {
    public static I18nItem MESSAGE_1 = new AppSpecificI18nItem
        (p ("en", "That's cool!"),
         p ("fr", "C'est le pied!"),
         p ("ru", "Круто!")); 
    public static I18nItem MESSAGE_2 = new AppSpecificI18nItem
        (p ("en", "Just what we need!"),
         p ("fr", "Exactement ce qu'il nous faut!"),
         p ("ru", "То, что нужно!"));
    // ...
  } // class MyMessages

And finally we are going to use the messages we have defined:
...

// in some cases implicit conversion to String is available, and the
// overridden 'getUserLanguages()' method combined with
// 'I18nItem#toString()' takes care of everything...
System.out.println (MyMessages.MESSAGE_1);

// ... and in other cases the 's()' shorthand method is at our
// disposal...
logger.info (MyMessages.MESSAGE_2.s ());

And in every case like these your IDE is likely to assist you with right selection of the message to use...

Downloads


Both jar-with-source and demo downloads are available. Please feel free to use and modify. The license is BSD.

Acknowledgements


Special thanks to my colleague Sergey Petrov for the idea that access to a resource via some class member might be preferable against string alias .



Continued: Step 2: Dealing with message arguments

added on 2013-06-21