blob: abbdaed8b3db9f6499199339db720d69a2b2b6c8 [file] [log] [blame]
package org.bouncycastle.cert.crmf;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.security.SecureRandom;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.cmp.CMPObjectIdentifiers;
import org.bouncycastle.asn1.cmp.PBMParameter;
import org.bouncycastle.asn1.iana.IANAObjectIdentifiers;
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.operator.GenericKey;
import org.bouncycastle.operator.MacCalculator;
import org.bouncycastle.operator.RuntimeOperatorException;
import org.bouncycastle.util.Strings;
public class PKMACBuilder
{
private AlgorithmIdentifier owf;
private int iterationCount;
private AlgorithmIdentifier mac;
private int saltLength = 20;
private SecureRandom random;
private PKMACValuesCalculator calculator;
private PBMParameter parameters;
private int maxIterations;
public PKMACBuilder(PKMACValuesCalculator calculator)
{
this(new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1), 1000, new AlgorithmIdentifier(IANAObjectIdentifiers.hmacSHA1, DERNull.INSTANCE), calculator);
}
/**
* Create a PKMAC builder enforcing a ceiling on the maximum iteration count.
*
* @param calculator supporting calculator
* @param maxIterations max allowable value for iteration count.
*/
public PKMACBuilder(PKMACValuesCalculator calculator, int maxIterations)
{
this.maxIterations = maxIterations;
this.calculator = calculator;
}
private PKMACBuilder(AlgorithmIdentifier hashAlgorithm, int iterationCount, AlgorithmIdentifier macAlgorithm, PKMACValuesCalculator calculator)
{
this.owf = hashAlgorithm;
this.iterationCount = iterationCount;
this.mac = macAlgorithm;
this.calculator = calculator;
}
/**
* Set the salt length in octets.
*
* @param saltLength length in octets of the salt to be generated.
* @return the generator
*/
public PKMACBuilder setSaltLength(int saltLength)
{
if (saltLength < 8)
{
throw new IllegalArgumentException("salt length must be at least 8 bytes");
}
this.saltLength = saltLength;
return this;
}
public PKMACBuilder setIterationCount(int iterationCount)
{
if (iterationCount < 100)
{
throw new IllegalArgumentException("iteration count must be at least 100");
}
checkIterationCountCeiling(iterationCount);
this.iterationCount = iterationCount;
return this;
}
public PKMACBuilder setSecureRandom(SecureRandom random)
{
this.random = random;
return this;
}
public PKMACBuilder setParameters(PBMParameter parameters)
{
checkIterationCountCeiling(parameters.getIterationCount().getValue().intValue());
this.parameters = parameters;
return this;
}
public MacCalculator build(char[] password)
throws CRMFException
{
if (parameters != null)
{
return genCalculator(parameters, password);
}
else
{
byte[] salt = new byte[saltLength];
if (random == null)
{
this.random = new SecureRandom();
}
random.nextBytes(salt);
return genCalculator(new PBMParameter(salt, owf, iterationCount, mac), password);
}
}
private void checkIterationCountCeiling(int iterationCount)
{
if (maxIterations > 0 && iterationCount > maxIterations)
{
throw new IllegalArgumentException("iteration count exceeds limit (" + iterationCount + " > " + maxIterations + ")");
}
}
private MacCalculator genCalculator(final PBMParameter params, char[] password)
throws CRMFException
{
// From RFC 4211
//
// 1. Generate a random salt value S
//
// 2. Append the salt to the pw. K = pw || salt.
//
// 3. Hash the value of K. K = HASH(K)
//
// 4. Iter = Iter - 1. If Iter is greater than zero. Goto step 3.
//
// 5. Compute an HMAC as documented in [HMAC].
//
// MAC = HASH( K XOR opad, HASH( K XOR ipad, data) )
//
// Where opad and ipad are defined in [HMAC].
byte[] pw = Strings.toUTF8ByteArray(password);
byte[] salt = params.getSalt().getOctets();
byte[] K = new byte[pw.length + salt.length];
System.arraycopy(pw, 0, K, 0, pw.length);
System.arraycopy(salt, 0, K, pw.length, salt.length);
calculator.setup(params.getOwf(), params.getMac());
int iter = params.getIterationCount().getValue().intValue();
do
{
K = calculator.calculateDigest(K);
}
while (--iter > 0);
final byte[] key = K;
return new MacCalculator()
{
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
public AlgorithmIdentifier getAlgorithmIdentifier()
{
return new AlgorithmIdentifier(CMPObjectIdentifiers.passwordBasedMac, params);
}
public GenericKey getKey()
{
return new GenericKey(getAlgorithmIdentifier(), key);
}
public OutputStream getOutputStream()
{
return bOut;
}
public byte[] getMac()
{
try
{
return calculator.calculateMac(key, bOut.toByteArray());
}
catch (CRMFException e)
{
throw new RuntimeOperatorException("exception calculating mac: " + e.getMessage(), e);
}
}
};
}
}