| 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. |
| * <p> |
| * 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. |
| * </p> |
| * If you're after the most control with generating a response use: |
| * <pre> |
| * TimeStampResponse tsResp; |
| * |
| * try |
| * { |
| * tsResp = tsRespGen.generateGrantedResponse(request, new BigInteger("23"), new Date()); |
| * } |
| * catch (Exception e) |
| * { |
| * tsResp = tsRespGen.generateRejectedResponse(e); |
| * } |
| * </pre> |
| * The generate method does this, but provides a status string of "Operation Okay". |
| * <p> |
| * 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. |
| * </p> |
| */ |
| 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. |
| * <p> |
| * If genTime is null a timeNotAvailable error response will be returned. Calling generate() is the |
| * equivalent of: |
| * <pre> |
| * TimeStampResponse tsResp; |
| * |
| * try |
| * { |
| * tsResp = tsRespGen.generateGrantedResponse(request, serialNumber, genTime, "Operation Okay"); |
| * } |
| * catch (Exception e) |
| * { |
| * tsResp = tsRespGen.generateRejectedResponse(e); |
| * } |
| * </pre> |
| * @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. |
| * <p> |
| * 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. |
| * </p> |
| * @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. |
| * <p> |
| * 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. |
| * </p> |
| * @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. |
| * <p> |
| * 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. |
| * </p> |
| * @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; |
| } |
| |
| class FailInfo extends DERBitString |
| { |
| FailInfo(int failInfoValue) |
| { |
| super(getBytes(failInfoValue), getPadBits(failInfoValue)); |
| } |
| } |
| } |