Part of Slepp's ProjectsPastebinTURLImagebinFilebin
Feedback -- English French German Japanese
Create Upload Newest Tools Donate
Sign In | Create Account

Vigenere.java
Thursday, October 2nd, 2008 at 1:02:14pm MDT 

  1. /*
  2.  * Vigenere.java
  3.  * by Eric Farmer
  4.  */
  5.  
  6. /**
  7.  * This class contains methods for encrypting and decrypting using the Vigenere
  8.  * cipher.  All computation is done with arrays of character codes in the range
  9.  * 0 to 25 ('a' to 'z'); convenience methods convert between this and string
  10.  * format.
  11.  *
  12.  * @author  Eric Farmer
  13.  * @version 2002-04-30
  14.  */
  15. public class Vigenere {
  16.  
  17.     /* Contains only static methods. */
  18.  
  19.     private Vigenere() {}
  20.  
  21.     /**
  22.      * Converts the alphabetic characters in the given string to an array of
  23.      * character codes in the range 0 to 25.  All non-alphabetic characters are
  24.      * ignored.
  25.      *
  26.      * @param s the string to convert
  27.      *
  28.      * @return  the array of character codes
  29.      */
  30.     public static char[] stringToLetters(String s) {
  31.         StringBuffer buffer = new StringBuffer(s.toLowerCase());
  32.         int length = 0;
  33.         for (int i = 0; i < buffer.length(); i++) {
  34.             char ch = buffer.charAt(i);
  35.             if (Character.isLetter(ch)) {
  36.                 buffer.setCharAt(length++, ch);
  37.             }
  38.         }
  39.         buffer.setLength(length);
  40.         char[] c = buffer.toString().toCharArray();
  41.         for (int i = 0; i < c.length; i++) {
  42.             c[i] -= 'a';
  43.         }
  44.         return c;
  45.     }
  46.  
  47.     /**
  48.      * Converts the given array of character codes, in the range 0 to 25, to a
  49.      * string of corresponding lowercase alphabetic characters.
  50.      *
  51.      * @param c the array of character codes
  52.      *
  53.      * @return  the string of alphabetic characters
  54.      */
  55.     public static String lettersToString(char[] c) {
  56.         for (int i = 0; i < c.length; i++) {
  57.             c[i] += 'a';
  58.         }
  59.         String s = String.copyValueOf(c);
  60.         for (int i = 0; i < c.length; i++) {
  61.             c[i] -= 'a';
  62.         }
  63.         return s;
  64.     }
  65.  
  66.     /**
  67.      * Encrypts the given plaintext with the given key, both consisting of
  68.      * character codes in the range 0 to 25.
  69.      *
  70.      * @param plainText the text to be encrypted
  71.      * @param key       the encryption key
  72.      *
  73.      * @return          the encrypted text
  74.      */
  75.     public static char[] encrypt(char[] plainText, char[] key) {
  76.         char[] cipherText = new char[plainText.length];
  77.         for (int i = 0; i < plainText.length; i++) {
  78.             cipherText[i] = (char)((plainText[i] + key[i%key.length])%26);
  79.         }
  80.         return cipherText;
  81.     }
  82.  
  83.     /**
  84.      * Decrypts the given ciphertext with the given key, both consisting of
  85.      * character codes in the range 0 to 25.
  86.      *
  87.      * @param cipherText the text to be decrypted
  88.      * @param key        the decryption key
  89.      *
  90.      * @return           the decrypted text
  91.      */
  92.     public static char[] decrypt(char[] cipherText, char[] key) {
  93.         char[] plainText = new char[cipherText.length];
  94.         for (int i = 0; i < cipherText.length; i++) {
  95.             plainText[i] = (char)((cipherText[i] + 26 - key[i%key.length])%26);
  96.         }
  97.         return plainText;
  98.     }
  99.  
  100.     /**
  101.      * Estimates the keyword length for the given ciphertext, based on the
  102.      * index of coincidence test.  The keyword length which maximizes the
  103.      * minimum index of coincidence over all parts of the ciphertext partition
  104.      * is returned.
  105.      *
  106.      * @param cipherText the ciphertext
  107.      *
  108.      * @return           the estimated keyword length
  109.      */
  110.     public static int guessKeyLength(char[] cipherText) {
  111.         int bestKeyLength = 2;
  112.         double maxMinIndex = Double.NEGATIVE_INFINITY;
  113.         int maxKeyLength = (int)Math.sqrt(cipherText.length);
  114.  
  115.         /* Try "all" possible keyword lengths. */
  116.  
  117.         for (int keyLength = 2; keyLength <= maxKeyLength; keyLength++) {
  118.             double minIndex = Double.POSITIVE_INFINITY;
  119.  
  120.             /* Compute each index of coincidence, and find the minimum. */
  121.  
  122.             for (int offset = 0; offset < keyLength; offset++) {
  123.                 double index = indexOfCoincidence(cipherText, offset,
  124.                         keyLength);
  125.                 if (index < minIndex) {
  126.                     minIndex = index;
  127.                 }
  128.             }
  129.  
  130.             /* Select the keyword length that maximizes the minimum. */
  131.  
  132.             if (minIndex > maxMinIndex) {
  133.                 maxMinIndex = minIndex;
  134.                 bestKeyLength = keyLength;
  135.             }
  136.         }
  137.         return bestKeyLength;
  138.     }
  139.  
  140.     /**
  141.      * Estimates the keyword for the given ciphertext, given the keyword
  142.      * length, based on a combination of monoalphabetic frequency analysis and
  143.      * mutual indices of coincidence.
  144.      *
  145.      * @param cipherText the ciphertext
  146.      * @param keyLength  the keyword length
  147.      *
  148.      * @return           the estimated keyword
  149.      */
  150.     public static char[] guessKey(char[] cipherText, int keyLength) {
  151.         char[] key = new char[keyLength];
  152.         boolean[] found = new boolean[keyLength];
  153.         int lastFound = 0;
  154.  
  155.         /*
  156.          * Build the keyword one letter at a time.  Based on simple frequency
  157.          * analysis in each (monoalphabetic) block, estimate each keyletter by
  158.          * matching the most frequent ciphertext letter with plaintext 'e'.
  159.          * Select the best of these matches by "confidence," measured by the
  160.          * discrepancy between the most frequent and second most frequent
  161.          * letter.
  162.          */
  163.         int maxDiffFreq = -1;
  164.         for (int offset = 0; offset < keyLength; offset++) {
  165.             int[] bins = frequencies(cipherText, offset, keyLength);
  166.             int bestE = 0;
  167.  
  168.             /* Estimate keyletter based on plaintext 'e'. */
  169.  
  170.             for (int c = 1; c < 26; c++) {
  171.                 if (bins[c] > bins[bestE]) {
  172.                     bestE = c;
  173.                 }
  174.             }
  175.             int maxFrequency = bins[bestE];
  176.  
  177.             /* Select the keyletter we are most confident in. */
  178.  
  179.             sort(bins);
  180.             int diff = maxFrequency - bins[24];
  181.             if (diff > maxDiffFreq) {
  182.                 maxDiffFreq = diff;
  183.                 lastFound = offset;
  184.                 key[offset] = (char)((bestE + 22)%26);
  185.             }
  186.         }
  187.         found[lastFound] = true;
  188.  
  189.         /*
  190.          * Continue the incremental building of the keyword using mutual
  191.          * indices of coincidence.  Pair the most recently found keyletter with
  192.          * each other remaining keyletter, "solving" using the mutual index of
  193.          * coincidence test.  Select the best of these solutions again by
  194.          * "confidence," or discrepancy between the two highest indices.
  195.          */
  196.         for (int i = 1; i < keyLength; i++) {
  197.             int bestOffset = 0;
  198.             double maxDiffIndex = Double.NEGATIVE_INFINITY;
  199.             for (int offset = 0; offset < keyLength; offset++) {
  200.  
  201.                 /* If this keyletter isn't already known, estimate it. */
  202.  
  203.                 if (!found[offset]) {
  204.                     double[] indices = mutualIndicesOfCoincidence(cipherText,
  205.                             offset, lastFound, keyLength);
  206.                     int bestShift = 0;
  207.  
  208.                     /* Estimate keyletter based on mutual index. */
  209.  
  210.                     for (int shift = 1; shift < 26; shift++) {
  211.                         if (indices[shift] > indices[bestShift]) {
  212.                             bestShift = shift;
  213.                         }
  214.                     }
  215.                     double maxIndex = indices[bestShift];
  216.  
  217.                     /* Select the keyletter we are most confident in. */
  218.  
  219.                     sort(indices);
  220.                     double diff = maxIndex - indices[24];
  221.                     if (diff > maxDiffIndex) {
  222.                         maxDiffIndex = diff;
  223.                         bestOffset = offset;
  224.                         key[offset] = (char)((key[lastFound] + bestShift)%26);
  225.                     }
  226.                 }
  227.             }
  228.             lastFound = bestOffset;
  229.             found[lastFound] = true;
  230.         }
  231.  
  232.         /*
  233.          * At this point, we have a very good guess of the keyword.  The weak
  234.          * point seems to be not the relationships between keyletters (derived
  235.          * by the mutual indices of coincidence), but the choice from the 26
  236.          * cyclic shifts of the keyword based on frequency analysis.  We refine
  237.          * our guess by selecting the shift which maximizes the average
  238.          * frequency of guessed plaintext 'e' over all parts of the ciphertext
  239.          * partition.
  240.          */
  241.  
  242.         int[][] bins = new int[keyLength][];
  243.         for (int offset = 0; offset < keyLength; offset++) {
  244.             bins[offset] = frequencies(cipherText, offset, keyLength);
  245.         }
  246.  
  247.         int bestShift = 0,
  248.             maxFreq = -1;
  249.         for (int shift = 0; shift < 26; shift++) {
  250.             int totalFreq = 0;
  251.             for (int offset = 0; offset < keyLength; offset++) {
  252.                 totalFreq += bins[offset][(4 + key[offset] + shift)%26];
  253.             }
  254.             if (totalFreq > maxFreq) {
  255.                 maxFreq = totalFreq;
  256.                 bestShift = shift;
  257.             }
  258.         }
  259.  
  260.         for (int offset = 0; offset < keyLength; offset++) {
  261.             key[offset] = (char)((key[offset] + bestShift)%26);
  262.         }
  263.         return key;
  264.     }
  265.  
  266.     /**
  267.      * Returns the frequencies of the letters in the given subtext of the given
  268.      * ciphertext.  Element c (in the range 0 to 25) in the array that is
  269.      * returned indicates the number of occurrences of letter c in the subtext.
  270.      * The subtext is specified by the keyword length and an offset; e.g., the
  271.      * entire ciphertext is specified by offset 0 and key length 1.
  272.      *
  273.      * @param cipherText the entire ciphertext
  274.      * @param offset     the offset from the start of the ciphertext
  275.      * @param keyLength  the keyword length
  276.      *
  277.      * @return           the frequencies of the 26 ciphertext letters
  278.      */
  279.     public static int[] frequencies(char[] cipherText, int offset,
  280.                                     int keyLength) {
  281.         int[] bins = new int[26];
  282.         for (int i = offset; i < cipherText.length; i+= keyLength) {
  283.             bins[cipherText[i]]++;
  284.         }
  285.         return bins;
  286.     }
  287.  
  288.     /**
  289.      * Returns the index of coincidence of the given subtext of the given
  290.      * ciphertext.
  291.      *
  292.      * @see #frequencies
  293.      *
  294.      * @param cipherText the entire ciphertext
  295.      * @param offset     the offset from the start of the ciphertext
  296.      * @param keyLength  the keyword length
  297.      *
  298.      * @return           the index of coincidence
  299.      */
  300.     public static double indexOfCoincidence(char[] cipherText, int offset,
  301.                                             int keyLength) {
  302.         double index = 0;
  303.         int[] bins = frequencies(cipherText, offset, keyLength);
  304.         int length = 0;
  305.  
  306.         for (int c = 0; c < 26; c++) {
  307.             index += bins[c]*(bins[c] - 1);
  308.             length += bins[c];
  309.         }
  310.         return index/(length*(length - 1));
  311.     }
  312.  
  313.     /**
  314.      * Returns the mutual indices of coincidence for the given subtexts of the
  315.      * given ciphertext, one for each of 26 possible shifts.  Element c in the
  316.      * array that is returned indicates the mutual index of coincidence for the
  317.      * first subtext and the second subtext shifted by c.
  318.      *
  319.      * @see #frequencies
  320.      *
  321.      * @param cipherText the entire ciphertext
  322.      * @param offset1    the first offset from the start of the ciphertext
  323.      * @param offset2    the second offset from the start of the ciphertext
  324.      * @param keyLength  the keyword length
  325.      *
  326.      * @return           the mutual indices of coincidence
  327.      */
  328.     public static double[] mutualIndicesOfCoincidence(char[] cipherText,
  329.                                                       int offset1, int offset2,
  330.                                                       int keyLength) {
  331.         double[] indices = new double[26];
  332.  
  333.         /* Compute frequencies in each (monoalphabetic) block only once. */
  334.  
  335.         int[] bins1 = frequencies(cipherText, offset1, keyLength);
  336.         int[] bins2 = frequencies(cipherText, offset2, keyLength);
  337.         int length1 = 0, length2 = 0;
  338.         for (int c = 0; c < 26; c++) {
  339.             length1 += bins1[c];
  340.             length2 += bins2[c];
  341.         }
  342.  
  343.         /* Compute index for each possible shift. */
  344.  
  345.         for (int shift = 0; shift < 26; shift++) {
  346.             for (int c = 0; c < 26; c++) {
  347.                 indices[shift] += bins1[(c + shift)%26]*bins2[c];
  348.             }
  349.             indices[shift] /= length1*length2;
  350.         }
  351.         return indices;
  352.     }
  353.  
  354.     /* Sorting is not built in to Java 1.1 */
  355.  
  356.     private static void sort(int[] a) {
  357.         for (int i = 0; i < a.length - 1; i++) {
  358.             for (int j = i + 1; j < a.length; j++) {
  359.                 if (a[i] > a[j]) {
  360.                     int temp = a[i];
  361.                     a[i] = a[j];
  362.                     a[j] = temp;
  363.                 }
  364.             }
  365.         }
  366.     }
  367.  
  368.     private static void sort(double[] a) {
  369.         for (int i = 0; i < a.length - 1; i++) {
  370.             for (int j = i + 1; j < a.length; j++) {
  371.                 if (a[i] > a[j]) {
  372.                     double temp = a[i];
  373.                     a[i] = a[j];
  374.                     a[j] = temp;
  375.                 }
  376.             }
  377.         }
  378.     }
  379. }

advertising

Update the Post

Either update this post and resubmit it with changes, or make a new post.

You may also comment on this post.

update paste below
details of the post (optional)

Note: Only the paste content is required, though the following information can be useful to others.

Save name / title?

(space separated, optional)



Please note that information posted here will expire by default in one month. If you do not want it to expire, please set the expiry time above. If it is set to expire, web search engines will not be allowed to index it prior to it expiring. Items that are not marked to expire will be indexable by search engines. Be careful with your passwords. All illegal activities will be reported and any information will be handed over to the authorities, so be good.

worth-right
worth-right