package org.bouncycastle.jce;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.ByteArrayInputStream;
import java.security.KeyStore;
import java.security.Security;

import javax.crypto.SecretKeyFactory;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.PBEParameterSpec;
import javax.crypto.spec.PBEKeySpec;

import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.DERObject;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.DigestInfo;
import org.bouncycastle.asn1.util.ASN1Dump;
import org.bouncycastle.asn1.pkcs.Pfx;
import org.bouncycastle.asn1.pkcs.ContentInfo;
import org.bouncycastle.asn1.pkcs.MacData;
import org.bouncycastle.jce.provider.JCEPBEKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

/**
 * Utility class for reencoding PKCS
 */
public class PKCS12Util
{
    /**
     * Just re-encode the outer layer of the PKCS#12 file to definite length encoding.
     *
     * @param berPKCS12File - original PKCS#12 file
     * @return a byte array representing the DER encoding of the PFX structure
     * @throws IOException
     */
    public static byte[] convertToDefiniteLength(byte[] berPKCS12File)
        throws IOException
    {
        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
        DEROutputStream dOut = new DEROutputStream(bOut);

        Pfx pfx = new Pfx(ASN1Sequence.getInstance(ASN1Object.fromByteArray(berPKCS12File)));

        bOut.reset();

        dOut.writeObject(pfx);

        return bOut.toByteArray();
    }

    /**
     * Re-encode the PKCS#12 structure to definite length encoding at the inner layer
     * as well, recomputing the MAC accordingly.
     *
     * @param berPKCS12File - original PKCS12 file.
     * @param provider - provider to use for MAC calculation.
     * @return a byte array representing the DER encoding of the PFX structure.
     * @throws IOException on parsing, encoding errors.
     */
    public static byte[] convertToDefiniteLength(byte[] berPKCS12File, char[] passwd, String provider)
        throws IOException
    {
        Pfx pfx = new Pfx(ASN1Sequence.getInstance(ASN1Object.fromByteArray(berPKCS12File)));

        ContentInfo info = pfx.getAuthSafe();

        ASN1OctetString content = ASN1OctetString.getInstance(info.getContent());

        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
        DEROutputStream dOut = new DEROutputStream(bOut);

        ASN1InputStream contentIn = new ASN1InputStream(content.getOctets());
        DERObject obj;

        while ((obj = contentIn.readObject()) != null)
        {
            dOut.writeObject(obj);
        }

        info = new ContentInfo(info.getContentType(), new DEROctetString(bOut.toByteArray()));

        MacData mData = pfx.getMacData();
        try
        {
            int itCount = mData.getIterationCount().intValue();
            byte[] data = ASN1OctetString.getInstance(info.getContent()).getOctets();
            byte[] res = calculatePbeMac(mData.getMac().getAlgorithmId().getObjectId(), mData.getSalt(), itCount, passwd, data, provider);

            AlgorithmIdentifier algId = new AlgorithmIdentifier(mData.getMac().getAlgorithmId().getObjectId(), new DERNull());
            DigestInfo dInfo = new DigestInfo(algId, res);

            mData = new MacData(dInfo, mData.getSalt(), itCount);
        }
        catch (Exception e)
        {
            throw new IOException("error constructing MAC: " + e.toString());
        }
        
        pfx = new Pfx(info, mData);

        bOut.reset();
        
        dOut.writeObject(pfx);
        
        return bOut.toByteArray();
    }

    private static byte[] calculatePbeMac(
        DERObjectIdentifier oid,
        byte[]              salt,
        int                 itCount,
        char[]              password,
        byte[]              data,
        String              provider)
        throws Exception
    {
        SecretKeyFactory keyFact = SecretKeyFactory.getInstance(oid.getId(), provider);
        PBEParameterSpec defParams = new PBEParameterSpec(salt, itCount);
        PBEKeySpec pbeSpec = new PBEKeySpec(password);
        SecretKey key = keyFact.generateSecret(pbeSpec);

        Mac mac = Mac.getInstance(oid.getId(), provider);
        mac.init(key, defParams);
        mac.update(data);

        return mac.doFinal();
    }
}

@@ -94,140 +94,34 @@
 on;%0A
-import java.io.FileInputStream;%0Aimport java.io.ByteArrayInputStream;%0Aimport java.security.KeyStore;%0Aimport java.security.Security
+%0Aimport javax.crypto.Mac
 ;%0A
-%0A
 impo
@@ -149,42 +149,10 @@
 tKey
-Factory
 ;%0A
-import javax.crypto.Mac;%0A
 impo
@@ -168,32 +168,39 @@
 crypto.SecretKey
+Factory
 ;%0Aimport javax.c
@@ -213,25 +213,19 @@
 spec.PBE
-Parameter
+Key
 Spec;%0Aim
@@ -246,27 +246,33 @@
 pto.spec.PBE
-Key
+Parameter
 Spec;%0A%0Aimpor
@@ -295,22 +295,22 @@
 le.asn1.
-DEROut
+ASN1In
 putStrea
@@ -357,51 +357,8 @@
 ct;%0A
-import org.bouncycastle.asn1.ASN1Sequence;%0A
 impo
@@ -436,59 +436,16 @@
 ASN1
-InputStream;%0Aimport org.bouncycastle.asn1.DERObject
+Sequence
 ;%0Aim
@@ -474,27 +474,20 @@
 asn1.DER
-OctetString
+Null
 ;%0Aimport
@@ -504,36 +504,38 @@
 ycastle.asn1.DER
-Null
+Object
 ;%0Aimport org.bou
@@ -603,32 +603,22 @@
 sn1.
-x509.AlgorithmIdentifier
+DEROctetString
 ;%0Aim
@@ -648,106 +648,23 @@
 sn1.
-x509.DigestInfo;%0Aimport org.bouncycastle.asn1.util.ASN1Dump;%0Aimport org.bouncycastle.asn1.pkcs.Pfx
+DEROutputStream
 ;%0Aim
@@ -779,89 +779,122 @@
 tle.
-jce.provider.JCEPBEKey;%0Aimport org.bouncycastle.jce.provider.BouncyCastleProvider
+asn1.pkcs.Pfx;%0Aimport org.bouncycastle.asn1.x509.AlgorithmIdentifier;%0Aimport org.bouncycastle.asn1.x509.DigestInfo
 ;%0A%0A/

Remove unused imports