001/****************************************************************
002 * Licensed to the Apache Software Foundation (ASF) under one   *
003 * or more contributor license agreements.  See the NOTICE file *
004 * distributed with this work for additional information        *
005 * regarding copyright ownership.  The ASF licenses this file   *
006 * to you under the Apache License, Version 2.0 (the            *
007 * "License"); you may not use this file except in compliance   *
008 * with the License.  You may obtain a copy of the License at   *
009 *                                                              *
010 *   http://www.apache.org/licenses/LICENSE-2.0                 *
011 *                                                              *
012 * Unless required by applicable law or agreed to in writing,   *
013 * software distributed under the License is distributed on an  *
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
015 * KIND, either express or implied.  See the License for the    *
016 * specific language governing permissions and limitations      *
017 * under the License.                                           *
018 ****************************************************************/
019
020package org.apache.james.user.lib.util;
021
022import javax.mail.MessagingException;
023import javax.mail.internet.MimeUtility;
024
025import java.io.ByteArrayOutputStream;
026import java.io.FileInputStream;
027import java.io.FileOutputStream;
028import java.io.IOException;
029import java.io.OutputStream;
030import java.security.MessageDigest;
031import java.security.NoSuchAlgorithmException;
032import java.util.Locale;
033
034/**
035 * Computes and verifies digests of files and strings
036 */
037public class DigestUtil {
038
039    /**
040     * Command line interface. Use -help for arguments.
041     * 
042     * @param args
043     *            the arguments passed in on the command line
044     */
045    public static void main(String[] args) {
046
047        String alg = "SHA";
048        boolean file = false;
049
050        if (args.length == 0 || args.length > 4) {
051            printUsage();
052            return;
053        }
054
055        for (int i = 0; i < args.length; i++) {
056            String currArg = args[i].toLowerCase(Locale.US);
057            if (currArg.equals("-help") || currArg.equals("-usage")) {
058                printUsage();
059                return;
060            }
061            if (currArg.equals("-alg")) {
062                alg = args[i + 1];
063            }
064            if (currArg.equals("-file")) {
065                file = true;
066            }
067        }
068
069        if (file) {
070            digestFile(args[args.length - 1], alg);
071        } else {
072            try {
073                String hash = digestString(args[args.length - 1], alg);
074                System.out.println("Hash is: " + hash);
075            } catch (NoSuchAlgorithmException nsae) {
076                System.out.println("No such algorithm available");
077            }
078        }
079    }
080
081    /**
082     * Print the command line usage string.
083     */
084    public static void printUsage() {
085        System.out.println("Usage: " + "java org.apache.james.security.DigestUtil" + " [-alg algorithm]" + " [-file] filename|string");
086    }
087
088    /**
089     * Calculate digest of given file with given algorithm. Writes digest to
090     * file named filename.algorithm .
091     * 
092     * @param filename
093     *            the String name of the file to be hashed
094     * @param algorithm
095     *            the algorithm to be used to compute the digest
096     */
097    public static void digestFile(String filename, String algorithm) {
098        byte[] b = new byte[65536];
099        int read;
100        FileInputStream fis = null;
101        FileOutputStream fos = null;
102        try {
103            MessageDigest md = MessageDigest.getInstance(algorithm);
104            fis = new FileInputStream(filename);
105            while (fis.available() > 0) {
106                read = fis.read(b);
107                md.update(b, 0, read);
108            }
109            byte[] digest = md.digest();
110            String fileNameBuffer = filename + "." + algorithm;
111            fos = new FileOutputStream(fileNameBuffer);
112            OutputStream encodedStream = MimeUtility.encode(fos, "base64");
113            encodedStream.write(digest);
114            fos.flush();
115        } catch (Exception e) {
116            System.out.println("Error computing Digest: " + e);
117        } finally {
118            try {
119                fis.close();
120                fos.close();
121            } catch (Exception ignored) {
122            }
123        }
124    }
125
126    /**
127     * Calculate digest of given String using given algorithm. Encode digest in
128     * MIME-like base64.
129     * 
130     * @param pass
131     *            the String to be hashed
132     * @param algorithm
133     *            the algorithm to be used
134     * @return String Base-64 encoding of digest
135     * 
136     * @throws NoSuchAlgorithmException
137     *             if the algorithm passed in cannot be found
138     */
139    public static String digestString(String pass, String algorithm) throws NoSuchAlgorithmException {
140
141        MessageDigest md;
142        ByteArrayOutputStream bos;
143
144        try {
145            md = MessageDigest.getInstance(algorithm);
146            byte[] digest = md.digest(pass.getBytes("iso-8859-1"));
147            bos = new ByteArrayOutputStream();
148            OutputStream encodedStream = MimeUtility.encode(bos, "base64");
149            encodedStream.write(digest);
150            return bos.toString("iso-8859-1");
151        } catch (IOException ioe) {
152            throw new RuntimeException("Fatal error: " + ioe);
153        } catch (MessagingException me) {
154            throw new RuntimeException("Fatal error: " + me);
155        }
156    }
157
158    /**
159     * Private constructor to prevent instantiation of the class
160     */
161    private DigestUtil() {
162    }
163}