com.igeekinc.indelible.indeliblefs.security.EntityAuthenticationServerCore.java Source code

Java tutorial

Introduction

Here is the source code for com.igeekinc.indelible.indeliblefs.security.EntityAuthenticationServerCore.java

Source

/*
 * Copyright 2002-2014 iGeek, Inc.
 * All Rights Reserved
 * @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.@
 */

package com.igeekinc.indelible.indeliblefs.security;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.rmi.RemoteException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Date;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.security.auth.x500.X500Principal;

import org.apache.log4j.Logger;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.jce.PKCS10CertificationRequest;
import org.bouncycastle.x509.X509V1CertificateGenerator;
import org.bouncycastle.x509.X509V3CertificateGenerator;
import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;

import com.igeekinc.indelible.indeliblefs.IndelibleEntity;
import com.igeekinc.indelible.oid.EntityID;
import com.igeekinc.indelible.oid.ObjectID;
import com.igeekinc.indelible.oid.ObjectIDFactory;
import com.igeekinc.util.logging.ErrorLogMessage;

public class EntityAuthenticationServerCore extends IndelibleEntity implements EntityAuthenticationServer {
    private PrivateKey signingKey;
    private KeyStore keyStore;
    private X509Certificate rootCertificate;
    private SocketAddress serverAddress;

    private static final String kRootCertAlias = "IndelibleRootCert";
    private static final String kDefaultKeyStorePassword = "idb301$";
    private static final String kSigningKeyAlias = "IndelibleRootSigningKey";
    private static final String kKeyStoreFileName = "IndelibleKeyStore";

    public EntityAuthenticationServerCore(File securityRootDir) throws KeyStoreException, NoSuchAlgorithmException,
            CertificateException, FileNotFoundException, IOException, UnrecoverableKeyException {
        super(null);
        if (securityRootDir == null)
            throw new IllegalArgumentException("securityRootDir cannot be null");
        if (!securityRootDir.isDirectory())
            throw new IllegalArgumentException(
                    "Security root dir '" + securityRootDir.getAbsolutePath() + "' is not a directory");
        File keyStoreFile = new File(securityRootDir, kKeyStoreFileName);
        keyStore = KeyStore.getInstance("JKS");
        keyStore.load(new FileInputStream(keyStoreFile), kDefaultKeyStorePassword.toCharArray());
        signingKey = (PrivateKey) keyStore.getKey(kSigningKeyAlias, kDefaultKeyStorePassword.toCharArray());
        rootCertificate = (X509Certificate) keyStore.getCertificate(kRootCertAlias);
        Principal rootPrincipal = rootCertificate.getIssuerDN();
        String uidString = EntityAuthenticationClient.getUID(rootPrincipal);
        EntityID rootID = (EntityID) ObjectIDFactory.reconstituteFromString(uidString);
        setEntityID(rootID);
    }

    public static final int kRequestingServerIDBytesOffset = 0;
    public static final int kChallengeBufferSize = ObjectID.kTotalBytes;

    /* (non-Javadoc)
     * @see com.igeekinc.indelible.indeliblefs.security.SecurityServer#registerServer(java.security.cert.X509Certificate)
     */
    public void registerServer(X509Certificate selfSignedServerCert)
            throws InvalidKeyException, CertificateException, NoSuchAlgorithmException, NoSuchProviderException,
            SignatureException, KeyStoreException {
        selfSignedServerCert.checkValidity();
        selfSignedServerCert.verify(selfSignedServerCert.getPublicKey(), "BC");
        EntityID serverID = EntityAuthentication.getObjectIDFromCertificateSerialNumber(selfSignedServerCert);
        logger.error(new ErrorLogMessage("Server " + serverID + " requesting registration"));
        if (keyStore.containsAlias(serverID.toString())) {
            Certificate checkKey = keyStore.getCertificate(serverID.toString());
            if (!checkKey.getPublicKey().equals(selfSignedServerCert.getPublicKey())) {
                logger.error(new ErrorLogMessage("Attempting to register server " + serverID
                        + " but public key does not match previously registered public key"));
                throw new InvalidKeyException("Registered public key does not match");
            }
            logger.error(new ErrorLogMessage("Server " + serverID + " was already registered"));
            return; // OK, we're good
        }
        keyStore.setCertificateEntry(serverID.toString(), selfSignedServerCert);
        logger.error(new ErrorLogMessage("Server " + serverID + " registered"));
    }

