1. 程式人生 > >Java密碼學原型演算法實現——第一部分:標準Hash演算法

Java密碼學原型演算法實現——第一部分:標準Hash演算法

題注

從部落格中看出來我是個比較鍾愛Java的應用密碼學研究者。雖然C在密碼學中有不可替代的優勢:速度快,但是,Java的可移植性使得開發人員可以很快地將程式碼移植到各個平臺,這比C實現要方便的多。尤其是Android平臺的出現,Java的應用也就越來越廣。因此,我本人在密碼學研究過程中實際上也在逐漸使用和封裝一些知名的Java密碼學庫,主要是方便自己使用。

Java JDK實際上自帶了密碼學庫,支援幾乎所有通用密碼學原型的實現。然而,自帶密碼庫有幾個缺點:第一,由於版權問題,其並不支援全部的密碼學原型,如256位的AES加密等等。第二,Java JDK的使用並非特別的方便。因此,我現在基本上在使用另一個知名的Java密碼學庫:Bouncy Castle,其官方網址為:http://www.bouncycastle.org/java.html。

但是,現在網上對於Bouncy Castle的介紹實在是有點少,而且每個人寫的程式碼都不太一樣,絕大多數人共享的程式碼只是為了個人使用,複用性並不是很強。因此,我現在在逐漸編寫一些複用性比較強的封裝,從而方便在自己設計的公鑰加密、簽名等體制中呼叫。

第一部分的實現是標準Hash演算法。這一實現使用的是Java JDK,但是使用了Bouncy Castle的工具庫實現Byte和String的一些轉換。

一些Util封裝

在進入正題前,我先介紹一下自己引用或者實現的一些Util封裝,這些封裝會方便我進行下一步的實現。不管使用的方便與否,各位看官湊合著看,湊合著用,如果有任何可以改進的地方還請給我發郵件,我的郵箱是

[email protected]

In.java

這個In.java,包括下面的Out.java、StdIn.java以及StdOut.java都是從Princeton的演算法公開課中使用的庫函式。他們對Java的原始輸入輸出進行了進一步的封裝,我個人認為非常好用。當然了,在實現過程中大家也可以使用JDK自帶的函式庫。Princeton的開發者們實際上對於其庫函式封裝了一個統一的jar檔案,稱為stdlib.jar。大家可以直接在工程中引用這個jar檔案,或者下載原始碼並放置在工程中。我是選擇了後者,這主要是為了方便工程向Android中的移植,避免了jar檔案引用過程中可能出現的一些錯誤。所有的原始碼可以從下面的連結中下載到:http://algs4.cs.princeton.edu/home/。為了方便使用,在此我附上程式碼,但是注意這些程式碼的原始版權為Princeton大學。

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.net.URL;
import java.net.URLConnection;
import java.util.Locale;
import java.util.Scanner;


/**
 *  <i>Input</i>. This class provides methods for reading strings
 *  and numbers from standard input, file input, URL, and socket.
 *  <p>
 *  The Locale used is: language = English, country = US. This is consistent
 *  with the formatting conventions with Java floating-point literals,
 *  command-line arguments (via <tt>Double.parseDouble()</tt>)
 *  and standard output (via <tt>System.out.print()</tt>). It ensures that
 *  standard input works the number formatting used in the textbook.
 *  <p>
 *  For additional documentation, see <a href="http://introcs.cs.princeton.edu/31datatype">Section 3.1</a> of
 *  <i>Introduction to Programming in Java: An Interdisciplinary Approach</i> by Robert Sedgewick and Kevin Wayne.
 */
public final class In {
    private Scanner scanner;

    // assume Unicode UTF-8 encoding
    private final static String charsetName = "UTF-8";

    // private final static String charsetName = "ISO-8859-1";

    // assume language = English, country = US for consistency with System.out.
    private Locale usLocale = new Locale("en", "US");

   /**
     * Create an input stream for standard input.
     */
    public In() {
        scanner = new Scanner(new BufferedInputStream(System.in), charsetName);
        scanner.useLocale(usLocale);
    }

   /**
     * Create an input stream from a socket.
     */
    public In(Socket socket) {
        try {
            InputStream is = socket.getInputStream();
            scanner = new Scanner(new BufferedInputStream(is), charsetName);
            scanner.useLocale(usLocale);
        }
        catch (IOException ioe) {
            System.err.println("Could not open " + socket);
        }
    }

   /**
     * Create an input stream from a URL.
     */
    public In(URL url) {
        try {
            URLConnection site = url.openConnection();
            InputStream is     = site.getInputStream();
            scanner            = new Scanner(new BufferedInputStream(is), charsetName);
            scanner.useLocale(usLocale);
        }
        catch (IOException ioe) {
            System.err.println("Could not open " + url);
        }
    }

   /**
     * Create an input stream from a file.
     */
    public In(File file) {

        try {
            scanner = new Scanner(file, charsetName);
            scanner.useLocale(usLocale);
        }
        catch (IOException ioe) {
            System.err.println("Could not open " + file);
        }
    }


   /**
     * Create an input stream from a filename or web page name.
     */
    public In(String s) {

        try {
            // first try to read file from local file system
            File file = new File(s);
            if (file.exists()) {
                scanner = new Scanner(file, charsetName);
                scanner.useLocale(usLocale);
                return;
            }

            // next try for files included in jar
            URL url = getClass().getResource(s);

            // or URL from web
            if (url == null) { url = new URL(s); }

            URLConnection site = url.openConnection();
            InputStream is     = site.getInputStream();
            scanner            = new Scanner(new BufferedInputStream(is), charsetName);
            scanner.useLocale(usLocale);
        }
        catch (IOException ioe) {
            System.err.println("Could not open " + s);
        }
    }

   /**
     * Does the input stream exist?
     */
    public boolean exists()  {
        return scanner != null;
    }

   /**
     * Is the input stream empty?
     */
    public boolean isEmpty() {
        return !scanner.hasNext();
    }

   /**
     * Does the input stream have a next line?
     */
    public boolean hasNextLine() {
        return scanner.hasNextLine();
    }

