package org.bouncycastle.its.bc;

import java.io.IOException;
import java.io.OutputStream;

import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.sec.SECObjectIdentifiers;
import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.io.DigestOutputStream;
import org.bouncycastle.crypto.params.ECNamedDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.signers.DSADigestSigner;
import org.bouncycastle.crypto.signers.ECDSASigner;
import org.bouncycastle.its.ITSCertificate;
import org.bouncycastle.its.operator.ITSContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.bc.BcDefaultDigestProvider;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;

public class BcITSContentSigner
    implements ITSContentSigner
{
    private final ECPrivateKeyParameters privKey;
    private final ITSCertificate signerCert;
    private final AlgorithmIdentifier digestAlgo;
    private final Digest digest;
    private final byte[] parentData;
    private final ASN1ObjectIdentifier curveID;
    private final byte[] parentDigest;

    /**
     * Constructor for self-signing.
     *
     * @param privKey
     */
    public BcITSContentSigner(ECPrivateKeyParameters privKey)
    {
        this(privKey, null);
    }

    public BcITSContentSigner(ECPrivateKeyParameters privKey, ITSCertificate signerCert)
    {
        this.privKey = privKey;
        this.curveID = ((ECNamedDomainParameters)privKey.getParameters()).getName();
        this.signerCert = signerCert;
        if (curveID.equals(SECObjectIdentifiers.secp256r1))
        {
            digestAlgo = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256);
        }
        else if (curveID.equals(TeleTrusTObjectIdentifiers.brainpoolP256r1))
        {
            digestAlgo = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256);
        }
        else if (curveID.equals(TeleTrusTObjectIdentifiers.brainpoolP384r1))
        {
            digestAlgo = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha384);
        }
        else
        {
            throw new IllegalArgumentException("unknown key type");
        }

        try
        {
            this.digest = BcDefaultDigestProvider.INSTANCE.get(digestAlgo);
        }
        catch (OperatorCreationException e)
        {
            throw new IllegalStateException("cannot recognise digest type: " + digestAlgo.getAlgorithm());
        }

        if (signerCert != null)
        {
            try
            {
                this.parentData = signerCert.getEncoded();
                this.parentDigest = new byte[digest.getDigestSize()];

                digest.update(parentData, 0, parentData.length);

                digest.doFinal(parentDigest, 0);
            }
            catch (IOException e)
            {
                throw new IllegalStateException("signer certificate encoding failed: " + e.getMessage());
            }
        }
        else
        {
            this.parentData = null;
            this.parentDigest = null;
        }
    }

    public ITSCertificate getAssociatedCertificate()
    {
        return signerCert;
    }

    public byte[] getAssociatedCertificateDigest()
    {
        return Arrays.clone(parentDigest);
    }

    public AlgorithmIdentifier getDigestAlgorithm()
    {
        return digestAlgo;
    }

    public OutputStream getOutputStream()
    {
        return new DigestOutputStream(digest);
    }

    public boolean isForSelfSigning()
    {
        return parentDigest == null;
    }

    public byte[] getSignature()
    {
        byte[] clientCertDigest = new byte[digest.getDigestSize()];


        digest.doFinal(clientCertDigest, 0);

        //System.out.println("Generate tbs hash: " + Hex.toHexString(clientCertDigest));

        final DSADigestSigner signer;

        try
        {
            signer = new DSADigestSigner(new ECDSASigner(), BcDefaultDigestProvider.INSTANCE.get(digestAlgo));
        }
        catch (OperatorCreationException e)
        {
            throw new RuntimeException(e.getMessage(), e);
        }

        signer.init(true, privKey);

        signer.update(clientCertDigest, 0, clientCertDigest.length);

        //
        // if parent digest is null we've been created specifically to sign ourselves.
        //
        if (parentDigest == null)
        {
            byte[] empty = new byte[digest.getDigestSize()];
            digest.doFinal(empty, 0);

            //System.out.println("gen " + Hex.toHexString(empty));

            signer.update(empty, 0, empty.length);
        }
        else
        {
            signer.update(parentDigest, 0, parentDigest.length);
        }

        return signer.generateSignature();
    }
}

@@ -956,51 +956,8 @@
 ays;
-%0Aimport org.bouncycastle.util.encoders.Hex;
 %0A%0Apu
@@ -3212,68 +3212,208 @@
     
-this.parentData = null;%0A            this.parentDigest = null
+// self signed so we use a null digest for the parent.%0A            this.parentData = null;%0A            this.parentDigest = new byte%5Bdigest.getDigestSize()%5D;%0A            digest.doFinal(parentDigest, 0)
 ;%0A  
@@ -3883,29 +3883,27 @@
 turn parentD
-igest
+ata
  == null;%0A  
@@ -4074,912 +4074,195 @@
     
-//System.out.println(%22Generate tbs hash: %22 + Hex.toHexString(clientCertDigest));%0A%0A        final DSADigestSigner signer;%0A%0A        try%0A        %7B%0A            signer = new DSADigestSigner(new ECDSASigner(), BcDefaultDigestProvider.INSTANCE.get(digestAlgo));%0A        %7D%0A        catch (OperatorCreationException e)%0A        %7B%0A            throw new RuntimeException(e.getMessage(), e);%0A        %7D%0A%0A        signer.init(true, privKey);%0A%0A        signer.update(clientCertDigest, 0, clientCertDigest.length);%0A%0A        //%0A        // if parent digest is null we've been created specifically to sign ourselves.%0A        //%0A        if (parentDigest == null)%0A        %7B%0A            byte%5B%5D empty = new byte%5Bdigest.getDigestSize()%5D;%0A            digest.doFinal(empty, 0);%0A%0A            //System.out.println(%22gen %22 + Hex.toHexString(empty));%0A%0A            signer.update(empty, 0, empty.length);%0A        %7D%0A        else%0A        %7B%0A    
+final DSADigestSigner signer = new DSADigestSigner(new ECDSASigner(), digest);%0A%0A        signer.init(true, privKey);%0A%0A        signer.update(clientCertDigest, 0, clientCertDigest.length);%0A%0A
     
@@ -4317,26 +4317,16 @@
 length);
-%0A        %7D
 %0A%0A      

refactor to remove extra digest creation.