Getting the hostname of an IP address in Java

The InetAddress Class is able to return the hostname given an IP.
Following app demonstrates the use:

import java.net.*;
 
public class Main
{
   public static void main(String [] args) {
      if (args.length != 1) {
         System.out.println("Usage: java Main <IP>");
         System.exit(1);
      }
 
      try {
         InetAddress inet = InetAddress.getByName(args[0]);
         System.out.println ("Hostname of " + args[0] + ": " + inet.getHostName());
      }
      catch(UnknownHostException e) {
         System.out.println(e);
      }
   }
}

Running it with

java Main 204.71.202.160

outputs:

Hostname of 204.71.202.160: w1.yahoo.com

Bringing up the dial-up networking connection window in Java

In Windows 95/98/ME, the dial up items are not traditional exe files. The following command line executes the necessary files to start a dialup session:

   c:windowsRUNDLL32.EXE RNAUI.DLL,RnaDial <your DUN connection name>

Applied in Java: create a Batch file containing this line and call this file through the Runtime class:
Dial.bat

start /wait rundll32.exe rnaui.dll,RnaDial My Connection

DialUp.java:

import java.io.*;
 
public class DialUp
{ 
   public static void main(String []args) {
      try {
         Process p = Runtime.getRuntime().exec("Dial.bat");
         p.waitFor();
      }
      catch (Exception e) {
         System.out.println(e);
      }
   }
}

When to use setTcpNoDelay

It allows you to enable or disable Nagle’s algorithm, which is used to conserve bandwidth by minimizing the number of data segments that are sent. When TcpNoDelay is enabled, data will not be sent immediately, instead it waits for more write calls and tries to squeeze in as much as possible into a packet before sending. If you know your app should be using small packets anyway (eg. chat apps), and send them as soon as possible (no wait), at the cost of more bandwidth, then you can disable nagle’s algorithm:

   setTcpNoDelay(true)

Nagle’s algorithm is described in RFC896.

What is a certification authority (CA)?

In assymetric encryption, you have a private key with which you encrypt messages and your receivers have public keys with which they can decrypt them. The receiver wants to ensure that the sender is the one who he claims to be. To build in trust between the sender and receiver, the sender can ask for a certificate to a Certification Authority (CA), after providing this organization with documents that proof his identity. The CA issues to the sender a certificate, containing his public key some identification information and a digital signature created using the private key of the CA. The sender can then use this certificate in communicating messages. The receiver uses the public key of the CA to check on the validity of the public key of the sender. The whole idea is that if the receiver trusts the CA, he trusts all the certificates the CA issues.

Generating a MAC that uses SHA-1

The following example consists of a Sender and a Receiver.

- The Sender creates a message, generates a secret key, generates a mac given the message and the key and writes both the message and the mac to the file “message.ser”. Typically, this would be send to the other party.
A MAC works with a secret key. The weak point is that both the sender and the receiver must know the secret key. In the example, the secret key is stored in a keystore “keystore.kst” in the current directory.

- The Receiver reads “message.ser” and extracts the message and the MAC. To find out whether the message was tampered with, it calculates the MAC again using the message and the secret key that it reads out from “keystore.kst”.

Note that to use the keystore for secret keys, the keystore algorithm “JCEKS” must be used. “JCEKS” is implemented by the security provider that comes with JCE. You can download it here.

I added jce1_2_2.jar and sunjce_provider to my classpath. The code is documented.

Sender.java:

import javax.crypto.spec.*;
import javax.crypto.Mac;
import java.security.*;
import javax.crypto.*;
import sun.misc.*;
import java.io.*;
 