   /**
     * Read and return the next line.
     */
    public String readLine() {
        String line;
        try                 { line = scanner.nextLine(); }
        catch (Exception e) { line = null;               }
        return line;
    }

   /**
     * Read and return the next character.
     */
    public char readChar() {
        // (?s) for DOTALL mode so . matches any character, including a line termination character
        // 1 says look only one character ahead
        // consider precompiling the pattern
        String s = scanner.findWithinHorizon("(?s).", 1);
        return s.charAt(0);
    }



    // return rest of input as string
   /**
     * Read and return the remainder of the input as a string.
     */
    public String readAll() {
        if (!scanner.hasNextLine()) { return null; }

        // reference: http://weblogs.java.net/blog/pat/archive/2004/10/stupid_scanner_1.html
        return scanner.useDelimiter("\\A").next();
    }



   /**
     * Return the next string from the input stream.
     */
    public String  readString() {
        return scanner.next();
    }

   /**
     * Return the next int from the input stream.
     */
    public int readInt() {
        return scanner.nextInt();
    }

   /**
     * Return the next double from the input stream.
     */
    public double readDouble() {
        return scanner.nextDouble();
    }

   /**
     * Return the next float from the input stream.
     */
    public double readFloat() {
        return scanner.nextFloat();
    }

   /**
     * Return the next long from the input stream.
     */
    public long readLong() {
        return scanner.nextLong();
    }

   /**
     * Return the next byte from the input stream.
     */
    public byte readByte() {
        return scanner.nextByte();
    }


   /**
     * Return the next boolean from the input stream, allowing "true" or "1"
     * for true and "false" or "0" for false.
     */
    public boolean readBoolean() {
        String s = readString();
        if (s.equalsIgnoreCase("true"))  return true;
        if (s.equalsIgnoreCase("false")) return false;
        if (s.equals("1"))               return true;
        if (s.equals("0"))               return false;
        throw new java.util.InputMismatchException();
    }

   /**
     * Read ints from file
     */
    public static int[] readInts(String filename) {
        In in = new In(filename);
        String[] fields = in.readAll().trim().split("\\s+");
        int[] vals = new int[fields.length];
        for (int i = 0; i < fields.length; i++)
            vals[i] = Integer.parseInt(fields[i]);
        return vals;
    }

   /**
     * Read doubles from file
     */
    public static double[] readDoubles(String filename) {
        In in = new In(filename);
        String[] fields = in.readAll().trim().split("\\s+");
        double[] vals = new double[fields.length];
        for (int i = 0; i < fields.length; i++)
            vals[i] = Double.parseDouble(fields[i]);
        return vals;
    }

   /**
     * Read strings from a file
     */
    public static String[] readStrings(String filename) {
        In in = new In(filename);
        String[] fields = in.readAll().trim().split("\\s+");
        return fields;
    }

   /**
     * Read ints from standard input
     */
    public static int[] readInts() {
        In in = new In();
        String[] fields = in.readAll().trim().split("\\s+");
        int[] vals = new int[fields.length];
        for (int i = 0; i < fields.length; i++)
            vals[i] = Integer.parseInt(fields[i]);
        return vals;
    }

   /**
     * Read doubles from standard input
     */
    public static double[] readDoubles() {
        In in = new In();
        String[] fields = in.readAll().trim().split("\\s+");
        double[] vals = new double[fields.length];
        for (int i = 0; i < fields.length; i++)
            vals[i] = Double.parseDouble(fields[i]);
        return vals;
    }

   /**
     * Read strings from standard input
     */
    public static String[] readStrings() {
        In in = new In();
        String[] fields = in.readAll().trim().split("\\s+");
        return fields;
    }

   /**
     * Close the input stream.
     */
    public void close() { scanner.close();  }



   /**
     * Test client.
     */
    public static void main(String[] args) {
        In in;
        String urlName = "http://introcs.cs.princeton.edu/stdlib/InTest.txt";

        // read from a URL
        System.out.println("readAll() from URL " + urlName);
        System.out.println("---------------------------------------------------------------------------");
        try {
            in = new In(urlName);
            System.out.println(in.readAll());
        }
        catch (Exception e) { System.out.println(e); }
        System.out.println();

        // read one line at a time from URL
        System.out.println("readLine() from URL " + urlName);
        System.out.println("---------------------------------------------------------------------------");
        try {
            in = new In(urlName);
            while (!in.isEmpty()) {
                String s = in.readLine();
                System.out.println(s);
            }
        }
        catch (Exception e) { System.out.println(e); }
        System.out.println();

        // read one string at a time from URL
        System.out.println("readString() from URL " + urlName);
        System.out.println("---------------------------------------------------------------------------");
        try {
            in = new In(urlName);
            while (!in.isEmpty()) {
                String s = in.readString();
                System.out.println(s);
            }
        }
        catch (Exception e) { System.out.println(e); }
        System.out.println();


        // read one line at a time from file in current directory
        System.out.println("readLine() from current directory");
        System.out.println("---------------------------------------------------------------------------");
        try {
            in = new In("./InTest.txt");
            while (!in.isEmpty()) {
                String s = in.readLine();
                System.out.println(s);
            }
        }
        catch (Exception e) { System.out.println(e); }
        System.out.println();


        // read one line at a time from file using relative path
        System.out.println("readLine() from relative path");
        System.out.println("---------------------------------------------------------------------------");
        try {
            in = new In("../stdlib/InTest.txt");
            while (!in.isEmpty()) {
                String s = in.readLine();
                System.out.println(s);
            }
        }
        catch (Exception e) { System.out.println(e); }
        System.out.println();

        // read one char at a time
        System.out.println("readChar() from file");
        System.out.println("---------------------------------------------------------------------------");
        try {
            in = new In("InTest.txt");
            while (!in.isEmpty()) {
                char c = in.readChar();
                System.out.print(c);
            }
        }
        catch (Exception e) { System.out.println(e); }
        System.out.println();
        System.out.println();

        // read one line at a time from absolute OS X / Linux path
        System.out.println("readLine() from absolute OS X / Linux path");
        System.out.println("---------------------------------------------------------------------------");
        in = new In("/n/fs/csweb/introcs/stdlib/InTest.txt");
        try {
            while (!in.isEmpty()) {
                String s = in.readLine();
                System.out.println(s);
            }
        }
        catch (Exception e) { System.out.println(e); }
        System.out.println();


        // read one line at a time from absolute Windows path
        System.out.println("readLine() from absolute Windows path");
        System.out.println("---------------------------------------------------------------------------");
        try {
            in = new In("G:\\www\\introcs\\stdlib\\InTest.txt");
            while (!in.isEmpty()) {
                String s = in.readLine();
                System.out.println(s);
            }
            System.out.println();
        }
        catch (Exception e) { System.out.println(e); }
        System.out.println();

    }

}



