/* * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.security.timestamp; import java.io.IOException; import sun.security.pkcs.PKCS7; import sun.security.util.Debug; import sun.security.util.DerValue; /** * This class provides the response corresponding to a timestamp request, * as defined in * <a href="http://www.ietf.org/rfc/rfc3161.txt">RFC 3161</a>. * * The TimeStampResp ASN.1 type has the following definition: * <pre> * * TimeStampResp ::= SEQUENCE { * status PKIStatusInfo, * timeStampToken TimeStampToken OPTIONAL ] * * PKIStatusInfo ::= SEQUENCE { * status PKIStatus, * statusString PKIFreeText OPTIONAL, * failInfo PKIFailureInfo OPTIONAL } * * PKIStatus ::= INTEGER { * granted (0), * -- when the PKIStatus contains the value zero a TimeStampToken, as * -- requested, is present. * grantedWithMods (1), * -- when the PKIStatus contains the value one a TimeStampToken, * -- with modifications, is present. * rejection (2), * waiting (3), * revocationWarning (4), * -- this message contains a warning that a revocation is * -- imminent * revocationNotification (5) * -- notification that a revocation has occurred } * * PKIFreeText ::= SEQUENCE SIZE (1..MAX) OF UTF8String * -- text encoded as UTF-8 String (note: each UTF8String SHOULD * -- include an RFC 1766 language tag to indicate the language * -- of the contained text) * * PKIFailureInfo ::= BIT STRING { * badAlg (0), * -- unrecognized or unsupported Algorithm Identifier * badRequest (2), * -- transaction not permitted or supported * badDataFormat (5), * -- the data submitted has the wrong format * timeNotAvailable (14), * -- the TSA's time source is not available * unacceptedPolicy (15), * -- the requested TSA policy is not supported by the TSA * unacceptedExtension (16), * -- the requested extension is not supported by the TSA * addInfoNotAvailable (17) * -- the additional information requested could not be understood * -- or is not available * systemFailure (25) * -- the request cannot be handled due to system failure } * * TimeStampToken ::= ContentInfo * -- contentType is id-signedData * -- content is SignedData * -- eContentType within SignedData is id-ct-TSTInfo * -- eContent within SignedData is TSTInfo * * </pre> * * @since 1.5 * @author Vincent Ryan * @see Timestamper */ public class TSResponse { // Status codes (from RFC 3161) /** * The requested timestamp was granted. */ public static final int GRANTED = 0; /** * The requested timestamp was granted with some modifications. */ public static final int GRANTED_WITH_MODS = 1; /** * The requested timestamp was not granted. */ public static final int REJECTION = 2; /** * The requested timestamp has not yet been processed. */ public static final int WAITING = 3; /** * A warning that a certificate revocation is imminent. */ public static final int REVOCATION_WARNING = 4; /** * Notification that a certificate revocation has occurred. */ public static final int REVOCATION_NOTIFICATION = 5; // Failure codes (from RFC 3161) /** * Unrecognized or unsupported algorithm identifier. */ public static final int BAD_ALG = 0; /** * The requested transaction is not permitted or supported. */ public static final int BAD_REQUEST = 2; /** * The data submitted has the wrong format. */ public static final int BAD_DATA_FORMAT = 5; /** * The TSA's time source is not available. */ public static final int TIME_NOT_AVAILABLE = 14; /** * The requested TSA policy is not supported by the TSA. */ public static final int UNACCEPTED_POLICY = 15; /** * The requested extension is not supported by the TSA. */ public static final int UNACCEPTED_EXTENSION = 16; /** * The additional information requested could not be understood or is not * available. */ public static final int ADD_INFO_NOT_AVAILABLE = 17; /** * The request cannot be handled due to system failure. */ public static final int SYSTEM_FAILURE = 25; private static final Debug debug = Debug.getInstance("ts"); private int status; private String[] statusString = null; private boolean[] failureInfo = null; private byte[] encodedTsToken = null; private PKCS7 tsToken = null; private TimestampToken tstInfo; /** * Constructs an object to store the response to a timestamp request. * * @param status A buffer containing the ASN.1 BER encoded response. * @throws IOException The exception is thrown if a problem is encountered * parsing the timestamp response. */ TSResponse(byte[] tsReply) throws IOException { parse(tsReply); } /** * Retrieve the status code returned by the TSA. */ public int getStatusCode() { return status; } /** * Retrieve the status messages returned by the TSA. * * @return If null then no status messages were received. */ public String[] getStatusMessages() { return statusString; } /** * Retrieve the failure info returned by the TSA. * * @return the failure info, or null if no failure code was received. */ public boolean[] getFailureInfo() { return failureInfo; } public String getStatusCodeAsText() { switch (status) { case GRANTED: return "the timestamp request was granted."; case GRANTED_WITH_MODS: return "the timestamp request was granted with some modifications."; case REJECTION: return "the timestamp request was rejected."; case WAITING: return "the timestamp request has not yet been processed."; case REVOCATION_WARNING: return "warning: a certificate revocation is imminent."; case REVOCATION_NOTIFICATION: return "notification: a certificate revocation has occurred."; default: return ("unknown status code " + status + "."); } } private boolean isSet(int position) { return failureInfo[position]; } public String getFailureCodeAsText() { if (failureInfo == null) { return ""; } try { if (isSet(BAD_ALG)) return "Unrecognized or unsupported algorithm identifier."; if (isSet(BAD_REQUEST)) return "The requested transaction is not permitted or " + "supported."; if (isSet(BAD_DATA_FORMAT)) return "The data submitted has the wrong format."; if (isSet(TIME_NOT_AVAILABLE)) return "The TSA's time source is not available."; if (isSet(UNACCEPTED_POLICY)) return "The requested TSA policy is not supported by the TSA."; if (isSet(UNACCEPTED_EXTENSION)) return "The requested extension is not supported by the TSA."; if (isSet(ADD_INFO_NOT_AVAILABLE)) return "The additional information requested could not be " + "understood or is not available."; if (isSet(SYSTEM_FAILURE)) return "The request cannot be handled due to system failure."; } catch (ArrayIndexOutOfBoundsException ex) {} return ("unknown failure code"); } /** * Retrieve the timestamp token returned by the TSA. * * @return If null then no token was received. */ public PKCS7 getToken() { return tsToken; } public TimestampToken getTimestampToken() { return tstInfo; } /** * Retrieve the ASN.1 BER encoded timestamp token returned by the TSA. * * @return If null then no token was received. */ public byte[] getEncodedToken() { return encodedTsToken; } /* * Parses the timestamp response. * * @param status A buffer containing the ASN.1 BER encoded response. * @throws IOException The exception is thrown if a problem is encountered * parsing the timestamp response. */ private void parse(byte[] tsReply) throws IOException { // Decode TimeStampResp DerValue derValue = new DerValue(tsReply); if (derValue.tag != DerValue.tag_Sequence) { throw new IOException("Bad encoding for timestamp response"); } // Parse status DerValue statusInfo = derValue.data.getDerValue(); this.status = statusInfo.data.getInteger(); if (debug != null) { debug.println("timestamp response: status=" + this.status); } // Parse statusString, if present if (statusInfo.data.available() > 0) { byte tag = (byte)statusInfo.data.peekByte(); if (tag == DerValue.tag_SequenceOf) { DerValue[] strings = statusInfo.data.getSequence(1); statusString = new String[strings.length]; for (int i = 0; i < strings.length; i++) { statusString[i] = strings[i].getUTF8String(); if (debug != null) { debug.println("timestamp response: statusString=" + statusString[i]); } } } } // Parse failInfo, if present if (statusInfo.data.available() > 0) { this.failureInfo = statusInfo.data.getUnalignedBitString().toBooleanArray(); } // Parse timeStampToken, if present if (derValue.data.available() > 0) { DerValue timestampToken = derValue.data.getDerValue(); encodedTsToken = timestampToken.toByteArray(); tsToken = new PKCS7(encodedTsToken); tstInfo = new TimestampToken(tsToken.getContentInfo().getData()); } // Check the format of the timestamp response if (this.status == 0 || this.status == 1) { if (tsToken == null) { throw new TimestampException( "Bad encoding for timestamp response: " + "expected a timeStampToken element to be present"); } } else if (tsToken != null) { throw new TimestampException( "Bad encoding for timestamp response: " + "expected no timeStampToken element to be present"); } } final static class TimestampException extends IOException { private static final long serialVersionUID = -1631631794891940953L; TimestampException(String message) { super(message); } } }