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