Out.java

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Locale;

/**
 *  This class provides methods for writing strings and numbers to
 *  various output streams, including standard output, file, and sockets.
 *  <p>
 *  For additional documentation, see
 *  <a href="http://introcs.cs.princeton.edu/31datatype">Section 3.1</a> of
 *  <i>Introduction to Programming in Java: An Interdisciplinary Approach</i>
 *  by Robert Sedgewick and Kevin Wayne.
 */
public class Out {

    // force Unicode UTF-8 encoding; otherwise it's system dependent
    private static String charsetName = "UTF-8";

    // assume language = English, country = US for consistency with In
    private static final Locale US_LOCALE = new Locale("en", "US");

    private PrintWriter out;

   /**
     * Create an Out object using an OutputStream.
     */
    public Out(OutputStream os) {
        try {
            OutputStreamWriter osw = new OutputStreamWriter(os, charsetName);
            out = new PrintWriter(osw, true);
        }
        catch (IOException e) { e.printStackTrace(); }
    }

   /**
     * Create an Out object using standard output.
     */
    public Out() { this(System.out); }

   /**
     * Create an Out object using a Socket.
     */
    public Out(Socket socket) {
        try {
            OutputStream os = socket.getOutputStream();
            OutputStreamWriter osw = new OutputStreamWriter(os, charsetName);
            out = new PrintWriter(osw, true);
        }
        catch (IOException e) { e.printStackTrace(); }
    }

   /**
     * Create an Out object using a file specified by the given name.
     */
    public Out(String s) {
        try {
            OutputStream os = new FileOutputStream(s);
            OutputStreamWriter osw = new OutputStreamWriter(os, charsetName);
            out = new PrintWriter(osw, true);
        }
        catch (IOException e) { e.printStackTrace(); }
    }

   /**
     * Close the output stream.
     */
    public void close() { out.close(); }



   /**
     * Terminate the line.
     */
    public void println() {
        out.println();
    }

   /**
     * Print an object and then terminate the line.
     */
    public void println(Object x) {
        out.println(x);
    }

   /**
     * Print a boolean and then terminate the line.
     */
    public void println(boolean x) {
        out.println(x);
    }

   /**
     * Print a char and then terminate the line.
     */
    public void println(char x) {
        out.println(x);
    }

   /**
     * Print an double and then terminate the line.
     */
    public void println(double x) {
        out.println(x);
    }

   /**
     * Print a float and then terminate the line.
     */
    public void println(float x) {
        out.println(x);
    }

   /**
     * Print an int and then terminate the line.
     */
    public void println(int x) {
        out.println(x);
    }

   /**
     * Print a long and then terminate the line.
     */
    public void println(long x) {
        out.println(x);
    }

   /**
     * Print a byte and then terminate the line.
     */
    public void println(byte x) {
        out.println(x);
    }



   /**
     * Flush the output stream.
     */
    public void print() {
        out.flush();
    }

   /**
     * Print an object and then flush the output stream.
     */
    public void print(Object x) {
        out.print(x);
        out.flush();
    }

   /**
     * Print an boolean and then flush the output stream.
     */
    public void print(boolean x) {
        out.print(x);
        out.flush();
    }

   /**
     * Print an char and then flush the output stream.
     */
    public void print(char x) {
        out.print(x);
        out.flush();
    }

   /**
     * Print an double and then flush the output stream.
     */
    public void print(double x) {
        out.print(x);
        out.flush();
    }

   /**
     * Print a float and then flush the output stream.
     */
    public void print(float x) {
        out.print(x);
        out.flush();
    }

   /**
     * Print an int and then flush the output stream.
     */
    public void print(int x) {
        out.print(x);
        out.flush();
    }

   /**
     * Print a long and then flush the output stream.
     */
    public void print(long x) {
        out.print(x);
        out.flush();
    }

   /**
     * Print a byte and then flush the output stream.
     */
    public void print(byte x) {
        out.print(x);
        out.flush();
    }

   /**
     * Print a formatted string using the specified format string and arguments,
     * and then flush the output stream.
     */
    public void printf(String format, Object... args) {
        out.printf(US_LOCALE, format, args);
        out.flush();
    }

   /**
     * Print a formatted string using the specified locale, format string and arguments,
     * and then flush the output stream.
     */
    public void printf(Locale locale, String format, Object... args) {
        out.printf(locale, format, args);
        out.flush();
    }


   /**
     * A test client.
     */
    public static void main(String[] args) {
        Out out;

        // write to stdout
        out = new Out();
        out.println("Test 1");
        out.close();

        // write to a file
        out = new Out("test.txt");
        out.println("Test 2");
        out.close();
    }

}


StdIn.java

import java.io.BufferedInputStream;
import java.util.Locale;
import java.util.Scanner;

/**
 *  <i>Standard input</i>. This class provides methods for reading strings
 *  and numbers from standard input.
 *  <p>
 *  The Locale used is: language = English, country = US. This is consistent
 *  with the formatting conventions with Java floating-point literals,
 *  command-line arguments (via <tt>Double.parseDouble()</tt>)
 *  and standard output (via <tt>System.out.print()</tt>). It ensures that
 *  standard input works with the input files used in the textbook.
 *  <p>
 *  For additional documentation, see <a href="http://introcs.cs.princeton.edu/15inout">Section 1.5</a> of
 *  <i>Introduction to Programming in Java: An Interdisciplinary Approach</i> by Robert Sedgewick and Kevin Wayne.
 */
