1 /* 2 * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.timestamp; 27 28 import java.io.IOException; 29 import sun.security.pkcs.PKCS7; 30 import sun.security.util.Debug; 31 import sun.security.util.DerValue; 32 33 /** 34 * This class provides the response corresponding to a timestamp request, 35 * as defined in 36 * <a href="http://www.ietf.org/rfc/rfc3161.txt">RFC 3161</a>. 37 * 38 * The TimeStampResp ASN.1 type has the following definition: 39 * <pre> 40 * 41 * TimeStampResp ::= SEQUENCE { 42 * status PKIStatusInfo, 43 * timeStampToken TimeStampToken OPTIONAL ] 44 * 45 * PKIStatusInfo ::= SEQUENCE { 46 * status PKIStatus, 47 * statusString PKIFreeText OPTIONAL, 48 * failInfo PKIFailureInfo OPTIONAL } 49 * 50 * PKIStatus ::= INTEGER { 51 * granted (0), 52 * -- when the PKIStatus contains the value zero a TimeStampToken, as 53 * -- requested, is present. 54 * grantedWithMods (1), 55 * -- when the PKIStatus contains the value one a TimeStampToken, 56 * -- with modifications, is present. 57 * rejection (2), 58 * waiting (3), 59 * revocationWarning (4), 60 * -- this message contains a warning that a revocation is 61 * -- imminent 62 * revocationNotification (5) 63 * -- notification that a revocation has occurred } 64 * 65 * PKIFreeText ::= SEQUENCE SIZE (1..MAX) OF UTF8String 66 * -- text encoded as UTF-8 String (note: each UTF8String SHOULD 67 * -- include an RFC 1766 language tag to indicate the language 68 * -- of the contained text) 69 * 70 * PKIFailureInfo ::= BIT STRING { 71 * badAlg (0), 72 * -- unrecognized or unsupported Algorithm Identifier 73 * badRequest (2), 74 * -- transaction not permitted or supported 75 * badDataFormat (5), 76 * -- the data submitted has the wrong format 77 * timeNotAvailable (14), 78 * -- the TSA's time source is not available 79 * unacceptedPolicy (15), 80 * -- the requested TSA policy is not supported by the TSA 81 * unacceptedExtension (16), 82 * -- the requested extension is not supported by the TSA 83 * addInfoNotAvailable (17) 84 * -- the additional information requested could not be understood 85 * -- or is not available 86 * systemFailure (25) 87 * -- the request cannot be handled due to system failure } 88 * 89 * TimeStampToken ::= ContentInfo 90 * -- contentType is id-signedData 91 * -- content is SignedData 92 * -- eContentType within SignedData is id-ct-TSTInfo 93 * -- eContent within SignedData is TSTInfo 94 * 95 * </pre> 96 * 97 * @since 1.5 98 * @author Vincent Ryan 99 * @see Timestamper 100 */ 101 102 public class TSResponse { 103 104 // Status codes (from RFC 3161) 105 106 /** 107 * The requested timestamp was granted. 108 */ 109 public static final int GRANTED = 0; 110 111 /** 112 * The requested timestamp was granted with some modifications. 113 */ 114 public static final int GRANTED_WITH_MODS = 1; 115 116 /** 117 * The requested timestamp was not granted. 118 */ 119 public static final int REJECTION = 2; 120 121 /** 122 * The requested timestamp has not yet been processed. 123 */ 124 public static final int WAITING = 3; 125 126 /** 127 * A warning that a certificate revocation is imminent. 128 */ 129 public static final int REVOCATION_WARNING = 4; 130 131 /** 132 * Notification that a certificate revocation has occurred. 133 */ 134 public static final int REVOCATION_NOTIFICATION = 5; 135 136 // Failure codes (from RFC 3161) 137 138 /** 139 * Unrecognized or unsupported algorithm identifier. 140 */ 141 public static final int BAD_ALG = 0; 142 143 /** 144 * The requested transaction is not permitted or supported. 145 */ 146 public static final int BAD_REQUEST = 2; 147 148 /** 149 * The data submitted has the wrong format. 150 */ 151 public static final int BAD_DATA_FORMAT = 5; 152 153 /** 154 * The TSA's time source is not available. 155 */ 156 public static final int TIME_NOT_AVAILABLE = 14; 157 158 /** 159 * The requested TSA policy is not supported by the TSA. 160 */ 161 public static final int UNACCEPTED_POLICY = 15; 162 163 /** 164 * The requested extension is not supported by the TSA. 165 */ 166 public static final int UNACCEPTED_EXTENSION = 16; 167 168 /** 169 * The additional information requested could not be understood or is not 170 * available. 171 */ 172 public static final int ADD_INFO_NOT_AVAILABLE = 17; 173 174 /** 175 * The request cannot be handled due to system failure. 176 */ 177 public static final int SYSTEM_FAILURE = 25; 178 179 private static final Debug debug = Debug.getInstance("ts"); 180 181 private int status; 182 183 private String[] statusString = null; 184 185 private boolean[] failureInfo = null; 186 187 private byte[] encodedTsToken = null; 188 189 private PKCS7 tsToken = null; 190 191 private TimestampToken tstInfo; 192 193 /** 194 * Constructs an object to store the response to a timestamp request. 195 * 196 * @param status A buffer containing the ASN.1 BER encoded response. 197 * @throws IOException The exception is thrown if a problem is encountered 198 * parsing the timestamp response. 199 */ 200 TSResponse(byte[] tsReply) throws IOException { 201 parse(tsReply); 202 } 203 204 /** 205 * Retrieve the status code returned by the TSA. 206 */ 207 public int getStatusCode() { 208 return status; 209 } 210 211 /** 212 * Retrieve the status messages returned by the TSA. 213 * 214 * @return If null then no status messages were received. 215 */ 216 public String[] getStatusMessages() { 217 return statusString; 218 } 219 220 /** 221 * Retrieve the failure info returned by the TSA. 222 * 223 * @return the failure info, or null if no failure code was received. 224 */ 225 public boolean[] getFailureInfo() { 226 return failureInfo; 227 } 228 229 public String getStatusCodeAsText() { 230 231 switch (status) { 232 case GRANTED: 233 return "the timestamp request was granted."; 234 235 case GRANTED_WITH_MODS: 236 return 237 "the timestamp request was granted with some modifications."; 238 239 case REJECTION: 240 return "the timestamp request was rejected."; 241 242 case WAITING: 243 return "the timestamp request has not yet been processed."; 244 245 case REVOCATION_WARNING: 246 return "warning: a certificate revocation is imminent."; 247 248 case REVOCATION_NOTIFICATION: 249 return "notification: a certificate revocation has occurred."; 250 251 default: 252 return ("unknown status code " + status + "."); 253 } 254 } 255 256 private boolean isSet(int position) { 257 return failureInfo[position]; 258 } 259 260 public String getFailureCodeAsText() { 261 262 if (failureInfo == null) { 263 return ""; 264 } 265 266 try { 267 if (isSet(BAD_ALG)) 268 return "Unrecognized or unsupported algorithm identifier."; 269 if (isSet(BAD_REQUEST)) 270 return "The requested transaction is not permitted or " + 271 "supported."; 272 if (isSet(BAD_DATA_FORMAT)) 273 return "The data submitted has the wrong format."; 274 if (isSet(TIME_NOT_AVAILABLE)) 275 return "The TSA's time source is not available."; 276 if (isSet(UNACCEPTED_POLICY)) 277 return "The requested TSA policy is not supported by the TSA."; 278 if (isSet(UNACCEPTED_EXTENSION)) 279 return "The requested extension is not supported by the TSA."; 280 if (isSet(ADD_INFO_NOT_AVAILABLE)) 281 return "The additional information requested could not be " + 282 "understood or is not available."; 283 if (isSet(SYSTEM_FAILURE)) 284 return "The request cannot be handled due to system failure."; 285 } catch (ArrayIndexOutOfBoundsException ex) {} 286 287 return ("unknown failure code"); 288 } 289 290 /** 291 * Retrieve the timestamp token returned by the TSA. 292 * 293 * @return If null then no token was received. 294 */ 295 public PKCS7 getToken() { 296 return tsToken; 297 } 298 299 public TimestampToken getTimestampToken() { 300 return tstInfo; 301 } 302 303 /** 304 * Retrieve the ASN.1 BER encoded timestamp token returned by the TSA. 305 * 306 * @return If null then no token was received. 307 */ 308 public byte[] getEncodedToken() { 309 return encodedTsToken; 310 } 311 312 /* 313 * Parses the timestamp response. 314 * 315 * @param status A buffer containing the ASN.1 BER encoded response. 316 * @throws IOException The exception is thrown if a problem is encountered 317 * parsing the timestamp response. 318 */ 319 private void parse(byte[] tsReply) throws IOException { 320 // Decode TimeStampResp 321 322 DerValue derValue = new DerValue(tsReply); 323 if (derValue.tag != DerValue.tag_Sequence) { 324 throw new IOException("Bad encoding for timestamp response"); 325 } 326 327 // Parse status 328 329 DerValue statusInfo = derValue.data.getDerValue(); 330 this.status = statusInfo.data.getInteger(); 331 if (debug != null) { 332 debug.println("timestamp response: status=" + this.status); 333 } 334 // Parse statusString, if present 335 if (statusInfo.data.available() > 0) { 336 byte tag = (byte)statusInfo.data.peekByte(); 337 if (tag == DerValue.tag_SequenceOf) { 338 DerValue[] strings = statusInfo.data.getSequence(1); 339 statusString = new String[strings.length]; 340 for (int i = 0; i < strings.length; i++) { 341 statusString[i] = strings[i].getUTF8String(); 342 if (debug != null) { 343 debug.println("timestamp response: statusString=" + 344 statusString[i]); 345 } 346 } 347 } 348 } 349 // Parse failInfo, if present 350 if (statusInfo.data.available() > 0) { 351 this.failureInfo 352 = statusInfo.data.getUnalignedBitString().toBooleanArray(); 353 } 354 355 // Parse timeStampToken, if present 356 if (derValue.data.available() > 0) { 357 DerValue timestampToken = derValue.data.getDerValue(); 358 encodedTsToken = timestampToken.toByteArray(); 359 tsToken = new PKCS7(encodedTsToken); 360 tstInfo = new TimestampToken(tsToken.getContentInfo().getData()); 361 } 362 363 // Check the format of the timestamp response 364 if (this.status == 0 || this.status == 1) { 365 if (tsToken == null) { 366 throw new TimestampException( 367 "Bad encoding for timestamp response: " + 368 "expected a timeStampToken element to be present"); 369 } 370 } else if (tsToken != null) { 371 throw new TimestampException( 372 "Bad encoding for timestamp response: " + 373 "expected no timeStampToken element to be present"); 374 } 375 } 376 377 final static class TimestampException extends IOException { 378 private static final long serialVersionUID = -1631631794891940953L; 379 380 TimestampException(String message) { 381 super(message); 382 } 383 } 384 }