blob: 29191567d02fcf5fc43ef604ac4fdf6ce79e87aa [file] [log] [blame]
package org.bouncycastle.cert.cmp;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1GeneralizedTime;
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.cmp.CMPCertificate;
import org.bouncycastle.asn1.cmp.InfoTypeAndValue;
import org.bouncycastle.asn1.cmp.PKIBody;
import org.bouncycastle.asn1.cmp.PKIFreeText;
import org.bouncycastle.asn1.cmp.PKIHeader;
import org.bouncycastle.asn1.cmp.PKIHeaderBuilder;
import org.bouncycastle.asn1.cmp.PKIMessage;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.MacCalculator;
/**
* Builder for creating a protected PKI message.
*/
public class ProtectedPKIMessageBuilder
{
private PKIHeaderBuilder hdrBuilder;
private PKIBody body;
private List generalInfos = new ArrayList();
private List extraCerts = new ArrayList();
/**
* Commence a message with the header version CMP_2000.
*
* @param sender message sender.
* @param recipient intended recipient.
*/
public ProtectedPKIMessageBuilder(GeneralName sender, GeneralName recipient)
{
this(PKIHeader.CMP_2000, sender, recipient);
}
/**
* Commence a message with a specific header type.
*
* @param pvno the version CMP_1999 or CMP_2000.
* @param sender message sender.
* @param recipient intended recipient.
*/
public ProtectedPKIMessageBuilder(int pvno, GeneralName sender, GeneralName recipient)
{
hdrBuilder = new PKIHeaderBuilder(pvno, sender, recipient);
}
/**
* Set the identifier for the transaction the new message will belong to.
*
* @param tid the transaction ID.
* @return the current builder instance.
*/
public ProtectedPKIMessageBuilder setTransactionID(byte[] tid)
{
hdrBuilder.setTransactionID(tid);
return this;
}
/**
* Include a human-readable message in the new message.
*
* @param freeText the contents of the human readable message,
* @return the current builder instance.
*/
public ProtectedPKIMessageBuilder setFreeText(PKIFreeText freeText)
{
hdrBuilder.setFreeText(freeText);
return this;
}
/**
* Add a generalInfo data record to the header of the new message.
*
* @param genInfo the generalInfo data to be added.
* @return the current builder instance.
*/
public ProtectedPKIMessageBuilder addGeneralInfo(InfoTypeAndValue genInfo)
{
generalInfos.add(genInfo);
return this;
}
/**
* Set the creation time for the new message.
*
* @param time the message creation time.
* @return the current builder instance.
*/
public ProtectedPKIMessageBuilder setMessageTime(Date time)
{
hdrBuilder.setMessageTime(new ASN1GeneralizedTime(time));
return this;
}
/**
* Set the recipient key identifier for the key to be used to verify the new message.
*
* @param kid a key identifier.
* @return the current builder instance.
*/
public ProtectedPKIMessageBuilder setRecipKID(byte[] kid)
{
hdrBuilder.setRecipKID(kid);
return this;
}
/**
* Set the recipient nonce field on the new message.
*
* @param nonce a NONCE, typically copied from the sender nonce of the previous message.
* @return the current builder instance.
*/
public ProtectedPKIMessageBuilder setRecipNonce(byte[] nonce)
{
hdrBuilder.setRecipNonce(nonce);
return this;
}
/**
* Set the sender key identifier for the key used to protect the new message.
*
* @param kid a key identifier.
* @return the current builder instance.
*/
public ProtectedPKIMessageBuilder setSenderKID(byte[] kid)
{
hdrBuilder.setSenderKID(kid);
return this;
}
/**
* Set the sender nonce field on the new message.
*
* @param nonce a NONCE, typically 128 bits of random data.
* @return the current builder instance.
*/
public ProtectedPKIMessageBuilder setSenderNonce(byte[] nonce)
{
hdrBuilder.setSenderNonce(nonce);
return this;
}
/**
* Set the body for the new message
*
* @param body the message body.
* @return the current builder instance.
*/
public ProtectedPKIMessageBuilder setBody(PKIBody body)
{
this.body = body;
return this;
}
/**
* Add an "extra certificate" to the message.
*
* @param extraCert the extra certificate to add.
* @return the current builder instance.
*/
public ProtectedPKIMessageBuilder addCMPCertificate(X509CertificateHolder extraCert)
{
extraCerts.add(extraCert);
return this;
}
/**
* Build a protected PKI message which has MAC based integrity protection.
*
* @param macCalculator MAC calculator.
* @return the resulting protected PKI message.
* @throws CMPException if the protection MAC cannot be calculated.
*/
public ProtectedPKIMessage build(MacCalculator macCalculator)
throws CMPException
{
finaliseHeader(macCalculator.getAlgorithmIdentifier());
PKIHeader header = hdrBuilder.build();
try
{
DERBitString protection = new DERBitString(calculateMac(macCalculator, header, body));
return finaliseMessage(header, protection);
}
catch (IOException e)
{
throw new CMPException("unable to encode MAC input: " + e.getMessage(), e);
}
}
/**
* Build a protected PKI message which has MAC based integrity protection.
*
* @param signer the ContentSigner to be used to calculate the signature.
* @return the resulting protected PKI message.
* @throws CMPException if the protection signature cannot be calculated.
*/
public ProtectedPKIMessage build(ContentSigner signer)
throws CMPException
{
finaliseHeader(signer.getAlgorithmIdentifier());
PKIHeader header = hdrBuilder.build();
try
{
DERBitString protection = new DERBitString(calculateSignature(signer, header, body));
return finaliseMessage(header, protection);
}
catch (IOException e)
{
throw new CMPException("unable to encode signature input: " + e.getMessage(), e);
}
}
private void finaliseHeader(AlgorithmIdentifier algorithmIdentifier)
{
hdrBuilder.setProtectionAlg(algorithmIdentifier);
if (!generalInfos.isEmpty())
{
InfoTypeAndValue[] genInfos = new InfoTypeAndValue[generalInfos.size()];
hdrBuilder.setGeneralInfo((InfoTypeAndValue[])generalInfos.toArray(genInfos));
}
}
private ProtectedPKIMessage finaliseMessage(PKIHeader header, DERBitString protection)
{
if (!extraCerts.isEmpty())
{
CMPCertificate[] cmpCerts = new CMPCertificate[extraCerts.size()];
for (int i = 0; i != cmpCerts.length; i++)
{
cmpCerts[i] = new CMPCertificate(((X509CertificateHolder)extraCerts.get(i)).toASN1Structure());
}
return new ProtectedPKIMessage(new PKIMessage(header, body, protection, cmpCerts));
}
else
{
return new ProtectedPKIMessage(new PKIMessage(header, body, protection));
}
}
private byte[] calculateSignature(ContentSigner signer, PKIHeader header, PKIBody body)
throws IOException
{
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(header);
v.add(body);
OutputStream sOut = signer.getOutputStream();
sOut.write(new DERSequence(v).getEncoded(ASN1Encoding.DER));
sOut.close();
return signer.getSignature();
}
private byte[] calculateMac(MacCalculator macCalculator, PKIHeader header, PKIBody body)
throws IOException
{
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(header);
v.add(body);
OutputStream sOut = macCalculator.getOutputStream();
sOut.write(new DERSequence(v).getEncoded(ASN1Encoding.DER));
sOut.close();
return macCalculator.getMac();
}
}