CipherDES.java - A Java Implementation of DES

This section provides a tutorial Java program, CipherDES.java - A Java Implementation of DES encryption and decryption algorithm.

Merging the illustration programs from the previous chapter together, I got the following simple Java implementation of the DES algorithm, CipherDES.java:

/* CipherDES.java
#- Copyright (c) 2013, HerongYang.com, All Rights Reserved.
 */
package herong;
import java.io.*;
public class CipherDES {
   public static void main(String[] a) {
      if (a.length<4) {
         System.out.println("Usage:");
         System.out.println("java DesCipher encrypt/decrypt keyFile"
            +" msgFile cphFile");
         return;
      }
      String mode = a[0];
      String keyFile = a[1];
      String msgFile = a[2];
      String cphFile = a[3];
      try {
         byte[] theKey = readBytes(keyFile);
         byte[][] subKeys = getSubkeys(theKey);
         byte[] theMsg = readBytes(msgFile);
         byte[] theCph = cipher(theMsg,subKeys,mode);
         writeBytes(theCph,cphFile);
         printBytes(theKey,"Key block");
         printBytes(theMsg,"Input block");    
         printBytes(theCph,"Output block");    
      } catch (Exception e) {
         e.printStackTrace();
         return;
      }
   }
   static final int[] IP = {
      58, 50, 42, 34, 26, 18, 10, 2,
      60, 52, 44, 36, 28, 20, 12, 4,
      62, 54, 46, 38, 30, 22, 14, 6,
      64, 56, 48, 40, 32, 24, 16, 8,
      57, 49, 41, 33, 25, 17,  9, 1,
      59, 51, 43, 35, 27, 19, 11, 3,
      61, 53, 45, 37, 29, 21, 13, 5,
      63, 55, 47, 39, 31, 23, 15, 7
   };
   static final int[] E = {
      32,  1,  2,  3,  4,  5,
       4,  5,  6,  7,  8,  9,
       8,  9, 10, 11, 12, 13,
      12, 13, 14, 15, 16, 17,
      16, 17, 18, 19, 20, 21,
      20, 21, 22, 23, 24, 25,
      24, 25, 26, 27, 28, 29,
      28, 29, 30, 31, 32,  1
   };
   static final int[] P = {
      16,  7, 20, 21,
      29, 12, 28, 17,
       1, 15, 23, 26,
       5, 18, 31, 10,
       2,  8, 24, 14,
      32, 27,  3,  9,
      19, 13, 30,  6,
      22, 11,  4, 25
   };
   static final int[] INVP = {
      40, 8, 48, 16, 56, 24, 64, 32,
      39, 7, 47, 15, 55, 23, 63, 31,
      38, 6, 46, 14, 54, 22, 62, 30,
      37, 5, 45, 13, 53, 21, 61, 29,
      36, 4, 44, 12, 52, 20, 60, 28,
      35, 3, 43, 11, 51, 19, 59, 27,
      34, 2, 42, 10, 50, 18, 58, 26,
      33, 1, 41,  9, 49, 17, 57, 25
   };
   public static byte[] cipher(byte[] theMsg, byte[][] subKeys, 
      String mode) throws Exception {
      if (theMsg.length<8) 
         throw new Exception("Message is less than 64 bits.");
      theMsg = selectBits(theMsg,IP); // Initial Permutation
      int blockSize = IP.length;
      byte[] l = selectBits(theMsg,0,blockSize/2);
      byte[] r = selectBits(theMsg,blockSize/2,blockSize/2);
      int numOfSubKeys = subKeys.length;
      for (int k=0; k<numOfSubKeys; k++) {
      	 byte[] rBackup = r;
         r = selectBits(r,E); // Expansion
         if (mode.equalsIgnoreCase("encrypt")) 
            r = doXORBytes(r,subKeys[k]); // XOR with the sub key
         else 
            r = doXORBytes(r,subKeys[numOfSubKeys-k-1]);
         r = substitution6x4(r); // Substitution
         r = selectBits(r,P); // Permutation
         r = doXORBytes(l,r); // XOR with the previous left half
         l = rBackup; // Taking the previous right half
      }
      byte[] lr = concatenateBits(r,blockSize/2,l,blockSize/2);
      lr = selectBits(lr,INVP); // Inverse Permutation
      return lr;
   } 
   private static byte[] doXORBytes(byte[] a, byte[] b) {
      byte[] out = new byte[a.length];
      for (int i=0; i<a.length; i++) {
         out[i] = (byte) (a[i] ^ b[i]);
      }
      return out;
   }
   static final int[] S = {
14,  4, 13,  1,  2, 15, 11,  8,  3, 10,  6, 12,  5,  9,  0,  7, // S1
 0, 15,  7,  4, 14,  2, 13,  1, 10,  6, 12, 11,  9,  5,  3,  8,
 4,  1, 14,  8, 13,  6,  2, 11, 15, 12,  9,  7,  3, 10,  5,  0,
15, 12,  8,  2,  4,  9,  1,  7,  5, 11,  3, 14, 10,  0,  6, 13,
15,  1,  8, 14,  6, 11,  3,  4,  9,  7,  2, 13, 12,  0,  5, 10, // S2
 3, 13,  4,  7, 15,  2,  8, 14, 12,  0,  1, 10,  6,  9, 11,  5,
 0, 14,  7, 11, 10,  4, 13,  1,  5,  8, 12,  6,  9,  3,  2, 15,
13,  8, 10,  1,  3, 15,  4,  2, 11,  6,  7, 12,  0,  5, 14,  9,
10,  0,  9, 14,  6,  3, 15,  5,  1, 13, 12,  7, 11,  4,  2,  8, // S3
13,  7,  0,  9,  3,  4,  6, 10,  2,  8,  5, 14, 12, 11, 15,  1,
13,  6,  4,  9,  8, 15,  3,  0, 11,  1,  2, 12,  5, 10, 14,  7,
 1, 10, 13,  0,  6,  9,  8,  7,  4, 15, 14,  3, 11,  5,  2, 12,
 7, 13, 14,  3,  0,  6,  9, 10,  1,  2,  8,  5, 11, 12,  4, 15, // S4
13,  8, 11,  5,  6, 15,  0,  3,  4,  7,  2, 12,  1, 10, 14,  9,
10,  6,  9,  0, 12, 11,  7, 13, 15,  1,  3, 14,  5,  2,  8,  4,
 3, 15,  0,  6, 10,  1, 13,  8,  9,  4,  5, 11, 12,  7,  2, 14,
 2, 12,  4,  1,  7, 10, 11,  6,  8,  5,  3, 15, 13,  0, 14,  9, // S5
14, 11,  2, 12,  4,  7, 13,  1,  5,  0, 15, 10,  3,  9,  8,  6,
 4,  2,  1, 11, 10, 13,  7,  8, 15,  9, 12,  5,  6,  3,  0, 14,
11,  8, 12,  7,  1, 14,  2, 13,  6, 15,  0,  9, 10,  4,  5,  3,
12,  1, 10, 15,  9,  2,  6,  8,  0, 13,  3,  4, 14,  7,  5, 11, // S6
10, 15,  4,  2,  7, 12,  9,  5,  6,  1, 13, 14,  0, 11,  3,  8,
 9, 14, 15,  5,  2,  8, 12,  3,  7,  0,  4, 10,  1, 13, 11,  6,
 4,  3,  2, 12,  9,  5, 15, 10, 11, 14,  1,  7,  6,  0,  8, 13,
 4, 11,  2, 14, 15,  0,  8, 13,  3, 12,  9,  7,  5, 10,  6,  1, // S7
13,  0, 11,  7,  4,  9,  1, 10, 14,  3,  5, 12,  2, 15,  8,  6,
 1,  4, 11, 13, 12,  3,  7, 14, 10, 15,  6,  8,  0,  5,  9,  2,
 6, 11, 13,  8,  1,  4, 10,  7,  9,  5,  0, 15, 14,  2,  3, 12,
13,  2,  8,  4,  6, 15, 11,  1, 10,  9,  3, 14,  5,  0, 12,  7, // S8
 1, 15, 13,  8, 10,  3,  7,  4, 12,  5,  6, 11,  0, 14,  9,  2,
 7, 11,  4,  1,  9, 12, 14,  2,  0,  6, 10, 13, 15,  3,  5,  8,
 2,  1, 14,  7,  4, 10,  8, 13, 15, 12,  9,  0,  3,  5,  6, 11
   };   
   private static byte[] substitution6x4(byte[] in) {
      in = splitBytes(in,6); // Splitting byte[] into 6-bit blocks
      byte[] out = new byte[in.length/2];
      int lhByte = 0;
      for (int b=0; b<in.length; b++) { // Should be sub-blocks
         byte valByte = in[b];
         int r = 2*(valByte>>7&0x0001)+(valByte>>2&0x0001); // 1 and 6
         int c = valByte>>3&0x000F; // Middle 4 bits
         int hByte = S[64*b+16*r+c]; // 4 bits (half byte) output
         if (b%2==0) lhByte = hByte; // Left half byte
         else out[b/2] = (byte) (16*lhByte + hByte);
      }
      return out;
   }
   private static byte[] splitBytes(byte[] in, int len) {
      int numOfBytes = (8*in.length-1)/len + 1;
      byte[] out = new byte[numOfBytes];
      for (int i=0; i<numOfBytes; i++) {
         for (int j=0; j<len; j++) {
            int val = getBit(in, len*i+j); 
            setBit(out,8*i+j,val);
         }
      }
      return out;
   }
   static final int[] PC1 = {
      57, 49, 41, 33, 25, 17,  9,
       1, 58, 50, 42, 34, 26, 18,
      10,  2, 59, 51, 43, 35, 27,
      19, 11,  3, 60, 52, 44, 36,
      63, 55, 47, 39, 31, 23, 15,
       7, 62, 54, 46, 38, 30, 22,
      14,  6, 61, 53, 45, 37, 29,
      21, 13,  5, 28, 20, 12,  4
   };
   static final int[] PC2 = {
      14, 17, 11, 24,  1,  5,
       3, 28, 15,  6, 21, 10,
      23, 19, 12,  4, 26,  8,
      16,  7, 27, 20, 13,  2,
      41, 52, 31, 37, 47, 55,
      30, 40, 51, 45, 33, 48,
      44, 49, 39, 56, 34, 53,
      46, 42, 50, 36, 29, 32
   };
   static final int[] SHIFTS = {
      1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
   };
   public static byte[][] getSubkeys(byte[] theKey)
      throws Exception {
      int activeKeySize = PC1.length;
      int numOfSubKeys = SHIFTS.length;
      byte[] activeKey = selectBits(theKey,PC1);
      int halfKeySize = activeKeySize/2;
      byte[] c = selectBits(activeKey,0,halfKeySize);
      byte[] d = selectBits(activeKey,halfKeySize,halfKeySize);
      byte[][] subKeys = new byte[numOfSubKeys][];
      for (int k=0; k<numOfSubKeys; k++) {
         c = rotateLeft(c,halfKeySize,SHIFTS[k]);
         d = rotateLeft(d,halfKeySize,SHIFTS[k]);
         byte[] cd = concatenateBits(c,halfKeySize,d,halfKeySize);
         subKeys[k] = selectBits(cd,PC2);
      }
      return subKeys;
   }
   private static byte[] rotateLeft(byte[] in, int len, int step) {
      int numOfBytes = (len-1)/8 + 1;
      byte[] out = new byte[numOfBytes];
      for (int i=0; i<len; i++) {
         int val = getBit(in,(i+step)%len);
         setBit(out,i,val);
      }
      return out;
   }
   private static byte[] concatenateBits(byte[] a, int aLen, byte[] b, 
      int bLen) {
      int numOfBytes = (aLen+bLen-1)/8 + 1;
      byte[] out = new byte[numOfBytes];
      int j = 0;
      for (int i=0; i<aLen; i++) {
         int val = getBit(a,i);
         setBit(out,j,val);
         j++;
      }
      for (int i=0; i<bLen; i++) {
         int val = getBit(b,i);
         setBit(out,j,val);
         j++;
      }
      return out;
   }
   private static byte[] selectBits(byte[] in, int pos, int len) {
      int numOfBytes = (len-1)/8 + 1;
      byte[] out = new byte[numOfBytes];
      for (int i=0; i<len; i++) {
         int val = getBit(in,pos+i);
         setBit(out,i,val);
      }
      return out;
   }
   private static byte[] selectBits(byte[] in, int[] map) {
      int numOfBytes = (map.length-1)/8 + 1;
      byte[] out = new byte[numOfBytes];
      for (int i=0; i<map.length; i++) {
         int val = getBit(in,map[i]-1);
         setBit(out,i,val);
      }
      return out;
   }
   private static int getBit(byte[] data, int pos) {
      int posByte = pos/8; 
      int posBit = pos%8;
      byte valByte = data[posByte];
      int valInt = valByte>>(8-(posBit+1)) & 0x0001;
      return valInt;
   }
   private static void setBit(byte[] data, int pos, int val) {
      int posByte = pos/8; 
      int posBit = pos%8;
      byte oldByte = data[posByte];
      oldByte = (byte) (((0xFF7F>>posBit) & oldByte) & 0x00FF);
      byte newByte = (byte) ((val<<(8-(posBit+1))) | oldByte);
      data[posByte] = newByte;
   }
   private static byte[] readBytes(String in) throws Exception {
      FileInputStream fis = new FileInputStream(in);
      int numOfBytes = fis.available();
      byte[] buffer = new byte[numOfBytes];
      fis.read(buffer);
      fis.close();
      return buffer;
   }
   private static void writeBytes(byte[] data, String out)
      throws Exception {
      FileOutputStream fos = new FileOutputStream(out);
      fos.write(data);
      fos.close();
   }
   private static void printBytes(byte[] data, String name) {
      System.out.println("");
      System.out.println(name+":");
      for (int i=0; i<data.length; i++) {
         System.out.print(byteToBits(data[i])+" ");
      }
      System.out.println();
   }
   private static String byteToBits(byte b) {
      StringBuffer buf = new StringBuffer();
      for (int i=0; i<8; i++)
         buf.append((int)(b>>(8-(i+1)) & 0x0001));
      return buf.toString();
   }
}

