org.bouncycastle.tsp.TimeStampResponseGenerator Maven / Gradle / Ivy
package org.bouncycastle.tsp;
import java.io.IOException;
import java.math.BigInteger;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERUTF8String;
import org.bouncycastle.asn1.DLSequence;
import org.bouncycastle.asn1.cmp.PKIFailureInfo;
import org.bouncycastle.asn1.cmp.PKIFreeText;
import org.bouncycastle.asn1.cmp.PKIStatus;
import org.bouncycastle.asn1.cmp.PKIStatusInfo;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.asn1.tsp.TimeStampResp;
import org.bouncycastle.asn1.x509.Extensions;
/**
* Generator for RFC 3161 Time Stamp Responses.
*
* New generate methods have been introduced to give people more control over what ends up in the message.
* Unfortunately it turns out that in some cases fields like statusString must be left out otherwise a an
* otherwise valid timestamp will be rejected.
*
* If you're after the most control with generating a response use:
*
* TimeStampResponse tsResp;
*
* try
* {
* tsResp = tsRespGen.generateGrantedResponse(request, new BigInteger("23"), new Date());
* }
* catch (Exception e)
* {
* tsResp = tsRespGen.generateRejectedResponse(e);
* }
*
* The generate method does this, but provides a status string of "Operation Okay".
* * It should be pointed out that generateRejectedResponse() may also, on very rare occasions throw a TSPException. * In the event that happens, there's a serious internal problem with your responder. *
*/ public class TimeStampResponseGenerator { int status; ASN1EncodableVector statusStrings; int failInfo; private TimeStampTokenGenerator tokenGenerator; private Set acceptedAlgorithms; private Set acceptedPolicies; private Set acceptedExtensions; /** * * @param tokenGenerator * @param acceptedAlgorithms a set of OIDs giving accepted algorithms. */ public TimeStampResponseGenerator( TimeStampTokenGenerator tokenGenerator, Set acceptedAlgorithms) { this(tokenGenerator, acceptedAlgorithms, null, null); } /** * * @param tokenGenerator * @param acceptedAlgorithms a set of OIDs giving accepted algorithms. * @param acceptedPolicies if non-null a set of policies OIDs we are willing to sign under. */ public TimeStampResponseGenerator( TimeStampTokenGenerator tokenGenerator, Set acceptedAlgorithms, Set acceptedPolicies) { this(tokenGenerator, acceptedAlgorithms, acceptedPolicies, null); } /** * * @param tokenGenerator * @param acceptedAlgorithms a set of OIDs giving accepted algorithms. * @param acceptedPolicies if non-null a set of policies OIDs we are willing to sign under. * @param acceptedExtensions if non-null a set of extensions OIDs we are willing to accept. */ public TimeStampResponseGenerator( TimeStampTokenGenerator tokenGenerator, Set acceptedAlgorithms, Set acceptedPolicies, Set acceptedExtensions) { this.tokenGenerator = tokenGenerator; this.acceptedAlgorithms = convert(acceptedAlgorithms); this.acceptedPolicies = convert(acceptedPolicies); this.acceptedExtensions = convert(acceptedExtensions); statusStrings = new ASN1EncodableVector(); } private void addStatusString(String statusString) { statusStrings.add(new DERUTF8String(statusString)); } private void setFailInfoField(int field) { failInfo = failInfo | field; } private PKIStatusInfo getPKIStatusInfo() { ASN1EncodableVector v = new ASN1EncodableVector(); v.add(new ASN1Integer(status)); if (statusStrings.size() > 0) { v.add(PKIFreeText.getInstance(new DERSequence(statusStrings))); } if (failInfo != 0) { DERBitString failInfoBitString = new FailInfo(failInfo); v.add(failInfoBitString); } return PKIStatusInfo.getInstance(new DERSequence(v)); } /** * Return an appropriate TimeStampResponse. ** If genTime is null a timeNotAvailable error response will be returned. Calling generate() is the * equivalent of: * * TimeStampResponse tsResp; * * try * { * tsResp = tsRespGen.generateGrantedResponse(request, serialNumber, genTime, "Operation Okay"); * } * catch (Exception e) * { * tsResp = tsRespGen.generateRejectedResponse(e); * } * * @param request the request this response is for. * @param serialNumber serial number for the response token. * @param genTime generation time for the response token. * @return a TimeStampResponse. * @throws TSPException */ public TimeStampResponse generate( TimeStampRequest request, BigInteger serialNumber, Date genTime) throws TSPException { try { return this.generateGrantedResponse(request, serialNumber, genTime, "Operation Okay"); } catch (Exception e) { return this.generateRejectedResponse(e); } } /** * Return a granted response, if the passed in request passes validation. *
* If genTime is null a timeNotAvailable or a validation exception occurs a TSPValidationException will * be thrown. The parent TSPException will only occur on some sort of system failure. *
* @param request the request this response is for. * @param serialNumber serial number for the response token. * @param genTime generation time for the response token. * @return the TimeStampResponse with a status of PKIStatus.GRANTED * @throws TSPException on validation exception or internal error. */ public TimeStampResponse generateGrantedResponse( TimeStampRequest request, BigInteger serialNumber, Date genTime) throws TSPException { return generateGrantedResponse(request, serialNumber, genTime, null); } /** * Return a granted response, if the passed in request passes validation with the passed in status string. ** If genTime is null a timeNotAvailable or a validation exception occurs a TSPValidationException will * be thrown. The parent TSPException will only occur on some sort of system failure. *
* @param request the request this response is for. * @param serialNumber serial number for the response token. * @param genTime generation time for the response token. * @return the TimeStampResponse with a status of PKIStatus.GRANTED * @throws TSPException on validation exception or internal error. */ public TimeStampResponse generateGrantedResponse( TimeStampRequest request, BigInteger serialNumber, Date genTime, String statusString) throws TSPException { return generateGrantedResponse(request, serialNumber, genTime, statusString, null); } /** * Return a granted response, if the passed in request passes validation with the passed in status string and extra extensions. ** If genTime is null a timeNotAvailable or a validation exception occurs a TSPValidationException will * be thrown. The parent TSPException will only occur on some sort of system failure. *
* @param request the request this response is for. * @param serialNumber serial number for the response token. * @param genTime generation time for the response token. * @param additionalExtensions extra extensions to be added to the response token. * @return the TimeStampResponse with a status of PKIStatus.GRANTED * @throws TSPException on validation exception or internal error. */ public TimeStampResponse generateGrantedResponse( TimeStampRequest request, BigInteger serialNumber, Date genTime, String statusString, Extensions additionalExtensions) throws TSPException { if (genTime == null) { throw new TSPValidationException("The time source is not available.", PKIFailureInfo.timeNotAvailable); } request.validate(acceptedAlgorithms, acceptedPolicies, acceptedExtensions); status = PKIStatus.GRANTED; statusStrings = new ASN1EncodableVector(); if (statusString != null) { this.addStatusString(statusString); } PKIStatusInfo pkiStatusInfo = getPKIStatusInfo(); ContentInfo tstTokenContentInfo; try { tstTokenContentInfo = tokenGenerator.generate(request, serialNumber, genTime, additionalExtensions).toCMSSignedData().toASN1Structure(); } catch (TSPException e) { throw e; } catch (Exception e) { throw new TSPException( "Timestamp token received cannot be converted to ContentInfo", e); } try { return new TimeStampResponse(new DLSequence(new ASN1Encodable[] { pkiStatusInfo.toASN1Primitive(), tstTokenContentInfo.toASN1Primitive() })); } catch (IOException e) { throw new TSPException("created badly formatted response!"); } } /** * Generate a generic rejection response based on a TSPValidationException or * an Exception. Exceptions which are not an instance of TSPValidationException * will be treated as systemFailure. The return value of exception.getMessage() will * be used as the status string for the response. * * @param exception the exception thrown on validating the request. * @return a TimeStampResponse. * @throws TSPException if a failure response cannot be generated. */ public TimeStampResponse generateRejectedResponse(Exception exception) throws TSPException { if (exception instanceof TSPValidationException) { return generateFailResponse(PKIStatus.REJECTION, ((TSPValidationException)exception).getFailureCode(), exception.getMessage()); } else { return generateFailResponse(PKIStatus.REJECTION, PKIFailureInfo.systemFailure, exception.getMessage()); } } /** * Generate a non-granted TimeStampResponse with chosen status and FailInfoField. * * @param status the PKIStatus to set. * @param failInfoField the FailInfoField to set. * @param statusString an optional string describing the failure. * @return a TimeStampResponse with a failInfoField and optional statusString * @throws TSPException in case the response could not be created */ public TimeStampResponse generateFailResponse(int status, int failInfoField, String statusString) throws TSPException { this.status = status; this.statusStrings = new ASN1EncodableVector(); this.setFailInfoField(failInfoField); if (statusString != null) { this.addStatusString(statusString); } PKIStatusInfo pkiStatusInfo = getPKIStatusInfo(); TimeStampResp resp = new TimeStampResp(pkiStatusInfo, null); try { return new TimeStampResponse(resp); } catch (IOException e) { throw new TSPException("created badly formatted response!"); } } private Set convert(Set orig) { if (orig == null) { return orig; } Set con = new HashSet(orig.size()); for (Iterator it = orig.iterator(); it.hasNext();) { Object o = it.next(); if (o instanceof String) { con.add(new ASN1ObjectIdentifier((String)o)); } else { con.add(o); } } return con; } static class FailInfo extends DERBitString { FailInfo(int failInfoValue) { super(getBytes(failInfoValue), getPadBits(failInfoValue)); } } }