public final class StdIn {

    // assume Unicode UTF-8 encoding
    private static String charsetName = "UTF-8";

    // assume language = English, country = US for consistency with System.out.
    private static Locale usLocale = new Locale("en", "US");

    // the scanner object
    private static Scanner scanner = new Scanner(new BufferedInputStream(System.in), charsetName);

    // static initializer
    static { scanner.useLocale(usLocale); }

    // don't instantiate
    private StdIn() { }


    /**
     * Is there only whitespace left on standard input?
     */
    public static boolean isEmpty() {
        return !scanner.hasNext();
    }

    /**
     * Return next string from standard input
     */
    public static String readString() {
        return scanner.next();
    }

    /**
     * Return next int from standard input
     */
    public static int readInt() {
        return scanner.nextInt();
    }

    /**
     * Return next double from standard input
     */
    public static double readDouble() {
        return scanner.nextDouble();
    }

    /**
     * Return next float from standard input
     */
    public static float readFloat() {
        return scanner.nextFloat();
    }

    /**
     * Return next short from standard input
     */
    public static short readShort() {
        return scanner.nextShort();
    }

    /**
     * Return next long from standard input
     */
    public static long readLong() {
        return scanner.nextLong();
    }

    /**
     * Return next byte from standard input
     */
    public static byte readByte() {
        return scanner.nextByte();
    }

    /**
     * Return next boolean from standard input, allowing "true" or "1" for true,
     * and "false" or "0" for false
     */
    public static boolean readBoolean() {
        String s = readString();
        if (s.equalsIgnoreCase("true"))  return true;
        if (s.equalsIgnoreCase("false")) return false;
        if (s.equals("1"))               return true;
        if (s.equals("0"))               return false;
        throw new java.util.InputMismatchException();
    }

    /**
     * Does standard input have a next line?
     */
    public static boolean hasNextLine() {
        return scanner.hasNextLine();
    }

    /**
     * Return rest of line from standard input
     */
    public static String readLine() {
        return scanner.nextLine();
    }

    /**
     * Return next char from standard input
     */
    // a complete hack and inefficient - email me if you have a better
    public static char readChar() {
        // (?s) for DOTALL mode so . matches a line termination character
        // 1 says look only one character ahead
        // consider precompiling the pattern
        String s = scanner.findWithinHorizon("(?s).", 1);
        return s.charAt(0);
    }

    /**
     * Return rest of input from standard input
     */
    public static String readAll() {
        if (!scanner.hasNextLine()) return null;

        // reference: http://weblogs.java.net/blog/pat/archive/2004/10/stupid_scanner_1.html
        return scanner.useDelimiter("\\A").next();
    }

   /**
     * Read rest of input as array of ints
     */
    public static int[] readInts() {
        String[] fields = readAll().trim().split("\\s+");
        int[] vals = new int[fields.length];
        for (int i = 0; i < fields.length; i++)
            vals[i] = Integer.parseInt(fields[i]);
        return vals;
    }

   /**
     * Read rest of input as array of doubles
     */
    public static double[] readDoubles() {
        String[] fields = readAll().trim().split("\\s+");
        double[] vals = new double[fields.length];
        for (int i = 0; i < fields.length; i++)
            vals[i] = Double.parseDouble(fields[i]);
        return vals;
    }

   /**
     * Read rest of input as array of strings
     */
    public static String[] readStrings() {
        String[] fields = readAll().trim().split("\\s+");
        return fields;
    }



    /**
     * Unit test
     */
    public static void main(String[] args) {

        System.out.println("Type a string: ");
        String s = StdIn.readString();
        System.out.println("Your string was: " + s);
        System.out.println();

        System.out.println("Type an int: ");
        int a = StdIn.readInt();
        System.out.println("Your int was: " + a);
        System.out.println();

        System.out.println("Type a boolean: ");
        boolean b = StdIn.readBoolean();
        System.out.println("Your boolean was: " + b);
        System.out.println();

        System.out.println("Type a double: ");
        double c = StdIn.readDouble();
        System.out.println("Your double was: " + c);
        System.out.println();

    }

}


StdOut.java

import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.Locale;

/**
 *  <i>Standard output</i>. This class provides methods for writing strings
 *  and numbers to standard output.
 *  <p>
 *  For additional documentation, see <a href="http://introcs.cs.princeton.edu/15inout">Section 1.5</a> of
 *  <i>Introduction to Programming in Java: An Interdisciplinary Approach</i> by Robert Sedgewick and Kevin Wayne.
 */
public final class StdOut {

    // force Unicode UTF-8 encoding; otherwise it's system dependent
    private static final String charsetName = "UTF-8";

    // assume language = English, country = US for consistency with StdIn
    private static final Locale US_LOCALE = new Locale("en", "US");

    // send output here
    private static PrintWriter out;

    // this is called before invoking any methods
    static {
        try {
            out = new PrintWriter(new OutputStreamWriter(System.out, charsetName), true);
        }
        catch (UnsupportedEncodingException e) { System.out.println(e); }
    }

    // don't instantiate
    private StdOut() { }

    // close the output stream (not required)
   /**
     * Close standard output.
     */
    public static void close() {
        out.close();
    }

   /**
     * Terminate the current line by printing the line separator string.
     */
    public static void println() {
        out.println();
    }

   /**
     * Print an object to standard output and then terminate the line.
     */
    public static void println(Object x) {
        out.println(x);
    }

   /**
     * Print a boolean to standard output and then terminate the line.
     */
    public static void println(boolean x) {
        out.println(x);
    }

   /**
     * Print a char to standard output and then terminate the line.
     */
    public static void println(char x) {
        out.println(x);
    }