public class Sender
{
   public static void main(String []args) throws Exception {
      // message to send 
      String message = "the sun is green and the grass shines";
 
      // generate a random key and store it in keystore.kst in current directory 
      SecretKey key = generateAndStoreKey();
 
      // generate a MAC for the message, given the secret key
      byte[] mac = generateMAC(message, key);
      
      System.out.println("HMAC-MD5 for message '" + message + "':");
      System.out.println("t" + new BASE64Encoder().encode(mac));
      System.out.println("Writing message+mac to file 'message.ser'");
      
      // writing both the message and the mac to message.ser
      ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("message.ser"));
      oos.writeObject(message);
      oos.writeObject(mac);
      oos.close();
   }
 
   public static SecretKey generateAndStoreKey() throws Exception {
      KeyStore ks = KeyStore.getInstance("JCEKS");
      // initiaze new KeyStore, must do!
      ks.load(null, null);
 
      // generate random key
      SecureRandom sr = new SecureRandom();
      byte[] b = new byte[20];
      sr.nextBytes(b);
      SecretKey key = new SecretKeySpec(b, "HmacSHA1");
 
      // save secret key in our new keystore with alias MySecretKey
      FileOutputStream fos = new FileOutputStream("keystore.kst");
      ks.setKeyEntry("MySecretKey", key, "123456".toCharArray(), null);
      ks.store(fos, "123456".toCharArray());
      fos.close();
 
      return key;
   }
 
   public static byte[] generateMAC(String message, SecretKey key) throws Exception {
      // generate message digest based on key
      Mac mac = Mac.getInstance("HmacMD5");
      mac.init(key);
 
      return mac.doFinal(message.getBytes());
   }
}

Receiver.java:

import javax.crypto.spec.*;
import javax.crypto.Mac;
import java.security.*;
import javax.crypto.*;
import sun.misc.*;
import java.io.*;
 
public class Receiver
{
   public static void main(String []args) throws Exception {
      // read in message and mac from message.ser
      ObjectInputStream ois = new ObjectInputStream(new FileInputStream("message.ser"));
      String message = (String) ois.readObject();
      byte[] mac = (byte[]) ois.readObject();
 
      System.out.println("Original message: " + message);
 
      // get secret key from keystore keystore.kst
      SecretKey key = getSecretKey();
 
      // generate mac again given this key
      byte[] newMac = getMAC(message, key);
 
      // ensure the new mac is equal to the one that was "sent" to us
      if (MessageDigest.isEqual(mac, newMac)) {
         System.out.println("Message was not tampered with!");
      }
      else {
         System.out.println("Message has been tampered with!");
      }
   }
 
   public static byte[] getMAC(String message, SecretKey key) throws Exception {
      // generate message digest based on key
      Mac mac = Mac.getInstance("HmacMD5");
      mac.init(key);
  
      return mac.doFinal(message.getBytes());
   }

   public static SecretKey getSecretKey() throws Exception {
      FileInputStream fis = new FileInputStream("keystore.kst");
      KeyStore ks = KeyStore.getInstance("JCEKS");
      ks.load(fis, "123456".toCharArray());
 
      SecretKey key = (SecretKey) ks.getKey("MySecretKey", "123456".toCharArray());
  
      return key;
   }
}

sample run:

C:mybouncycastlemacsha1>java Sender
HMAC-MD5 for message 'the sun is green and the grass shines':
        XmvNl18rRj9ql4JkS3N8vw==
Writing message+mac to file 'message.ser'
 
C:mybouncycastlemacsha1>java Receiver
Original message: the sun is green and the grass shines
Message was not tampered with!

Programmatically generating a public and private key

Programmatically:

Main.java:

import javax.crypto.spec.*;
import java.security.*;
import javax.crypto.*;
import java.io.*;
 
public class Main
{
   public static void main(String []args) throws Exception {
      KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
      kpg.initialize(1024);
      KeyPair kp = kpg.genKeyPair();
 
      byte[] publicKey = kp.getPublic().getEncoded();
      byte[] privateKey = kp.getPrivate().getEncoded();
 
      FileOutputStream fos = new FileOutputStream("public.key");
      fos.write(publicKey);
      fos.close();
      fos = new FileOutputStream("private.key");
      fos.write(privateKey);
      fos.close();
   }
}

using keytool:

keytool -genkey -alias mykey -keysize 1024 -keyalg RSA

Formatting a number in a locale-sensitive manner

Use the class DecimalFormat. Set the pattern, a String that specifies how a number should be formatted, with applyPattern and invoke the method format which will return the formatted number.

In creating a pattern, you can use the following symbols: (from API)

0		digit
#		digit, zero shown as absent
.		placeholder for decimal separator
,		placeholder for grouping separator
E		separates mantissa and exponent for exponential formats
;		separates formats
-		default negative prefix
%		multiply by 100 and show as percentag
u2030	multiply by 1000 and show as per mille
u00a4	currency sign; replaced by currency symbol; if doubled, replaced by
		international currency symbol.  If present in a pattern, the monetary
		decimal separator is used instead of the decimal separator
