info.globalbus.dkim.DKIMUtil.java Source code

Java tutorial

Introduction

Here is the source code for info.globalbus.dkim.DKIMUtil.java

Source

/* 
 * Copyright 2008 The Apache Software Foundation or its licensors, as
 * applicable.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * A licence was granted to the ASF by Florian Sager on 30 November 2008
 */

package info.globalbus.dkim;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;

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

import com.sun.mail.util.QPEncoderStream;

/*
 * @author Florian Sager, http://www.agitos.de, 22.11.2008
 */

public class DKIMUtil {

    protected static String[] splitHeader(String header) throws DKIMSignerException {
        int colonPos = header.indexOf(':');
        if (colonPos == -1) {
            throw new DKIMSignerException("The header string " + header + " is no valid RFC 822 header-line");
        }
        return new String[] { header.substring(0, colonPos), header.substring(colonPos + 1) };
    }

    protected static String concatArray(ArrayList<String> l, String separator) {
        StringBuffer buf = new StringBuffer();
        Iterator<String> iter = l.iterator();
        while (iter.hasNext()) {
            buf.append(iter.next()).append(separator);
        }

        return buf.substring(0, buf.length() - separator.length());
    }

    protected static boolean isValidDomain(String domainname) {
        Pattern pattern = Pattern.compile("(.+)\\.(.+)");
        Matcher matcher = pattern.matcher(domainname);
        return matcher.matches();
    }

    // FSTODO: converts to "platforms default encoding" might be wrong ?
    protected static String QuotedPrintable(String s) {

        try (ByteArrayOutputStream boas = new ByteArrayOutputStream()) {
            try (QPEncoderStream encodeStream = new QPEncoderStream(boas)) {
                encodeStream.write(s.getBytes());

                String encoded = boas.toString();
                encoded = encoded.replaceAll(";", "=3B");
                encoded = encoded.replaceAll(" ", "=20");

                return encoded;
            }
        } catch (IOException ioe) {
            //
        }

        return null;
    }

    protected static String base64Encode(byte[] b) {
        String encoded = Base64.encodeBase64String(b);
        // remove unnecessary linefeeds after 76 characters
        encoded = encoded.replace("\n", ""); // Linux+Win
        return encoded.replace("\r", ""); // Win --> FSTODO: select Encoder
        // without line termination
    }

    public boolean checkDNSForPublickey(String signingDomain, String selector) throws DKIMSignerException {

        Hashtable<String, String> env = new Hashtable<String, String>();
        env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
        String recordname = selector + "._domainkey." + signingDomain;
        String value = null;

        try {
            DirContext dnsContext = new InitialDirContext(env);

            javax.naming.directory.Attributes attribs = dnsContext.getAttributes(recordname,
                    new String[] { "TXT" });
            javax.naming.directory.Attribute txtrecord = attribs.get("txt");

            if (txtrecord == null) {
                throw new DKIMSignerException("There is no TXT record available for " + recordname);
            }

            // "v=DKIM1; g=*; k=rsa; p=MIGfMA0G ..."
            value = (String) txtrecord.get();

        } catch (NamingException ne) {
            throw new DKIMSignerException("Selector lookup failed", ne);
        }

        if (value == null) {
            throw new DKIMSignerException("Value of RR " + recordname + " couldn't be retrieved");
        }

        // try to read public key from RR
        String[] tags = value.split(";");
        for (String tag : tags) {
            tag = tag.trim();
            if (tag.startsWith("p=")) {

                try {
                    KeyFactory keyFactory = KeyFactory.getInstance("RSA");

                    // decode public key, FSTODO: convert to DER format
                    PKCS8EncodedKeySpec pubSpec = new PKCS8EncodedKeySpec(tag.substring(2).getBytes());
                    keyFactory.generatePublic(pubSpec);
                } catch (NoSuchAlgorithmException nsae) {
                    throw new DKIMSignerException("RSA algorithm not found by JVM");
                } catch (InvalidKeySpecException ikse) {
                    throw new DKIMSignerException(
                            "The public key " + tag + " in RR " + recordname + " couldn't be decoded.");
                }

                // FSTODO: create test signature with privKey and test
                // validation with pubKey to check on a valid key pair

                return true;
            }
        }

        throw new DKIMSignerException("No public key available in " + recordname);
    }

}