| /* |
| * 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"); |
| } |
| } |
| |
| static final class TimestampException extends IOException { |
| private static final long serialVersionUID = -1631631794891940953L; |
| |
| TimestampException(String message) { |
| super(message); |
| } |
| } |
| } |