package com.jscape.util.keystore;

import com.jscape.util.IOTools;
import com.jscape.util.crypto.CryptoServicesProvider;
import com.jscape.util.io.DataCodec;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.Provider;
import java.security.PublicKey;
import java.security.interfaces.DSAParams;
import java.security.interfaces.DSAPrivateKey;
import java.security.spec.DSAPublicKeySpec;
import java.security.spec.PKCS8EncodedKeySpec;

import static com.jscape.util.ExceptionTools.ioException;

/**
 * @author Alex Usun
 */
public class Pkcs8DsaKeyPairCodec
        implements DataCodec<KeyPair> {

    private final Provider provider;

    public static KeyPair readValue(InputStream in, Provider provider)
            throws IOException {
        byte[] data = IOTools.readFully(in);

        try {
            PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(data);

            KeyFactory factory = provider != null ? KeyFactory.getInstance("DSA", provider) : KeyFactory.getInstance("DSA");

            DSAPrivateKey privateKey = (DSAPrivateKey) factory.generatePrivate(spec);

            DSAParams params = privateKey.getParams();
            BigInteger y = params.getG().modPow(privateKey.getX(), params.getP());
            DSAPublicKeySpec publicKeySpec = new DSAPublicKeySpec(y, params.getP(), params.getQ(), params.getG());
            PublicKey publicKey = factory.generatePublic(publicKeySpec);

            return new KeyPair(publicKey, privateKey);
        } catch (Exception e) {
            throw ioException(e);
        }
    }

    public static void writeValue(KeyPair value, OutputStream out)
            throws IOException {
        try {
            byte[] bytes = value.getPrivate().getEncoded();
            out.write(bytes);
        } catch (Exception e) {
            throw ioException(e);
        }
    }

    public static Pkcs8DsaKeyPairCodec defaultCodec() {
        return new Pkcs8DsaKeyPairCodec(null);
    }

    public static Pkcs8DsaKeyPairCodec bcCodec() {
        return new Pkcs8DsaKeyPairCodec(CryptoServicesProvider.instance());
    }

    public Pkcs8DsaKeyPairCodec(Provider provider) {
        this.provider = provider;
    }

    @Override
    public KeyPair read(InputStream in)
            throws IOException {
        return readValue(in, this.provider);
    }

    @Override
    public void write(KeyPair value, OutputStream out)
            throws IOException {
        writeValue(value, out);
    }
}
