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.
/** * 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.
Params:
  • sender – message sender.
  • recipient – intended recipient.
/** * 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.
Params:
  • pvno – the version CMP_1999 or CMP_2000.
  • sender – message sender.
  • recipient – intended 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.
Params:
  • tid – the transaction ID.
Returns:the current builder instance.
/** * 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.
Params:
  • freeText – the contents of the human readable message,
Returns:the current builder instance.
/** * 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.
Params:
  • genInfo – the generalInfo data to be added.
Returns:the current builder instance.
/** * 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.
Params:
  • time – the message creation time.
Returns:the current builder instance.
/** * 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.
Params:
  • kid – a key identifier.
Returns:the current builder instance.
/** * 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.
Params:
  • nonce – a NONCE, typically copied from the sender nonce of the previous message.
Returns:the current builder instance.
/** * 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.
Params:
  • kid – a key identifier.
Returns:the current builder instance.
/** * 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.
Params:
  • nonce – a NONCE, typically 128 bits of random data.
Returns:the current builder instance.
/** * 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
Params:
  • body – the message body.
Returns:the current builder instance.
/** * 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.
Params:
  • extraCert – the extra certificate to add.
Returns:the current builder instance.
/** * 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.
Params:
  • macCalculator – MAC calculator.
Throws:
  • CMPException – if the protection MAC cannot be calculated.
Returns:the resulting protected PKI message.
/** * 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.
Params:
  • signer – the ContentSigner to be used to calculate the signature.
Throws:
  • CMPException – if the protection signature cannot be calculated.
Returns:the resulting protected PKI message.
/** * 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(); } }