X		any other characters can be used in the prefix or suffix
'		used to quote special characters in a prefix or suffix

If you specify an pattern that makes no sense, a ParseException will be thrown.

Main.java:

import java.text.*;
import java.util.*; 
     
public class Main {
   static DecimalFormat decimalFormat = (DecimalFormat)NumberFormat.getInstance();
 
   public static void main(String args[]) {
 
      double numbers[] = { 0.0, 0.123, -0.123, 1.0, -1.0, 20.5, -20.5, 123, -123, 234567 };
      String patterns[] = { "##0.##", "###,###.##", "0,000.0", "0", "####-", "$000.##", 
                            "BEF ###", "#%", "u00a4u00a4 ###", "#;(NEG!) #-" };

      for (int i=0; i<patterns.length; i++) {
         for (int j=0; j<numbers.length; j++) {
            formatDouble(patterns[i], numbers[j]);
         }
         System.out.println();
      }
   }
 
   public static void formatDouble(String pattern, double number) { 
      decimalFormat.applyPattern(pattern);
      String result = decimalFormat.format(number);
   
      columnize(""+pattern, 15);
      columnize(""+number, 15);
      columnize(result, 15);
 
      System.out.println();
   }
 
   public static void columnize(String s, int col) {
      for (int i=0; i<col-s.length(); i++) {
         System.out.print(' ');
      }
      System.out.print(s);
   }
}

outputs:

         ##0.##            0.0              0
         ##0.##          0.123           0.12
         ##0.##         -0.123          -0.12
         ##0.##            1.0              1
         ##0.##           -1.0             -1
         ##0.##           20.5           20.5
         ##0.##          -20.5          -20.5
         ##0.##          123.0            123
         ##0.##         -123.0           -123
         ##0.##       234567.0         234567
 
     ###,###.##            0.0              0
     ###,###.##          0.123           0.12
     ###,###.##         -0.123          -0.12
     ###,###.##            1.0              1
     ###,###.##           -1.0             -1
     ###,###.##           20.5           20.5
     ###,###.##          -20.5          -20.5
     ###,###.##          123.0            123
     ###,###.##         -123.0           -123
     ###,###.##       234567.0        234,567
 
        0,000.0            0.0        0,000.0
        0,000.0          0.123        0,000.1
        0,000.0         -0.123       -0,000.1
        0,000.0            1.0        0,001.0
        0,000.0           -1.0       -0,001.0
        0,000.0           20.5        0,020.5
        0,000.0          -20.5       -0,020.5
        0,000.0          123.0        0,123.0
        0,000.0         -123.0       -0,123.0
        0,000.0       234567.0      234,567.0
 
              0            0.0              0
              0          0.123              0
              0         -0.123             -0
              0            1.0              1
              0           -1.0             -1
              0           20.5             20
              0          -20.5            -20
              0          123.0            123
              0         -123.0           -123
              0       234567.0         234567
 
          ####-            0.0             0-
          ####-          0.123             0-
          ####-         -0.123            -0-
          ####-            1.0             1-
          ####-           -1.0            -1-
          ####-           20.5            20-
          ####-          -20.5           -20-
          ####-          123.0           123-
          ####-         -123.0          -123-
          ####-       234567.0        234567-
 
        $000.##            0.0           $000
        $000.##          0.123        $000.12
        $000.##         -0.123       -$000.12
        $000.##            1.0           $001
        $000.##           -1.0          -$001
        $000.##           20.5         $020.5
        $000.##          -20.5        -$020.5
        $000.##          123.0           $123
        $000.##         -123.0          -$123
        $000.##       234567.0        $234567
 
          BE###            0.0            BE0
          BE###          0.123            BE0
          BE###         -0.123           -BE0
          BE###            1.0            BE1
          BE###           -1.0           -BE1
          BE###           20.5           BE20
          BE###          -20.5          -BE20
          BE###          123.0          BE123
          BE###         -123.0         -BE123
          BE###       234567.0       BE234567
 
             #%            0.0             0%
             #%          0.123            12%
             #%         -0.123           -12%
             #%            1.0           100%
             #%           -1.0          -100%
             #%           20.5          2050%
             #%          -20.5         -2050%
             #%          123.0         12300%
             #%         -123.0        -12300%
             #%       234567.0      23456700%
  
            ###            0.0          USD 0
            ###          0.123          USD 0
            ###         -0.123         -USD 0
            ###            1.0          USD 1
            ###           -1.0         -USD 1
            ###           20.5         USD 20
            ###          -20.5        -USD 20
            ###          123.0        USD 123
            ###         -123.0       -USD 123
            ###       234567.0     USD 234567
 
    #;(NEG!) #-            0.0              0
    #;(NEG!) #-          0.123              0
    #;(NEG!) #-         -0.123      (NEG!) 0-
    #;(NEG!) #-            1.0              1
    #;(NEG!) #-           -1.0      (NEG!) 1-
    #;(NEG!) #-           20.5             20
    #;(NEG!) #-          -20.5     (NEG!) 20-
    #;(NEG!) #-          123.0            123
    #;(NEG!) #-         -123.0    (NEG!) 123-
    #;(NEG!) #-       234567.0         234567