   /**
     * Print a double to standard output and then terminate the line.
     */
    public static void println(double x) {
        out.println(x);
    }

   /**
     * Print a float to standard output and then terminate the line.
     */
    public static void println(float x) {
        out.println(x);
    }

   /**
     * Print an int to standard output and then terminate the line.
     */
    public static void println(int x) {
        out.println(x);
    }

   /**
     * Print a long to standard output and then terminate the line.
     */
    public static void println(long x) {
        out.println(x);
    }

   /**
     * Print a short to standard output and then terminate the line.
     */
    public static void println(short x) {
        out.println(x);
    }

   /**
     * Print a byte to standard output and then terminate the line.
     */
    public static void println(byte x) {
        out.println(x);
    }

   /**
     * Flush standard output.
     */
    public static void print() {
        out.flush();
    }

   /**
     * Print an Object to standard output and flush standard output.
     */
    public static void print(Object x) {
        out.print(x);
        out.flush();
    }

   /**
     * Print a boolean to standard output and flush standard output.
     */
    public static void print(boolean x) {
        out.print(x);
        out.flush();
    }

   /**
     * Print a char to standard output and flush standard output.
     */
    public static void print(char x) {
        out.print(x);
        out.flush();
    }

   /**
     * Print a double to standard output and flush standard output.
     */
    public static void print(double x) {
        out.print(x);
        out.flush();
    }

   /**
     * Print a float to standard output and flush standard output.
     */
    public static void print(float x) {
        out.print(x);
        out.flush();
    }

   /**
     * Print an int to standard output and flush standard output.
     */
    public static void print(int x) {
        out.print(x);
        out.flush();
    }

   /**
     * Print a long to standard output and flush standard output.
     */
    public static void print(long x) {
        out.print(x);
        out.flush();
    }

   /**
     * Print a short to standard output and flush standard output.
     */
    public static void print(short x) {
        out.print(x);
        out.flush();
    }

   /**
     * Print a byte to standard output and flush standard output.
     */
    public static void print(byte x) {
        out.print(x);
        out.flush();
    }

   /**
     * Print a formatted string to standard output using the specified
     * format string and arguments, and flush standard output.
     */
    public static void printf(String format, Object... args) {
        out.printf(US_LOCALE, format, args);
        out.flush();
    }

   /**
     * Print a formatted string to standard output using the specified
     * locale, format string, and arguments, and flush standard output.
     */
    public static void printf(Locale locale, String format, Object... args) {
        out.printf(locale, format, args);
        out.flush();
    }

    // This method is just here to test the class
    public static void main(String[] args) {

        // write to stdout
        StdOut.println("Test");
        StdOut.println(17);
        StdOut.println(true);
        StdOut.printf("%.6f\n", 1.0/7.0);
    }

}


Timer.java

Timer.java主要是為了方便測試演算法執行的時間。我簡單地撰寫了一個java檔案進行測試,測試精讀大概是納秒級別的。在此需要提醒一下大家,System.nanoTime()返回的時間精度是非常高的,而System.currentTimeMillis()這個函式返回的結果精度只是毫秒級的,用於測試時間非常不精確,所以建議大家使用nanoTime()進行時間的測試。這個java檔案還包括了一個nowTime()函式,用於返回當前的系統時間。這在我個人的開發中有一些用途。因為也屬於時間函式,因此放在了Timer.java中。

import java.text.SimpleDateFormat;
import java.util.Date;

public class Timer {
	public enum FORMAT{
		SECOND, MILLI_SECOND, MICRO_SECOND, NANO_SECOND,
	}
	
	public static final int DEFAULT_MAX_NUM_TIMER = 10;
	public final int MAX_NUM_TIMER;
	
	private long[] timeRecorder;
	private boolean[] isTimerStart;
	private FORMAT[] outFormat;
	
	public static String nowTime() {
		SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");// 設定日期格式
		return df.format(new Date());
	}
	
	public Timer(){
		this.MAX_NUM_TIMER = DEFAULT_MAX_NUM_TIMER;
		this.timeRecorder = new long[MAX_NUM_TIMER];
		this.isTimerStart = new boolean[MAX_NUM_TIMER];
		this.outFormat = new FORMAT[MAX_NUM_TIMER];
		
		//set default format as millisecond
		for (int i=0; i<outFormat.length; i++){
			outFormat[i] = FORMAT.MILLI_SECOND;
		}
	}
	
	public Timer(int max_num_timer){
		this.MAX_NUM_TIMER = max_num_timer;
		this.timeRecorder = new long[MAX_NUM_TIMER];
		this.isTimerStart = new boolean[MAX_NUM_TIMER];
		this.outFormat = new FORMAT[MAX_NUM_TIMER];
		
		//set default format as millisecond
		for (int i=0; i<outFormat.length; i++){
			outFormat[i] = FORMAT.MILLI_SECOND;
		}
	}
	
	public void setFormat(int num, FORMAT format){
		//Ensure num less than MAX_NUM_TIMER
		assert(num >=0 && num < MAX_NUM_TIMER);
		
		this.outFormat[num] = format;
	}
	
	public void start(int num) {
		//Ensure the timer now stops.
		assert(!isTimerStart[num]);
		//Ensure num less than MAX_NUM_TIMER
		assert(num >=0 && num < MAX_NUM_TIMER);
		
		isTimerStart[num] = true;
		timeRecorder[num] = System.nanoTime();
	}
	
	public double stop(int num) {
		//Ensure the timer now starts.
		assert(isTimerStart[num]);
		//Ensure num less than MAX_NUM_TIMER
		assert(num >=0 && num < MAX_NUM_TIMER);
		
		long result = System.nanoTime() - timeRecorder[num];
		isTimerStart[num] = false;
		
		switch(outFormat[num]){
		case SECOND:
			return (double) result / 1000000000L;
		case MILLI_SECOND:
			return (double) result / 1000000L;
		case MICRO_SECOND:
			return (double) result / 1000L;
		case NANO_SECOND:
			return (double) result;
		default:
			return (double) result / 1000000L;
		}
	}
}


Hash函式介紹

