码迷,mamicode.com
首页 > 编程语言 > 详细

Java密码学原型算法实现——第一部分:标准Hash算法

时间:2014-08-06 23:03:42      阅读:816      评论:0      收藏:0      [点我收藏+]

标签:java   cryptography   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封装,这些封装会方便我进行下一步的实现。不管使用的方便与否,各位看官凑合着看,凑合着用,如果有任何可以改进的地方还请给我发邮件,我的邮箱是footman_900217@126.com。

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算法,布布扣,bubuko.com

Java密码学原型算法实现——第一部分:标准Hash算法

标签:java   cryptography   hash   

原文地址:http://blog.csdn.net/liuweiran900217/article/details/38406257

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!