org.spout.api.security.SecurityHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.spout.api.security.SecurityHandler.java

Source

/*
 * This file is part of SpoutAPI.
 *
 * Copyright (c) 2011-2012, SpoutDev <http://www.spout.org/>
 * SpoutAPI is licensed under the SpoutDev License Version 1.
 *
 * SpoutAPI is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition, 180 days after any changes are published, you can use the
 * software, incorporating those changes, under the terms of the MIT license,
 * as described in the SpoutDev License Version 1.
 *
 * SpoutAPI 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
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License,
 * the MIT license and the SpoutDev License Version 1 along with this program.
 * If not, see <http://www.gnu.org/licenses/> for the GNU Lesser General Public
 * License and see <http://www.spout.org/SpoutDevLicenseV1.txt> for the full license,
 * including the MIT license.
 */
package org.spout.api.security;

import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.Security;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.crypto.AsymmetricBlockCipher;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.encodings.PKCS1Encoding;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.engines.RSAEngine;
import org.bouncycastle.crypto.generators.RSAKeyPairGenerator;
import org.bouncycastle.crypto.modes.CFBBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.spout.api.Spout;

public class SecurityHandler {

    public static final boolean DECRYPT_MODE = false;
    public static final boolean ENCRYPT_MODE = true;

    private static final ConcurrentHashMap<String, AsymmetricCipherKeyPair> serverKeys = new ConcurrentHashMap<String, AsymmetricCipherKeyPair>();
    private static final Provider provider;
    private static final SecurityHandler instance;

    static {
        Provider p = Security.getProvider("BC");
        if (p == null) {
            Security.addProvider(new BouncyCastleProvider());
            p = Security.getProvider("BC");
            if (p == null) {
                Spout.getLogger().info("Unable to start security provider");
            }
        }
        provider = p;
        instance = new SecurityHandler();
    }

    public static SecurityHandler getInstance() {
        return instance;
    }

    public byte[] encodeKey(CipherParameters key) {
        if (!(key instanceof RSAKeyParameters)) {
            return null;
        }

        if (((RSAKeyParameters) key).isPrivate()) {
            return null;
        }

        RSAKeyParameters rsaKey = (RSAKeyParameters) key;

        ASN1EncodableVector encodable = new ASN1EncodableVector();
        encodable.add(new ASN1Integer(rsaKey.getModulus()));
        encodable.add(new ASN1Integer(rsaKey.getExponent()));

        return KeyUtil.getEncodedSubjectPublicKeyInfo(
                new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, new DERNull()),
                new DERSequence(encodable));
    }

    public BufferedBlockCipher getSymmetricCipher(String cipher, String wrapper) {
        if (cipher.equals("AES")) {
            return addSymmetricWrapper(new AESEngine(), wrapper);
        }

        return null;
    }

    private BufferedBlockCipher addSymmetricWrapper(BlockCipher rawCipher, String wrapper) {
        if (wrapper.startsWith("CFB")) {
            int bits;
            try {
                bits = Integer.parseInt(wrapper.substring(3));
            } catch (NumberFormatException e) {
                Spout.getLogger().info("Unable to parse bits for CFB wrapper from: " + wrapper);
                return null;
            }
            return new BufferedBlockCipher(new CFBBlockCipher(rawCipher, bits));
        }

        return new BufferedBlockCipher(rawCipher);
    }

    public PaddedBufferedBlockCipher addSymmetricPadding(BlockCipher rawCipher, String padding) {
        if (padding.equals("PKCS7")) {
            return new PaddedBufferedBlockCipher(rawCipher);
        }

        return null;
    }

    public AsymmetricBlockCipher getAsymmetricCipher(String cipher, String padding) {
        if (cipher.equals("RSA")) {
            return addAsymmetricPadding(new RSAEngine(), padding);
        }

        return null;
    }

    private AsymmetricBlockCipher addAsymmetricPadding(AsymmetricBlockCipher rawCipher, String padding) {
        if (padding == null) {
            return rawCipher;
        } else if (padding.equals("PKCS1")) {
            return new PKCS1Encoding(rawCipher);
        } else {
            return null;
        }
    }

    public AsymmetricCipherKeyPairGenerator getGenerator(String algorithm) {
        if (algorithm.equals("RSA")) {
            return new RSAKeyPairGenerator();
        }

        Spout.getLogger().info("Unable to find key generator " + algorithm);
        return null;
    }

    public void initGenerator(int keySize, String algorithm, AsymmetricCipherKeyPairGenerator generator,
            SecureRandom random) {
        if (algorithm.equals("RSA")) {
            RSAKeyGenerationParameters params = new RSAKeyGenerationParameters(new BigInteger("10001", 16), random,
                    keySize, 80);
            generator.init(params);
        }
    }

    public byte[] processAll(AsymmetricBlockCipher cipher, byte[] input) {
        int outputSize = 0;
        int blockSize = cipher.getInputBlockSize();
        List<byte[]> outputBlocks = new LinkedList<byte[]>();

        int pos = 0;

        while (pos < input.length) {
            int length = Math.min(input.length - pos, blockSize);
            byte[] result;
            try {
                result = cipher.processBlock(input, pos, length);
            } catch (InvalidCipherTextException e) {
                Spout.getLogger().info("Error processing encrypted data");
                return null;
            }
            outputSize += result.length;
            outputBlocks.add(result);
            pos += length;
        }

        byte[] output = new byte[outputSize];

        pos = 0;
        for (byte[] block : outputBlocks) {
            System.arraycopy(block, 0, output, pos, block.length);
            pos += block.length;
        }

        return output;
    }

    public AsymmetricCipherKeyPair getKeyPair(String algorithm) {
        return getKeyPair(1024, algorithm);
    }

    public AsymmetricCipherKeyPair getKeyPair(int keySize, String algorithm) {
        return getKeyPair(keySize, algorithm, "SHA1PRNG", "SUN");
    }

    public AsymmetricCipherKeyPair getKeyPair(int keySize, String algorithm, String RNGAlgorithm,
            String RNGProvider) {
        AsymmetricCipherKeyPair pair = serverKeys.get(algorithm);
        if (pair != null) {
            return pair;
        }

        if (provider == null) {
            return pair;
        }

        SecureRandom secureRandom = getSecureRandom(RNGAlgorithm, RNGProvider);
        if (secureRandom == null) {
            return null;
        }

        AsymmetricCipherKeyPairGenerator generator = getGenerator(algorithm);

        if (generator == null) {
            return null;
        }

        initGenerator(keySize, algorithm, generator, secureRandom);

        AsymmetricCipherKeyPair newPair = generator.generateKeyPair();

        AsymmetricCipherKeyPair oldPair = serverKeys.putIfAbsent(algorithm, newPair);
        if (oldPair != null) {
            return oldPair;
        }

        return newPair;
    }

    private SecureRandom getSecureRandom(String RNGAlgorithm, String RNGProvider) {
        try {
            if (RNGProvider != null) {
                return SecureRandom.getInstance(RNGAlgorithm, RNGProvider);
            } else {
                return SecureRandom.getInstance(RNGAlgorithm);
            }
        } catch (NoSuchProviderException e) {
            //Fallback to any provider for the algorithm
            return getSecureRandom(RNGAlgorithm, null);
        } catch (NoSuchAlgorithmException e) {
            Spout.getLogger()
                    .severe("Unable to find algorithm to generate random number generator for key pair creation ("
                            + RNGAlgorithm + ", " + RNGProvider + ")");
            return null;
        }
    }
}