com.guardtime.asn1.TstInfo.java Source code

Java tutorial

Introduction

Here is the source code for com.guardtime.asn1.TstInfo.java

Source

/*
 * $Id: TstInfo.java 268 2012-08-27 18:31:08Z ahto.truu $
 *
 *
 *
 * Copyright 2008-2011 GuardTime AS
 *
 * This file is part of the GuardTime client SDK.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.guardtime.asn1;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.util.Date;

import org.bouncycastle.asn1.ASN1Boolean;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.tsp.TSTInfo;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.asn1.x509.GeneralName;

import com.guardtime.util.Util;

/**
 * <a target="_blank" href="http://www.ietf.org/rfc/rfc3161.txt">RFC 3161</a>
 * structure {@code TSTInfo} ({@code contentInfo.content.encapContentInfo.eContent}).
 *
 * <pre>
 * TSTInfo ::= SEQUENCE {
 *    version        INTEGER  { v1(1) },
 *    policy         TSAPolicyId,
 *    messageImprint MessageImprint,
 *    -- MUST have the same value as the similar field in TimeStampReq
 *    serialNumber   INTEGER,
 *    -- Users MUST be ready to accommodate integers up to 160 bits
 *    genTime        GeneralizedTime,
 *    accuracy       Accuracy OPTIONAL,
 *    ordering       BOOLEAN DEFAULT FALSE,
 *    nonce          INTEGER OPTIONAL,
 *    -- MUST be present if the similar field was present in TimeStampReq.
 *    -- In that case it MUST have the same value.
 *    tsa            [0] GeneralName OPTIONAL,
 *    extensions     [1] IMPLICIT Extensions OPTIONAL
 * }
 * </pre>
 *
 * @see MessageImprint
 * @see Accuracy
 *
 * @since 0.4
 */
public final class TstInfo extends Asn1Wrapper {
    public static final int VERSION = 1;

    private TSTInfo tstInfo;
    private int version;
    private String policy;
    private MessageImprint messageImprint;
    private BigInteger serialNumber;
    private Date genTime;
    private Accuracy accuracy;
    private boolean ordering;
    private BigInteger nonce;
    private String tsa;
    private byte[] extensions;

    /**
     * Parses a DER-encoded {@code TSTInfo} out from the given input stream.
     *
     * @param in
     *            the input stream to read data from.
     * @return the {@code TSTInfo} object.
     * @throws Asn1FormatException
     *             if the data read from {@code in} does not represent a valid
     *             {@code TSTInfo} object.
     * @throws IOException
     *             if {@code in} throws one.
     */
    public static TstInfo getInstance(InputStream in) throws Asn1FormatException, IOException {
        if (in == null) {
            throw new IllegalArgumentException("invalid input stream: null");
        }

        try {
            ASN1Object obj = new ASN1InputStream(in).readObject();
            return new TstInfo(obj);
        } catch (IOException e) {
            if (isAsnParserException(e)) {
                throw new Asn1FormatException("TST info has invalid format", e);
            } else {
                throw e;
            }
        } catch (IllegalArgumentException e) {
            if (isAsnParserException(e)) {
                throw new Asn1FormatException("TST info has invalid format", e);
            } else {
                throw e;
            }
        }
    }