Hash函式是將任意長的子串M對映成一個較短的定長輸出子串H的函式,這個函式一般用h或者hash表示。我們要求h(M)必須易於計算,稱H=h(M)為M的Hash值。h函式是一個多對一的對映,一般為了安全性,我們要求很難從H求出原來的M,或者從H求出一個M',使得h(M)=h(M')。但是,我們可以驗證任意給定子串M'是否與M具有相同的Hash值。

Hash函式可以按照是否有金鑰作為輸入分為兩大類。一類有金鑰控制,即函式定義為h(k, M);另一類沒有金鑰控制,任何人都可以計算,即函式定義為h(M)。因此,h(k, M)只有擁有金鑰的人可以計算,所以其一般具有身份認證功能;h(M)任何人都可以計算,因而不具有身份認證的功能,只用於檢測接收資料的完整性,或者驗證輸入的內容是否為原始輸入的內容。

對於第一類Hash函式,在此我並不涉及,因為其函式一般來說是通過第二類Hash函式與單鑰加密函式合併實現的,因此我把單鑰加密函式以及第二類Hash函式作為密碼學原型函式,而把第一類Hash函式看做他們的一種擴充套件。對於第二類Hash函式,比較知名的,或者說大家都知道的有MD5,SHA1等等。但是實際上,MD5和SHA1已經被認為並不安全,攻破的人正是清華大學著名的密碼學家王小云老師。因此,在這裡我也不涉及MD5,SHA1的實現,而涉及到的是SHA256,SHA384,SHA512的實現。說句題外話,我有幸與王小云老師有過三面之緣,王小云老師確實是一個典型的數學家,有著非常嚴謹的思維,她的團隊現在致力於Lattice有限域結構的研究。如果看官有興趣讀王小云老師的博士,可以聯絡她,她人很nice的~

在密碼學中,Hash函式需要滿足如下形式:

  • 混合變換(Mixing Transformation)。對於任意的輸入x,輸出的雜湊至h(x)應當與區間[0, 2^{|h|}]中均勻的二進位制串在計算上是不可區分的。通俗點說,就是h(x)的輸出應該遍佈於區間[0, 2^{|h|}],輸出值與這個區間中任意數相等的概率都一樣。不過現在設計的演算法很難做到這一點,所以上述定義說的是計算上不可區分。
  • 抗碰撞攻擊(Collision Resistance)。找兩個輸入x和y,且x \neq y,使得h(x)=h(y),這在計算上應當是不可行的。為了使這個假設成立,要求h的輸出空間應當足夠大。一般來說要求為128位,典型的值為160位或者256位。比如MD5的輸出是128位,SHA1的輸出是160位,而SHA256顧名思義其輸出長度是256位。
  • 抗原像攻擊(Pre-image Resistance)。已知一個Hash值h,找一個輸入x,使得h=h(x),這在計算上是不可行的。這個假設同樣要求h的輸出空間足夠大。如果一個Hash不能抗原像攻擊,我們認為這個Hash函式徹底不安全了,不能再使用。
  • 有效性(Practical Efficiency)。給定一個輸入x,h(x)的計算可以在關於x的長度規模的低階多項式時間內完成。也就是說,對於任意的輸入x,計算h(x)是非常快的。