    /* (non-Javadoc)
     * @see com.igeekinc.indelible.indeliblefs.security.SecurityServer#authenticateServer(com.igeekinc.indelible.oid.ServerID, java.security.PublicKey)
     */
    public EntityAuthentication authenticateServer(EntityID serverID, byte[] encodedCertReq)
            throws CertificateEncodingException, InvalidKeyException, IllegalStateException,
            NoSuchProviderException, NoSuchAlgorithmException, SignatureException, UnrecoverableKeyException,
            KeyStoreException, IOException, CertificateParsingException, ServerNotRegisteredException,
            AuthenticationFailureException {
        Date startDate = new Date(System.currentTimeMillis() - (60L * 60L * 1000L)); // time from which certificate is valid
        Date expiryDate = new Date(startDate.getTime() + (30L * 24L * 60L * 60L * 1000L)); // time after which certificate is not valid
        BigInteger serialNumber = serverID.toBigInteger(); // serial number for certificate

        EntityAuthentication returnAuthentication = null;

        Certificate registeredCertificate = keyStore.getCertificate(serverID.toString());
        if (registeredCertificate != null) {
            PublicKey checkKey = registeredCertificate.getPublicKey();
            PKCS10CertificationRequest certReq = new PKCS10CertificationRequest(encodedCertReq);
            if (checkKey != null) {
                byte[] encodedCheckKey = checkKey.getEncoded();
                byte[] encodedCertKey = certReq.getPublicKey().getEncoded();
                if (Arrays.equals(encodedCheckKey, encodedCertKey)) {
                    X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
                    X500Principal dnName = new X500Principal(
                            EntityAuthenticationClient.kEntityIDCNPrefix + serverID.toString());

                    certGen.setSerialNumber(serialNumber);
                    certGen.setIssuerDN(rootCertificate.getSubjectX500Principal());
                    certGen.setNotBefore(startDate);
                    certGen.setNotAfter(expiryDate);
                    certGen.setSubjectDN(dnName); // note: same as issuer
                    certGen.setPublicKey(certReq.getPublicKey());
                    certGen.setSignatureAlgorithm(kCertificateSignatureAlg);

                    certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false,
                            new AuthorityKeyIdentifierStructure(rootCertificate));
                    certGen.addExtension(X509Extensions.SubjectKeyIdentifier, false,
                            new SubjectKeyIdentifierStructure(certReq.getPublicKey()));

                    X509Certificate cert = certGen.generate(signingKey, "BC");
                    returnAuthentication = new EntityAuthentication(cert);
                } else {
                    logger.error(new ErrorLogMessage(
                            "Server {0} requesting authentication, but registered key does not match", serverID));
                    throw new AuthenticationFailureException();
                }
            } else {
                logger.error(new ErrorLogMessage(
                        "Server {0} requesting authentication, no check key found in registered certificate",
                        serverID));
                throw new AuthenticationFailureException();
            }
        } else {
            logger.error(new ErrorLogMessage("Server {0} requesting authentication, but not registered", serverID));
            throw new ServerNotRegisteredException();
        }
        return returnAuthentication;
    }

    public Certificate getServerCertificate() throws KeyStoreException {
        return keyStore.getCertificate(kRootCertAlias);
    }

    /**
     * Returns the key managers to be used with the SSL socket that connects to this
     * AuthenticationServer
     * @return
     */
    public KeyManager[] getKeyManagers() {
        try {
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
            keyManagerFactory.init(keyStore, kDefaultKeyStorePassword.toCharArray());
            return keyManagerFactory.getKeyManagers();
        } catch (NoSuchAlgorithmException e) {
            Logger.getLogger(getClass()).error(new ErrorLogMessage("Caught exception"), e);
        } catch (KeyStoreException e) {
            Logger.getLogger(getClass()).error(new ErrorLogMessage("Caught exception"), e);
        } catch (UnrecoverableKeyException e) {
            Logger.getLogger(getClass()).error(new ErrorLogMessage("Caught exception"), e);
        }
        throw new InternalError("Could not initialize key managers");
    }

    public static void initRootSecurity(File securityRootDir, EntityID securityServerID)
            throws KeyStoreException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException,
            SignatureException, IllegalStateException, CertificateException, FileNotFoundException, IOException {
        if (securityRootDir == null)
            throw new IllegalArgumentException("securityRootDir cannot be null");
        if (!securityRootDir.isDirectory())
            throw new IllegalArgumentException(
                    "Security root dir '" + securityRootDir.getAbsolutePath() + "' is not a directory");
        File keyStoreFile = new File(securityRootDir, kKeyStoreFileName);
        if (keyStoreFile.exists())
            throw new IOException("Keystore file '" + keyStoreFile.getAbsolutePath()
                    + "' already exists - refusing to overwrite");
        KeyStore initKeyStore = KeyStore.getInstance("JKS");
        initKeyStore.load(null);
        Date startDate = new Date(); // time from which certificate is valid
        Date expiryDate = new Date(startDate.getTime() + (10L * 365L * 24L * 60L * 60L * 1000L)); // time after which certificate is not valid
        BigInteger serialNumber = securityServerID.toBigInteger(); // serial number for certificate
        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC");

        kpGen.initialize(1024, new SecureRandom());
        KeyPair keyPair = kpGen.generateKeyPair();

        X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
        X500Principal dnName = new X500Principal(
                "CN=Indelible FS Auto-generated Root, UID=" + securityServerID.toString());

        certGen.setSerialNumber(serialNumber);
        certGen.setIssuerDN(dnName);
        certGen.setNotBefore(startDate);
        certGen.setNotAfter(expiryDate);
        certGen.setSubjectDN(dnName); // note: same as issuer
        certGen.setPublicKey(keyPair.getPublic());
        certGen.setSignatureAlgorithm(kCertificateSignatureAlg);

        X509Certificate cert = certGen.generate(keyPair.getPrivate(), "BC");

        initKeyStore.setCertificateEntry(kRootCertAlias, cert);
        initKeyStore.setKeyEntry(kSigningKeyAlias, keyPair.getPrivate(), kDefaultKeyStorePassword.toCharArray(),
                new Certificate[] { cert });

        FileOutputStream keyStoreOutputStream = new FileOutputStream(keyStoreFile);
        initKeyStore.store(keyStoreOutputStream, kDefaultKeyStorePassword.toCharArray());
        keyStoreOutputStream.close();
    }

    public byte[] entityAuthenticationServerChallenge(byte[] bytesToSign) {
        Signature signingSignature;
        try {
            signingSignature = Signature.getInstance(kChallengeSignatureAlg, "BC");
            signingSignature.initSign(signingKey);
            signingSignature.update(bytesToSign);
            byte[] signedBytes = signingSignature.sign();
            return signedBytes;
        } catch (NoSuchAlgorithmException e) {
            Logger.getLogger(getClass()).error(new ErrorLogMessage("Caught exception"), e);
        } catch (NoSuchProviderException e) {
            Logger.getLogger(getClass()).error(new ErrorLogMessage("Caught exception"), e);
        } catch (InvalidKeyException e) {
            Logger.getLogger(getClass()).error(new ErrorLogMessage("Caught exception"), e);
        } catch (SignatureException e) {
            Logger.getLogger(getClass()).error(new ErrorLogMessage("Caught exception"), e);
        }
        throw new InternalError("Could not generate signing signature");
    }

    @Override
    public SocketAddress getServerAddress() throws RemoteException {
        if (serverAddress == null)
            throw new InternalError("serverAddress has not been set");
        return serverAddress;
    }

    public void setServerAddress(InetSocketAddress serverAddress) {
        if (serverAddress == null)
            throw new IllegalArgumentException("serverAddress cannot be null");
        this.serverAddress = serverAddress;
    }

    public String toString() {
        return serverAddress.toString() + " " + getEntityID();
    }

    @Override
    public void close() throws IOException {
        // no-op for now
    }

    @Override
    public void close(long timeout) throws IOException {
        // no-op for now
    }

    @Override
    public boolean isClosed() {
        return false;
    }
}