If you run this program with JDK 1.4.1 with test key and clear text block mentioned in the previous chapter, you will get:

java -cp . CipherDES encrypt TestKey.des TestMsg.clr TestMsg.cph

Key block:
00010011 00110100 01010111 01111001 10011011 10111100 11011111 11110001

Input block:
00000001 00100011 01000101 01100111 10001001 10101011 11001101 11101111

Output block:
10000101 11101000 00010011 01010100 00001111 00001010 10110100 00000101

java -cp . CipherDES decrypt TestKey.des TestMsg.cph TestMsg.bck

Key block:
00010011 00110100 01010111 01111001 10011011 10111100 11011111 11110001

Input block:
10000101 11101000 00010011 01010100 00001111 00001010 10110100 00000101

Output block:
00000001 00100011 01000101 01100111 10001001 10101011 11001101 11101111

As you can see from the output, the program works correctly.

Table of Contents

 About This Book

 Cryptography Terminology

 Cryptography Basic Concepts

 Introduction to AES (Advanced Encryption Standard)

 Introduction to DES Algorithm

 DES Algorithm - Illustrated with Java Programs

DES Algorithm Java Implementation

CipherDES.java - A Java Implementation of DES

 Java Implementation of DES - Test Cases

 DES Algorithm - Java Implementation in JDK JCE

 DES Encryption Operation Modes

 DES in Stream Cipher Modes

 PHP Implementation of DES - mcrypt

 Blowfish - 8-Byte Block Cipher

 Secret Key Generation and Management

 Cipher - Secret Key Encryption and Decryption

 Introduction of RSA Algorithm

 RSA Implementation using java.math.BigInteger Class

 Introduction of DSA (Digital Signature Algorithm)

 Java Default Implementation of DSA

 Private key and Public Key Pair Generation

 PKCS#8/X.509 Private/Public Encoding Standards

 Cipher - Public Key Encryption and Decryption

 MD5 Mesasge Digest Algorithm

 SHA1 Mesasge Digest Algorithm

 OpenSSL Introduction and Installation

 OpenSSL Generating and Managing RSA Keys

 OpenSSL Managing Certificates

 OpenSSL Generating and Signing CSR

 OpenSSL Validating Certificate Path

 "keytool" and "keystore" from JDK

 "OpenSSL" Signing CSR Generated by "keytool"

 Migrating Keys from "keystore" to "OpenSSL" Key Files

 Certificate X.509 Standard and DER/PEM Formats

 Migrating Keys from "OpenSSL" Key Files to "keystore"

 Using Certificates in IE (Internet Explorer)

 Using Certificates in Firefox

 Using Certificates in Google Chrome

 Outdated Tutorials

 References

 PDF Printing Version