com.zotoh.crypto.MICUte.java Source code

Java tutorial

Introduction

Here is the source code for com.zotoh.crypto.MICUte.java

Source

/*??
 * COPYRIGHT (C) 2008-2009 CHERIMOIA LLC. ALL RIGHTS RESERVED.
 *
 * THIS IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR
 * MODIFY IT UNDER THE TERMS OF THE APACHE LICENSE, 
 * VERSION 2.0 (THE "LICENSE").
 *
 * THIS LIBRARY IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
 * BUT WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
 *   
 * SEE THE LICENSE FOR THE SPECIFIC LANGUAGE GOVERNING PERMISSIONS 
 * AND LIMITATIONS UNDER THE LICENSE.
 *
 * You should have received a copy of the Apache License
 * along with this distribution; if not, you may obtain a copy of the 
 * License at 
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 ??*/

package com.zotoh.crypto;

import static com.zotoh.core.util.CoreUte.asBytes;
import static com.zotoh.core.util.CoreUte.safeGetClzname;

import java.io.InputStream;
import java.security.MessageDigest;
import java.security.cert.Certificate;

import javax.mail.BodyPart;
import javax.mail.Multipart;
import javax.mail.internet.ContentType;
import javax.mail.internet.MimeMultipart;

import org.apache.commons.codec.binary.Base64;

import com.zotoh.core.io.ByteOStream;
import com.zotoh.core.mime.MimeConsts;
import com.zotoh.core.util.Tuple;

/**
 * @author kenl
 *
 */
public enum MICUte {
    ;

    private static final String CRLF = "\r\n";
    private static final byte[] CRLFBITS = asBytes(CRLF);

    /**
     * Calculates a Message Integrity Check (Message Disposition)
     *
     * @param content The content across which the MIC is calculated. This can
     *                   be of type InputStream, String, byte[], or a Multipart (multipart/signed)
     * @param algorithm  The algorithm to use to calculate the MIC (e.g. sha1, md5)
     * @param addcrlfToString If true, adds a CRLF on the String data
     *                   since the receiving MTA uses canonicalized data to calculate MIC
     * @param addcrlfTobyte If true, adds a CRLF on the byte data
     *                   since the receiving MTA uses canonicalized data to calculate MIC
     * @return The base 64 encoded MIC value
     * @exception Exception
     */
    public static String calc(Object content, Certificate[] certs, String algo, boolean addcrlfToString,
            boolean addcrlfToBytes) throws Exception {
        InputStream micStream = null;
        byte micBits[] = null;
        boolean addcrlf = false;

        if (content instanceof byte[]) {
            micBits = fromBytes((byte[]) content, addcrlfToBytes);
        } else if (content instanceof String) {
            micBits = fromString((String) content, addcrlfToString);
        } else if (content instanceof InputStream) {
            if (addcrlfToBytes || addcrlfToString)
                addcrlf = true;
            micStream = (InputStream) content;
        } else if (CryptoUte.isSigned(content)) {
            return fromSMP(certs, content);
        } else if (content instanceof Multipart) {
            Object rc = fromMP((Multipart) content);
            if (rc instanceof InputStream)
                micStream = (InputStream) rc;
            else if (rc instanceof byte[])
                micBits = (byte[]) rc;
        } else {
            error(content);
        }

        MessageDigest messageDigest = MessageDigest.getInstance(algo);
        byte[] mic;

        if (micBits != null) {
            mic = messageDigest.digest(micBits);
        } else {
            byte[] buf = new byte[4096];
            int c;
            while ((c = micStream.read(buf)) > 0) {
                messageDigest.update(buf, 0, c);
            }

            if (addcrlf) {
                messageDigest.update(CRLFBITS);
            }

            if (micStream.markSupported())
                micStream.reset();

            mic = messageDigest.digest();
        }

        return Base64.encodeBase64String(mic);
    }

    /**
     * @param content
     * @param certs
     * @param algo
     * @return
     * @throws Exception
     */
    public static String calc(Object content, Certificate[] certs, String algo) throws Exception {
        return calc(content, certs, algo, false, false);
    }

    private static Object fromMP(Multipart mp) throws Exception {
        ContentType ct = new ContentType(mp.getContentType());
        BodyPart bp;
        Object contents;
        Object rc = null;
        int count = mp.getCount();

        if ("multipart/mixed".equalsIgnoreCase(ct.getBaseType())) {
            if (count > 0) {
                bp = mp.getBodyPart(0);
                contents = bp.getContent();

                // check for EDI payload sent as attachment
                String ctype = bp.getContentType();
                boolean getNextPart = false;

                if (ctype.indexOf("text/plain") >= 0) {
                    if (contents instanceof String) {
                        String bodyText = "This is a generated cryptographic message in MIME format";
                        if (((String) contents).startsWith(bodyText)) {
                            getNextPart = true;
                        }
                    }

                    if (!getNextPart) {
                        // check for a content disposition
                        // if disposition type is attachment, then this is a doc
                        getNextPart = true;
                        String disp = bp.getDisposition();
                        if (disp != null && disp.toLowerCase().equals("attachment"))
                            getNextPart = false;
                    }
                }

                if ((count >= 2) && getNextPart) {
                    bp = mp.getBodyPart(1);
                    contents = bp.getContent();
                }

                if (contents instanceof String) {
                    rc = asBytes((String) contents);
                } else if (contents instanceof byte[]) {
                    rc = contents;
                } else if (contents instanceof InputStream) {
                    rc = contents;
                } else {
                    String cn = contents == null ? "null" : contents.getClass().getName();
                    throw new Exception("Unsupport MIC object: " + cn);
                }
            }
        } else if (count > 0) {
            bp = mp.getBodyPart(0);
            contents = bp.getContent();

            if (contents instanceof String) {
                rc = asBytes((String) contents);
            } else if (contents instanceof byte[]) {
                rc = contents;
            } else if (contents instanceof InputStream) {
                rc = contents;
            } else {
                String cn = contents == null ? "null" : contents.getClass().getName();
                throw new Exception("Unsupport MIC object: " + cn);
            }
        }

        return rc;
    }

    private static String fromSMP(Certificate[] certs, Object micContent) throws Exception {

        if (!(micContent instanceof MimeMultipart)) {
            error(micContent);
        }

        MimeMultipart mp = (MimeMultipart) micContent;
        Tuple rc = CryptoUte.verifySmimeDigSig(mp, certs, MimeConsts.CTE_BINARY);

        return Base64.encodeBase64String((byte[]) rc.get(1));
    }

    private static byte[] fromString(String str, boolean wantcrlf) throws Exception {
        if (wantcrlf)
            str = str + CRLF;
        return asBytes(str);
    }

    private static byte[] fromBytes(byte[] bits, boolean wantcrlf) throws Exception {
        ByteOStream baos = new ByteOStream();
        baos.write(bits);

        if (wantcrlf) {
            baos.write(CRLFBITS);
        }

        return baos.asBytes();
    }

    private static void error(Object c) throws Exception {
        throw new Exception("Unsupported MIC content object : " + safeGetClzname(c));
    }

}