All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xipki.security.CtLog Maven / Gradle / Ivy

/*
 *
 * Copyright (c) 2013 - 2019 Lijun Liao
 *
 * 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 org.xipki.security;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.util.Pack;
import org.xipki.util.Args;

/**
 * RFC 6962 implementation of the required classes for the extension SCT in certificate.
 *
 * @author Lijun Liao
 */
public class CtLog {

  /**
   * struct {
   *     SignatureAndHashAlgorithm algorithm;
   *     opaque signature<0..2^16-1>;
   * } DigitallySigned;
   *
   */
  public static class DigitallySigned {

    private final SignatureAndHashAlgorithm algorithm;

    private final byte[] signature;

    public static DigitallySigned getInstance(byte[] encoded, AtomicInteger offsetObj) {
      int offset = offsetObj.get();

      // CHECKSTYLE:SKIP
      SignatureAndHashAlgorithm algorithm =
          SignatureAndHashAlgorithm.getInstance(copyOf(encoded, offset, 2));
      offset += 2;

      int signatureLen = readInt(encoded, offset, 2);
      offset += 2;
      byte[] signature = copyOf(encoded, offset, signatureLen);
      offset += signatureLen;

      offsetObj.set(offset);

      return new DigitallySigned(algorithm, signature);
    }

    public DigitallySigned(SignatureAndHashAlgorithm algorithm, byte[] signature) {
      this.algorithm = Args.notNull(algorithm, "algorithm");
      this.signature = Args.notNull(signature, "signature");
    }

    public SignatureAndHashAlgorithm getAlgorithm() {
      return algorithm;
    }

    public byte[] getSignature() {
      return Arrays.copyOf(signature, signature.length);
    }

    public Object getSignatureObject() {
      switch (algorithm.signature) {
        case ecdsa:
        case dsa:
          ASN1Sequence seq = ASN1Sequence.getInstance(signature);
          return new BigInteger[] {
              ASN1Integer.getInstance(seq.getObjectAt(0)).getPositiveValue(),
              ASN1Integer.getInstance(seq.getObjectAt(1)).getPositiveValue()};
        case rsa:
          return signature;
        case anonymous:
          return signature;
        default:
          return signature;
      }
    }

    public byte[] getEncoded() {
      byte[] ea = algorithm.getEncoded();
      final int n = ea.length + 2 + signature.length;
      byte[] res = new byte[n];
      System.arraycopy(ea, 0, res, 0, ea.length);
      int offset = ea.length;

      // length of signature
      final int sigLen = signature.length;
      offset += writeInt(sigLen, res, offset, 2);
      System.arraycopy(signature, 0, res, offset, sigLen);
      return res;
    }

  }

  /**
   * opaque SerializedSCT<1..2^16-1>;
   *
   */
  // CHECKSTYLE:SKIP
  public static class SerializedSCT {

    private final List scts;

    public static SerializedSCT getInstance(byte[] encoded) {
      int length = readInt(encoded, 0, 2);
      if (2 + length != encoded.length) {
        throw new IllegalArgumentException("length unmatch");
      }

      List scts = new LinkedList<>();

      AtomicInteger offsetObj = new AtomicInteger(2);
      while (offsetObj.get() < encoded.length) {
        int sctLen = readInt(encoded, offsetObj.getAndAdd(2), 2);
        SignedCertificateTimestamp sct = SignedCertificateTimestamp.getInstance(
            encoded, offsetObj, sctLen);
        scts.add(sct);
      }

      return new SerializedSCT(scts);
    }

    public SerializedSCT(List scts) {
      this.scts = scts == null ? new LinkedList<>() : new LinkedList<>(scts);
    }

    public int size() {
      return scts.size();
    }

    public SignedCertificateTimestamp get(int index) {
      return scts.get(index);
    }

    public SignedCertificateTimestamp remove(int index) {
      return scts.remove(index);
    }

    public void add(SignedCertificateTimestamp sct) {
      scts.add(sct);
    }