一般來說,應用典型的生日攻擊(birthday attack,http://zh.wikipedia.org/wiki/%E7%94%9F%E6%97%A5%E5%95%8F%E9%A1%8C),我們可以將hash的安全常數降低一半。比如對於MD5,我們可以有1/2的概率,以2^80的計算量,找到一組數x和y,使得h(x)=h(y),這就使得Hash函式不滿足抗碰撞攻擊。這種攻擊對於任何Hash函式都是可行的。因此,一般來說Hash函式的安全常數是其輸出長度的一半。如果一個Hash函式被證明其可以在比安全常數低的運算下能夠找到碰撞,我們則稱這個演算法是不安全的。王小云老師的結果是,對於MD5演算法,其甚至已經不能抗原像攻擊。而對於SHA1演算法,其安全性已經降低到69,已經不安全。

Hash函式的Java實現

其實Hash函式的實現比較簡單。不過為了具有重用性,我們要求寫出的函式既能夠對任意byte[]陣列進行Hash,也能夠對於任意的InputStream進行Hash。針對不同的演算法,我寫了一個封裝,其class名稱為GeneralHash(這是為了後面的GroupHash實現而區分),原始碼如下:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import org.bouncycastle.util.encoders.Hex;

import cn.edu.buaa.crypto.util.StdOut;

public class GeneralHash {
	public enum HashMode{
		SHA256,
		SHA384,
		SHA512,
	}
	
	private static final int DEFAULT_BLOCK_SIZE = 1024;
	
	public static byte[] Hash(HashMode hashMode, byte[] message){
		MessageDigest md = null;
		try{
			switch(hashMode){
			case SHA256:
				md = MessageDigest.getInstance("SHA-256");
				break;
			case SHA384:
				md = MessageDigest.getInstance("SHA-384");
				break;
			case SHA512:
				md = MessageDigest.getInstance("SHA-512");
				break;
			default:
				//Default Hash is SHA256
				md = MessageDigest.getInstance("SHA-256");
				break;
			}
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		md.update(message);
		return md.digest();
	}
	
	public static byte[] Hash(HashMode hashMode, InputStream in){
		MessageDigest md = null;
		try{
			switch(hashMode){
			case SHA256:
				md = MessageDigest.getInstance("SHA-256");
				break;
			case SHA384:
				md = MessageDigest.getInstance("SHA-384");
				break;
			case SHA512:
				md = MessageDigest.getInstance("SHA-512");
				break;
			default:
				//Default Hash is SHA256
				md = MessageDigest.getInstance("SHA-256");
				break;
			}
			int inL;
			byte[] b = new byte[DEFAULT_BLOCK_SIZE];
			while ((inL = in.read(b)) != -1) {  
			    md.update(b, 0, inL);  
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return md.digest();
	}
}

測試函式如下。測試函式也反映出我寫的這個class如何使用,大家可以參考一下。
	private static void Test_Hash_String(){
		String message1 = "TestGeneralHash-1";
		String message2 = "TestGeneralHash-2";
		byte[] byte256 = GeneralHash.Hash(HashMode.SHA256, message1.getBytes());
		String sha256 = new String(Hex.encode(byte256));
		StdOut.println("SHA256, message = " + message1);
		StdOut.println("Result = " + sha256 + ", length = " + byte256.length);
		//Hash result for the same value should be equal
		byte[] byte256_1 = GeneralHash.Hash(HashMode.SHA256, message1.getBytes());
		String sha256_1 = new String(Hex.encode(byte256_1));
		StdOut.println("SHA256, message = " + message1);
		StdOut.println("Result = " + sha256_1 + ", length = " + byte256_1.length);
		assert(sha256.equals(sha256_1));
		//Hash result for different values should be distinct
		byte[] byte256_2 = GeneralHash.Hash(HashMode.SHA256, message2.getBytes());
		String sha256_2 = new String(Hex.encode(byte256_2));
		StdOut.println("SHA256, message = " + message2);
		StdOut.println("Result = " + sha256_2 + ", length = " + byte256_2.length);
		StdOut.println();
		assert(!sha256.equals(sha256_2));
		
		byte[] byte384 = GeneralHash.Hash(HashMode.SHA384, message1.getBytes());
		String sha384 = new String(Hex.encode(byte384));
		StdOut.println("SHA384, message = " + message1);
		StdOut.println("Result = " + sha384 + ", length = " + byte384.length);
		//Hash result for the same value should be equal
		byte[] byte384_1 = GeneralHash.Hash(HashMode.SHA384, message1.getBytes());
		String sha384_1 = new String(Hex.encode(byte384_1));
		StdOut.println("SHA384, message = " + message1);
		StdOut.println("Result = " + sha384_1 + ", length = " + byte384_1.length);
		assert(sha384.equals(sha384_1));
		//Hash result for different values should be distinct
		byte[] byte384_2 = GeneralHash.Hash(HashMode.SHA384, message2.getBytes());
		String sha384_2 = new String(Hex.encode(byte384_2));
		StdOut.println("SHA384, message = " + message2);
		StdOut.println("Result = " + sha384_2 + ", length = " + byte384_2.length);
		StdOut.println();
		assert(!sha384.equals(sha384_2));
		
		byte[] byte512 = GeneralHash.Hash(HashMode.SHA512, message1.getBytes());
		String sha512 = new String(Hex.encode(byte512));
		StdOut.println("SHA512, message = " + message1);
		StdOut.println("Result = " + sha512 + ", length = " + byte512.length);
		//Hash result for the same value should be equal
		byte[] byte512_1 = GeneralHash.Hash(HashMode.SHA512, message1.getBytes());
		String sha512_1 = new String(Hex.encode(byte512_1));
		StdOut.println("SHA512, message = " + message1);
		StdOut.println("Result = " + sha512_1 + ", length = " + byte512_1.length);
		assert(sha512.equals(sha512_1));
		//Hash result for different values should be distinct
		byte[] byte512_2 = GeneralHash.Hash(HashMode.SHA512, message2.getBytes());
		String sha512_2 = new String(Hex.encode(byte512_2));
		StdOut.println("SHA512, message = " + message2);
		StdOut.println("Result = " + sha512_2 + ", length = " + byte512_2.length);
		StdOut.println();
		assert(!sha512.equals(sha512_2));
	}
	
	private static void Test_Hash_File(){
		try {
			File fileIn = new File("docs1.5on.zip");
			FileInputStream in;
			
			in = new FileInputStream(fileIn);
			byte[] byte256 = GeneralHash.Hash(HashMode.SHA256, in);
			String sha256 = new String(Hex.encode(byte256));
			StdOut.println("File SHA256 = " + sha256 + ", length = " + byte256.length);
			in.close();
			
			in = new FileInputStream(fileIn);
			byte[] byte384 = GeneralHash.Hash(HashMode.SHA384, in);
			String sha384 = new String(Hex.encode(byte384));
			StdOut.println("File SHA384 = " + sha384 + ", length = " + byte384.length);
			in.close();
			
			in = new FileInputStream(fileIn);
			byte[] byte512 = GeneralHash.Hash(HashMode.SHA512, in);
			String sha512 = new String(Hex.encode(byte512));
			StdOut.println("File SHA512 = " + sha512 + ", length = " + byte512.length);
			in.close();
			
		}  catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args){
		Test_Hash_String();
		Test_Hash_File();
	}


相關推薦

Java密碼原型演算法實現——第一部分標準Hash演算法

題注 從部落格中看出來我是個比較鍾愛Java的應用密碼學研究者。雖然C在密碼學中有不可替代的優勢:速度快,但是,Java的可移植性使得開發人員可以很快地將程式碼移植到各個平臺,這比C實現要方便的多。尤其是Android平臺的出現,Java的應用也就越來越廣。因此,我本人在密

Java密碼原型演算法實現——第三部分雙線性對

背景介紹 技術部落格已經好久沒更新了。倒不是因為沒得寫,是因為實在是太忙了,而且研究也到了一個瓶頸期,需要大量閱讀文獻。 本來打算很長一段時間都不更新部落格了,甚至打算等我畢業工作後再更新一些有價值的部落格,但是最近在CSDN私信上和知乎上經常收到求救帖子,

比特幣系統採用的公鑰密碼方案和ECDSA簽名演算法介紹——第一部分原理

ECC演算法是基於有限域的橢圓曲線上的數學演算法。關於ECC演算法基本原理的介紹,請參考《ECC加密演算法入門介紹》(http://www.8btc.com/eccmath),本文重點介紹Bitcoin系統中採用的公鑰密碼學方案和簽名演算法的實現細節。 一、 公鑰(pu

《從零開始架構》筆記——第一部分概念和基礎

第一章 架構基礎 模組與元件 模組:從邏輯角度拆分,主要目的是職責分離 元件:從物理角度拆分,主要目的是單元複用 框架與架構 框架:元件規範(開發規範),提供基礎功能的產品。 架構:對軟體系統結構的描述 架構設計的目的是什麼? 軟體架構的歷史 第一次軟體危機——結構化程式設計登場 2000名程式設計師歷時

【區塊鏈Go語言實現第一部分區塊鏈基本原型

ont 構建 獲得 列表 append 檢查 世紀 正常 私有 0x00 介紹 區塊鏈(Blockchain)是21世紀最具革命性的技術之一,目前它仍處於逐漸成熟階段,且其發展潛力尚未被完全意識到。從本質上講,區塊鏈只是一種記錄的分布式數據庫。但它之所以獨特,是因為它並

密碼03--go語言與非對稱加密RSA演算法實現

目錄 1.對稱加密的弊端 2.非對稱加密 2.1 非對稱加密使用場景 2.2 區分公私鑰 2.3 非對稱加密通訊流程 2.4 非對稱加密與對稱加密 3.非對稱加密RSA演算法 3.1 RSA演算法 3.2 RSA原理 3.3 RSA生成金鑰對流程

Java演算法實現 BAT公司為什麼要考演算法 github

BAT公司為什麼要考演算法? 答: 演算法是程式設計師的基本功。對於Java程式設計師來說。應用開發的時候,很少需要自己去實現演算法。所以,開發年限的增加,並不會提高程式設計師的演算法能力。而演算法是整個軟體技術的核心底層。演算法最能提現一個程式設計師的內功和核心競爭力。考

演算法設計與分析P8演算法實現第一

#include<stdio.h> #include<string.h> int main() { int n, t, i,temp; int count[10]; mem

雜湊三部曲第一部分C語言實現 靜態雜湊表

在這個靜態雜湊表中 我們用一個容量為10 的靜態陣列作為雜湊表的底層構造 但是陣列的每一個儲存空間中又分為兩個部分                                 資料區:data                                    

計算機圖形常用演算法實現10 多邊形裁剪Sutherland-Hodgman演算法

演算法原理比較簡單,用裁剪區域的四條邊分別去分割多邊形。 假設邊p1,p2,用區域某條邊進行裁剪。方向為p1->p2 1.p1在邊外,p2在邊外 無操作 2.p1外,p2內 儲存交點p和p2 3.p1內,p2內 儲存p2 4.p1內,p2外 儲存交點 分割完之後更新多邊形的點集 程式碼

普林斯頓大學_演算法公開課:Part_1_第一部分並查集

首先,給大家推薦一個平臺,Coursera (類比國內的mooc網),你可以在上面學習諸多國外一流大學的公開課視訊,各個領域的都有,涉獵範圍很廣。想要出國留學的小夥伴兒不妨在上面事先感受一波國外授課的氛圍與模式。言歸正傳,作為一名程式猿,演算法實在是太重要了!即便不是一名程式

java資料結構與之二叉樹相關實現(第一遍歷)

一、基本概念 每個結點最多有兩棵子樹,左子樹和右子樹,次序不可以顛倒。 性質: 非空二叉樹的第n層上至多有2^(n-1)個元素。 深度為h的二叉樹至多有2^h-1個結點。 滿二叉樹:所有終端都在同一層次,且非終端結點的度數為2。 在滿二叉

聚類演算法(K-means + Fuzzy C-means + Hierarchical + Mixture of Gaussians)---第一部分簡介

前言 什麼是聚類? 聚類可以被認為是最重要的無監督學習問題; 所以,像這樣的其他問題一樣,它涉及在未標記資料的集合中找到一個結構。聚類的簡單定義可能是“將物件組織成某些成員相似的組的過程”。因此,"cluster"是它們之間“相似”的物件的集合,並且與屬於其他"cluste

Java語言使用註解處理器生成程式碼 —— 第一部分註解型別

原文作者:deors 原文地址:https://deors.wordpress.com/2011/09/26/annotation-types/ 譯文作者:Jianan - [email protected] 版本資訊:本文基於

小白爬蟲——第一部分簡單學習Python

最後一個元素 if語句 () 子串 寫文件 接下來 文件數量 .so 報錯 學習目錄定義新函數文件讀寫數組字符串字典定義新函數創一個小群,供大家學習交流聊天如果有對學python方面有什麽疑惑問題的,或者有什麽想說的想聊的大家可以一起交流學習一起進步呀。也希望大家對學pyt

第一部分批處理的專用命令

分隔符 pause exe call pat sage 設置環境變量 定義 順序 批處理文件是將一系列命令按一定的順序集合為一個可執行的文本文件,其擴展名為BAT。這些命令統稱批處理命令,下面我就來給大家介紹一下批處理的命令。 1、 REM REM 是個註釋命令一般是用來給

jQuery源碼逐行分析學習02(第一部分jQuery的一些變量和函數)

篩選 復雜 我們 分解 support letter content 變量 new對象 第一次嘗試使用Office Word,方便程度大大超過網頁在線編輯,不過初次使用,一些內容不甚熟悉,望各位大神見諒~ 在上次的文章中,把整個jQuery的結構進行了梳理,得到了整個jQu

第一部分MongoDB備忘錄

taf slow 日誌 復制集 副本集 () nss nec ror 一、NoSQL 簡介   Nosql的全稱是Not Only Sql,這個概念早起就有人提出,在09年的時候比較火。Nosql指的是非關系型數據庫,而我們常用的都是關系型數據庫。就像我們常用的mysql,

各種基本演算法實現小結(五)—— 排序演算法

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

機器學習演算法實現解析——liblbfgs之L-BFGS演算法

在博文“優化演算法——擬牛頓法之L-BFGS演算法”中,已經對L-BFGS的演算法原理做了詳細的介紹,本文主要就開原始碼liblbfgs重新回顧L-BFGS的演算法原理以及具體的實現過程,在L-BFGS演算法中包含了處理L1正則的OWL-QN演算法,對於OWL-QN演算法的詳細原理,可以參見