| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
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)  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public static final int GRANTED = 0;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public static final int GRANTED_WITH_MODS = 1;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public static final int REJECTION = 2;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public static final int WAITING = 3;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public static final int REVOCATION_WARNING = 4;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public static final int REVOCATION_NOTIFICATION = 5;  | 
 | 
 | 
 | 
    // Failure codes (from RFC 3161)  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public static final int BAD_ALG = 0;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public static final int BAD_REQUEST = 2;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public static final int BAD_DATA_FORMAT = 5;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public static final int TIME_NOT_AVAILABLE = 14;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public static final int UNACCEPTED_POLICY = 15;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public static final int UNACCEPTED_EXTENSION = 16;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public static final int ADD_INFO_NOT_AVAILABLE = 17;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    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;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    TSResponse(byte[] tsReply) throws IOException { | 
 | 
        parse(tsReply);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public int getStatusCode() { | 
 | 
        return status;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public String[] getStatusMessages() { | 
 | 
        return statusString;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    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"); | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public PKCS7 getToken() { | 
 | 
        return tsToken;  | 
 | 
    }  | 
 | 
 | 
 | 
    public TimestampToken getTimestampToken() { | 
 | 
        return tstInfo;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public byte[] getEncodedToken() { | 
 | 
        return encodedTsToken;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    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); | 
 | 
        }  | 
 | 
          | 
 | 
        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]);  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
          | 
 | 
        if (statusInfo.data.available() > 0) { | 
 | 
            this.failureInfo  | 
 | 
                = statusInfo.data.getUnalignedBitString().toBooleanArray();  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        if (derValue.data.available() > 0) { | 
 | 
            DerValue timestampToken = derValue.data.getDerValue();  | 
 | 
            encodedTsToken = timestampToken.toByteArray();  | 
 | 
            tsToken = new PKCS7(encodedTsToken);  | 
 | 
            tstInfo = new TimestampToken(tsToken.getContentInfo().getData());  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        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);  | 
 | 
        }  | 
 | 
    }  | 
 | 
}  |