    public byte[] getEncoded() {
      if (scts == null || scts.isEmpty()) {
        return new byte[] {0, 0};
      }

      List encodedScts = new ArrayList<>(scts.size());
      int totalLen = 0;
      for (SignedCertificateTimestamp sct : scts) {
        byte[] encodedSct = sct.getEncoded();
        // the serialized SCT will be included. Although the maximal length is
        // not defined in RFC 6962, the log servers use 2 bytes to represent
        // the length.
        byte[] encodedSctWithLen = new byte[2 + encodedSct.length];
        writeInt(encodedSct.length, encodedSctWithLen, 0, 2);
        System.arraycopy(encodedSct, 0, encodedSctWithLen, 2, encodedSct.length);
        totalLen += encodedSctWithLen.length;
        encodedScts.add(encodedSctWithLen);
      }

      byte[] res = new byte[2 + totalLen];
      int offset = writeInt(totalLen, res, 0, 2);
      for (byte[] m : encodedScts) {
        System.arraycopy(m, 0, res, offset, m.length);
        offset += m.length;
      }

      return res;
    }

  }

  public static enum HashAlgorithm {
    none((byte) 0),
    md5((byte) 1),
    sha1((byte) 2),
    sha224((byte) 3),
    sha256((byte) 4),
    sha384((byte) 5),
    sha512((byte) 6);

    private byte code;

    private HashAlgorithm(byte code) {
      this.code = code;
    }

    public byte getCode() {
      return code;
    }

    public static HashAlgorithm ofCode(byte code) {
      for (HashAlgorithm m : values()) {
        if (m.code == code) {
          return m;
        }
      }
      return null;
    }
  }

  public static enum SignatureAlgorithm {
    anonymous((byte) 0),
    rsa((byte) 1),
    dsa((byte) 2),
    ecdsa((byte) 3);

    private byte code;

    private SignatureAlgorithm(byte code) {
      this.code = code;
    }

    public byte getCode() {
      return code;
    }

    public static SignatureAlgorithm ofCode(byte code) {
      for (SignatureAlgorithm m : values()) {
        if (m.code == code) {
          return m;
        }
      }
      return null;
    }
  }