    /**
     * Returns the DER representation of the {@code ContentInfo}.
     *
     * @return a DER byte array, or {@code null} on error.
     */
    public byte[] getDerEncoded() {
        try {
            return tstInfo.getEncoded(ASN1Encoding.DER);
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * Returns the version number of the syntax of the {@code TSTInfo} object.
     * GuardTime timestamps always use {@link #VERSION}.
     *
     * @return the value of the {@code version} field of this {@code TSTInfo}
     *         object.
     */
    public int getVersion() {
        return version;
    }

    /**
     * Returns the identifier of the policy under which the timestamp was
     * issued.
     *
     * @return the OID of the timestamping policy.
     */
    public String getPolicy() {
        return policy;
    }

    /**
     * Returns the imprint of the timestamped datum.
     *
     * @return the message imprint.
     */
    public MessageImprint getMessageImprint() {
        return messageImprint;
    }

    /**
     * Returns the serial number of the timestamp.
     *
     * @return the serial number.
     */
    public BigInteger getSerialNumber() {
        return serialNumber;
    }

    /**
     * Returns the time when the timestamping request was received by the
     * GuardTime gateway that issued the timestamp, according to the gateway's
     * local clock.
     * <p>
     * Note that this is not the same as the time value extracted from the
     * history hash chain of the {@link TimeSignature}.
     *
     * @return the request time.
     */
    public Date getGenTime() {
        return (Date) genTime.clone();
    }

    /**
     * Returns the claimed accuracy of the issuing gateway's local clock.
     *
     * @return the accuracy of the request time.
     */
    public Accuracy getAccuracy() {
        return accuracy;
    }

    /**
     * Returns {@code true} if any two timestamps can be ordered in time by
     * comparing the values returned by {@link #getGenTime()}.
     * <p>
     * This is not the case for GuardTime timestamps, so this method always
     * returns {@code false}
     *
     * @return {@code false}.
     */
    public boolean getOrdering() {
        return ordering;
    }

    /**
     * Returns the nonce from the timestamp, if there is one.
     *
     * @return the nonce, or {@code null}.
     */
    public BigInteger getNonce() {
        return nonce;
    }

    /**
     * Returns the name of the gateway that issued the timestamp.
     *
     * @return the hostname of the gateway.
     */
    public String getTsa() {
        return tsa;
    }

    /**
     * Returns the DER representation of {@code TSTInfo} extensions.
     * <p>
     * No extensions are used by the current version of the GuardTime service.
     *
     * @return DER-encoded extensions.
     */
    public byte[] getEncodedExtensions() {
        return Util.copyOf(extensions);
    }

    /**
     * Returns the same value as {@link #getAccuracy()}, but formatted for
     * human reading.
     *
     * @return the accuracy of the request time.
     */
    public String getFormattedAccuracy() {
        if (accuracy == null) {
            return null;
        }

        // RFC 3161: if either seconds, millis or micros is missing, then
        // a value of zero MUST be taken for the missing field.
        long value = 0;

        Integer seconds = accuracy.getSeconds();
        if (seconds != null) {
            value += seconds.intValue();
        }
        value *= 1000;

        Integer millis = accuracy.getMillis();
        if (millis != null) {
            value += millis.intValue();
        }
        value *= 1000;

        Integer micros = accuracy.getMicros();
        if (micros != null) {
            value += micros.intValue();
        }

        if (value % 1000000 == 0) {
            return (value / 1000000) + "s";
        } else if (value % 1000 == 0) {
            return (value / 1000) + "ms";
        } else {
            return value + "us";
        }
    }

    /**
     * Returns the same value as {@link #getTsa()}, but formatted for human
     * reading.
     *
     * @return the hostname of the gateway.
     */
    public String getFormattedTsa() {
        if (tsa == null) {
            return null;
        } else if (tsa.startsWith("0: ")) {
            return tsa.replaceFirst("0: ", "Other:");
        } else if (tsa.startsWith("1: ")) {
            return tsa.replaceFirst("1: ", "RFC822:");
        } else if (tsa.startsWith("2: ")) {
            return tsa.replaceFirst("2: ", "DNS:");
        } else if (tsa.startsWith("3: ")) {
            return tsa.replaceFirst("3: ", "X400:");
        } else if (tsa.startsWith("4: ")) {
            return tsa.replaceFirst("4: ", "DN:");
        } else if (tsa.startsWith("5:")) {
            return tsa.replaceFirst("5: ", "EDIParty:");
        } else if (tsa.startsWith("6: ")) {
            return tsa.replaceFirst("6: ", "URI:");
        } else if (tsa.startsWith("7: ")) {
            return tsa.replaceFirst("7: ", "IP:");
        } else if (tsa.startsWith("8:")) {
            return tsa.replaceFirst("8: ", "OID:");
        }

        return tsa;
    }

    /**
     * Class constructor.
     *
     * @param obj ASN.1 representation of TST info.
     *
     * @throws Asn1FormatException if provided ASN.1 object has invalid format.
     */
    TstInfo(ASN1Encodable obj) throws Asn1FormatException {
        try {
            tstInfo = TSTInfo.getInstance(obj);

            // Extract and check version
            BigInteger ver = tstInfo.getVersion().getValue();
            if (!ver.equals(BigInteger.valueOf(VERSION))) {
                throw new Asn1FormatException("invalid TST info version: " + ver);
            }
            version = ver.intValue();

            // Extract policy
            policy = tstInfo.getPolicy().getId();

            // Extract message imprint
            messageImprint = new MessageImprint(tstInfo.getMessageImprint().toASN1Primitive());

            // Extract serial number
            //
            // As `DERInteger` can be constructed out of `ASN1OctetString`
            // without any error, here we have no option to determine
            // if the serial number is actually an INTEGER or OCTET STRING.
            //
            // Possible solutions is to rewrite BouncyCastle `TSTInfo` class
            // adding more strict checks.
            serialNumber = tstInfo.getSerialNumber().getValue();

            // Extract request time
            //
            // Current BouncyCastle implementation can parse the time string
            // that does not omit trailing zeros in second fraction part.
            // RFC 3161 requires that such time string is labeled invalid.
            genTime = tstInfo.getGenTime().getDate();

            // Extract optional fields

            ASN1Encodable acc = tstInfo.getAccuracy();
            accuracy = ((acc == null) ? null : new Accuracy(acc.toASN1Primitive()));

            ASN1Boolean ord = tstInfo.getOrdering();
            ordering = (ord != null && ord.isTrue());

            ASN1Integer nnc = tstInfo.getNonce();
            nonce = ((nnc == null) ? null : nnc.getValue());

            GeneralName tsaName = tstInfo.getTsa();
            tsa = ((tsaName == null) ? null : tsaName.toString());

            Extensions exts = tstInfo.getExtensions();
            if (exts != null) {
                // check for critical extensions
                Asn1Util.checkExtensions(exts);
                extensions = exts.getEncoded(ASN1Encoding.DER);
            }
        } catch (Asn1FormatException e) {
            throw e;
        } catch (Exception e) {
            throw new Asn1FormatException("TST info has invalid format", e);
        }
    }
}