Converting a String to a Date object

Use the class DateFormat to do the language-independent date/time formatting for you. First get an instance of DateFormat:

   DateFormat df = DateFormat.getDateInstance();

This will return the default convention formatting for the current locale. There are two more getDateInstance methods in which you can specify the style (how a date will be provided: SHORT, MEDIUM, LONG, FULL) and locale (what position the different attributes like day, month, year, take). Once you have the DateFormat instance, you can start parsing dates using the parse method.
Check out the DateFormat API for more information.

Main.java:

import java.text.*;
import java.util.*;
 
public class Main {
   public static void main(String args[]) {
      try {
         DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);
         Date myDate = df.parse("12/20/1972");
 
         System.out.println(myDate);
      }
      catch(java.text.ParseException e) { 
         System.out.println(e);
      }
   }
}

Reading the manifest from a JAR file

Create a JarFile instance and call the method getManifest. You’ll have access to all the Manifest’s attributes.

Main.java:

import java.util.jar.*;
import java.util.*;
import java.io.*;
 
public class Main
{
   public static void main(String []args) throws IOException {
      JarFile jar = new JarFile("Main.jar");
      Manifest manifest = jar.getManifest();
 
      // now you can interrogate/modify the manifest file
      Attributes attributes = manifest.getMainAttributes();
      String st = (String) attributes.get(new Attributes.Name("Specification-Title"));
      System.out.println(st);           
   }
}

Localize log messages

You can assign a ResourceBundle with a logger. If you don’t, it will inherit the ResourceBundle from its parent. The following example localizes a message based on the current locale. Comment out the first line and it will use the default locale (eg. en_US) and hence our ListResourceBundle MyLogResources.

Notice that you can set MessageFormat parameters ({0}, {1}, …) by using another form of the log method.

Main.java:

import java.util.logging.*;
import java.util.*;
import java.io.*;
 
public class Main
{
   public static void main(String argv[]) {
      Locale.setDefault(new Locale("fr", "", ""));
      Logger logger = Logger.getLogger("main", "MyLogResources");
      logger.setUseParentHandlers(false);
      logger.setLevel(Level.ALL);
 
      ConsoleHandler consoleHandler = new ConsoleHandler();
      consoleHandler.setLevel(Level.ALL);
      logger.addHandler(consoleHandler);
 
      logger.severe("SEVERE_ERROR");
      logger.log(Level.WARNING, "FILE_NOT_FOUND", new Object[] { "autoexec.bat" });
      logger.info("OTHER_INFO");
   }
}

MyLogResources.java:

import java.util.*;
 
public class MyLogResources extends ListResourceBundle {
   public Object[][] getContents() {
      return contents;
   }
 
   static final Object[][] contents = {
      {"SEVERE_ERROR",   "A severe error occurred"},
      {"FILE_NOT_FOUND", "File {0} was not found"}
   };
}

MyLogResources_fr.java:

import java.util.*;
 
public class MyLogResources_fr extends ListResourceBundle {
   public Object[][] getContents() {
      return contents;
   }
 
   static final Object[][] contents = {
      {"SEVERE_ERROR",   "Il y avait un erreur tres grave"},
      {"FILE_NOT_FOUND", "Le fichier {0} n'est pas la"}
   };
}

Running java Main outputs:

21 janv. 2002 00:02:21 Main main
GRAVE: Il y avait un erreur tres grave
21 janv. 2002 00:02:21 Main main
ATTENTION: Le fichier autoexec.bat nest pas la
21 janv. 2002 00:02:21 Main main
INFO: OTHER_INFO