  /**
   * ASN.1 definition:
   * 

* struct { * HashAlgorithm hash; * SignatureAlgorithm signature; * } SignatureAndHashAlgorithm; * */ public static class SignatureAndHashAlgorithm { private final HashAlgorithm hash; private final SignatureAlgorithm signature; public static SignatureAndHashAlgorithm getInstance(byte[] encoded) { return new SignatureAndHashAlgorithm( HashAlgorithm.ofCode(encoded[0]), SignatureAlgorithm.ofCode(encoded[1])); } public SignatureAndHashAlgorithm(HashAlgorithm hash, SignatureAlgorithm signature) { this.hash = Args.notNull(hash, "hash"); this.signature = Args.notNull(signature, "signature"); } public HashAlgorithm getHash() { return hash; } public SignatureAlgorithm getSignature() { return signature; } public byte[] getEncoded() { return new byte[] {hash.getCode(), signature.getCode()}; } } /** * ASN1. definition: *

* struct { * Version sct_version; * LogID id; * uint64 timestamp; * CtExtensions extensions; * DigitallySigned signature * } SignedCertificateTimestamp * */ public static class SignedCertificateTimestamp { /** * ASN.1 definition: *

* enum { v1(0), (255) } * Version; */ private final byte version; /** * ASN.1 definition: *

* struct { * opaque key_id[32]; * } LogID; */ private final byte[] logId; /** * ASN.1 definition: *

* uint64 timestamp; */ private final long timestamp; /** * ASN.1 definition: *

* opaque CtExtensions<0..2^16-1>; * CtExtensions extensions. *

* Does not contain the encoded length. */ private final byte[] extensions; private final DigitallySigned digitallySigned; public static SignedCertificateTimestamp getInstance(byte[] encoded, AtomicInteger offsetObj, int len) { int startOffset = offsetObj.get(); int offset = startOffset; // CHECKSTYLE:SKIP byte version = encoded[offset++]; // CHECKSTYLE:SKIP byte[] logID = copyOf(encoded, offset, 32); offset += 32; // CHECKSTYLE:SKIP long timestamp = Pack.bigEndianToLong(encoded, offset); offset += 8; int extensionsLen = readInt(encoded, offset, 2); offset += 2; byte[] extensions; if (extensionsLen == 0) { extensions = new byte[0]; } else { extensions = copyOf(encoded, offset, extensionsLen); offset += extensionsLen; } offsetObj.set(offset); DigitallySigned digitallySigned = DigitallySigned.getInstance(encoded, offsetObj); if (offsetObj.get() != startOffset + len) { throw new IllegalArgumentException("length unmatch"); } return new SignedCertificateTimestamp(version, logID, timestamp, extensions, digitallySigned); } public SignedCertificateTimestamp(byte version, byte[] logId, long timestamp, byte[] extensions, DigitallySigned digitallySigned) { this.version = version; Args.notNull(logId, "logId"); Args.equals(logId.length, "logID.length", 32); this.logId = logId; this.timestamp = timestamp; this.extensions = extensions == null ? new byte[0] : extensions; this.digitallySigned = Args.notNull(digitallySigned, "digitallySigned"); } public int getVersion() { return version; } public byte[] getLogId() { return Arrays.copyOf(logId, logId.length); } public long getTimestamp() { return timestamp; } public byte[] getExtensions() { return extensions.length == 0 ? extensions : Arrays.copyOf(extensions, extensions.length); } public DigitallySigned getDigitallySigned() { return digitallySigned; } public byte[] getEncoded() { byte[] encodedDs = digitallySigned.getEncoded(); int totoalLen = 41 //37 = 1 + 32 + 8 + 2 // length of extensions + extensions.length + encodedDs.length; byte[] res = new byte[totoalLen]; int offset = 0; // version: 1 bytes res[offset++] = version; // logID: 32 bytes System.arraycopy(logId, 0, res, 1, logId.length); offset += logId.length; // timestamp: 8 bytes byte[] tsBytes = Pack.longToBigEndian(timestamp); System.arraycopy(tsBytes, 0, res, offset, 8); offset += 8; // extensions offset += writeInt(extensions.length, res, offset, 2); if (extensions.length > 0) { System.arraycopy(extensions, 0, res, offset, extensions.length); offset += extensions.length; } System.arraycopy(encodedDs, 0, res, offset, encodedDs.length); return res; } } /** * struct { * SerializedSCT sct_list <1..2^16-1>; * } SignedCertificateTimestampList; * */ public static class SignedCertificateTimestampList { private final SerializedSCT sctList; public static SignedCertificateTimestampList getInstance(byte[] encoded) { SerializedSCT sctList = SerializedSCT.getInstance(encoded); return new SignedCertificateTimestampList(sctList); } public SignedCertificateTimestampList(SerializedSCT sctList) { this.sctList = Args.notNull(sctList, "sctList"); } public SerializedSCT getSctList() { return sctList; } public byte[] getEncoded() { return sctList.getEncoded(); } } /** * Write integer value to the buffer. * @param value integer value to be written. * @param buffer buffer * @param offset offset of the buffer * @param bytesLenOfValue number of bytes to represent the length, between 1 and 4. * @return bytesLenOfLength */ private static int writeInt(int value, byte[] buffer, int offset, int bytesLenOfValue) { if (bytesLenOfValue == 4) { buffer[offset] = (byte) (value >>> 24); buffer[offset + 1] = (byte) (value >>> 16); buffer[offset + 2] = (byte) (value >>> 8); buffer[offset + 3] = (byte) value; } else if (bytesLenOfValue == 3) { buffer[offset] = (byte) (value >>> 16); buffer[offset + 1] = (byte) (value >>> 8); buffer[offset + 2] = (byte) value; } else if (bytesLenOfValue == 2) { buffer[offset] = (byte) (value >>> 8); buffer[offset + 1] = (byte) value; } else { buffer[offset] = (byte) value; } return bytesLenOfValue; } /** * Read integer value from the buffer. * @param buffer buffer * @param offset offset of the buffer * @param bytesLenOfValue number of bytes to represent the length, between 1 and 4. * @return bytesLenOfLength */ private static int readInt(byte[] buffer, int offset, int bytesLenOfValue) { if (bytesLenOfValue == 4) { return (0xFF & buffer[offset]) << 24 | (0xFF & buffer[offset + 1]) << 16 | (0xFF & buffer[offset + 2]) << 8 | (0xFF & buffer[offset + 3]); } else if (bytesLenOfValue == 3) { return (0xFF & buffer[offset]) << 16 | (0xFF & buffer[offset + 1]) << 8 | (0xFF & buffer[offset + 2]); } else if (bytesLenOfValue == 2) { return (0xFF & buffer[offset]) << 8 | (0xFF & buffer[offset + 1]); } else { return 0xFF & buffer[offset]; } } private static byte[] copyOf(byte[] original, int from, int len) { return Arrays.copyOfRange(original, from, from + len); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy