Package ch.ethz.inf.vs.scandium.dtls

Source Code of ch.ethz.inf.vs.scandium.dtls.CertificateRequest

/*******************************************************************************
* Copyright (c) 2014, Institute for Pervasive Computing, ETH Zurich.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
*    may be used to endorse or promote products derived from this software
*    without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Scandium (Sc) Security for Californium.
******************************************************************************/
package ch.ethz.inf.vs.scandium.dtls;

import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;

import javax.security.auth.x500.X500Principal;

import ch.ethz.inf.vs.scandium.util.DatagramReader;
import ch.ethz.inf.vs.scandium.util.DatagramWriter;

/**
* A non-anonymous server can optionally request a certificate from the client,
* if appropriate for the selected cipher suite. This message, if sent, will
* immediately follow the {@link ServerKeyExchange} message (if it is sent;
* otherwise, this message follows the server's {@link CertificateMessage}
* message). For further details see <a
* href="http://tools.ietf.org/html/rfc5246#section-7.4.4">RFC 5246, 7.4.4.
* Certificate Request</a>.
*
* @author Stefan Jucker
*
*/
public class CertificateRequest extends HandshakeMessage {

  // DTLS-specific constants ////////////////////////////////////////

  /* See http://tools.ietf.org/html/rfc5246#section-7.4.4 for message format. */

  private static final int CERTIFICATE_TYPES_LENGTH_BITS = 8;

  private static final int CERTIFICATE_TYPE_BITS = 8;

  private static final int SUPPORTED_SIGNATURE_LENGTH_BITS = 16;

  private static final int CERTIFICATE_AUTHORITIES_LENGTH_BITS = 16;
 
  private static final int CERTIFICATE_AUTHORITY_LENGTH_BITS = 16;

  private static final int SUPPORTED_SIGNATURE_BITS = 8;

  // Members ////////////////////////////////////////////////////////

  /** A list of the types of certificate types that the client may offer. */
  private List<ClientCertificateType> certificateTypes;

  /**
   * A list of the hash/signature algorithm pairs that the server is able to
   * verify, listed in descending order of preference.
   */
  private List<SignatureAndHashAlgorithm> supportedSignatureAlgorithms;

  /**
   * A list of the distinguished names of acceptable certificate_authorities,
   * represented in DER-encoded format. The list is between 0 and
   * 2<sup>16</sup>-1 bytes long, while one distinguished name can range from
   * 1 to 2<sup>16</sup>-1 bytes length. Therefore, the length in the
   * serialization must be handled carefully.
   */
  private List<DistinguishedName> certificateAuthorities;

  // Constructors ///////////////////////////////////////////////////
 
  /**
   * Initializes an empty certificate request.
   */
  public CertificateRequest() {
    this.certificateTypes = new ArrayList<ClientCertificateType>();
    this.supportedSignatureAlgorithms = new ArrayList<SignatureAndHashAlgorithm>();
    this.certificateAuthorities = new ArrayList<DistinguishedName>();
  }

  /**
   *
   * @param certificateTypes
   *            the list of allowed client certificate types.
   * @param supportedSignatureAlgorithms
   *            the list of supported signature and hash algorithms.
   * @param certificateAuthorities
   *            the list of allowed certificate authorities.
   */
  public CertificateRequest(List<ClientCertificateType> certificateTypes, List<SignatureAndHashAlgorithm> supportedSignatureAlgorithms, List<DistinguishedName> certificateAuthorities) {
    this.certificateTypes = certificateTypes;
    this.supportedSignatureAlgorithms = supportedSignatureAlgorithms;
    this.certificateAuthorities = certificateAuthorities;
  }

  // Methods ////////////////////////////////////////////////////////

  @Override
  public HandshakeType getMessageType() {
    return HandshakeType.CERTIFICATE_REQUEST;
  }

  @Override
  public int getMessageLength() {
    // fixed: certificate type length field (1 byte) + supported signature
    // algorithms length field (2 bytes) + certificate authorities length
    // field (2 bytes) = 5 bytes
   

    return 5 + certificateTypes.size() + (supportedSignatureAlgorithms.size() * 2) + getCertificateAuthoritiesLength();
  }
 
  private int getCertificateAuthoritiesLength() {
    // each distinguished name has a variable length, therefore we need an
    // additional 2 bytes length field for each name
    int certificateAuthLength = 0;
    for (DistinguishedName distinguishedName : certificateAuthorities) {
      certificateAuthLength += distinguishedName.getName().length + 2;
    }
   
    return certificateAuthLength;
  }
 
  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder(super.toString());
    if (certificateTypes.size() > 0) {
      sb.append("\t\tClient certificate type:\n");
      for (ClientCertificateType type : certificateTypes) {
        sb.append("\t\t\t" + type.toString() + "\n");
      }
    }
    if (supportedSignatureAlgorithms.size() > 0) {
      sb.append("\t\tSignature and hash algorithm:\n");
      for (SignatureAndHashAlgorithm algo : supportedSignatureAlgorithms) {
        sb.append("\t\t\t" + algo.toString() + "\n");
      }
    }
    if (certificateAuthorities.size() > 0) {
      sb.append("\t\tCertificate authorities:\n");
      for (DistinguishedName name : certificateAuthorities) {
        X500Principal principal = new X500Principal(name.getName());
        sb.append("\t\t\t" + principal.getName() + "\n");
      }
    }
    return sb.toString();
  }

  // Serialization //////////////////////////////////////////////////

  @Override
  public byte[] fragmentToByteArray() {
    DatagramWriter writer = new DatagramWriter();

    writer.write(certificateTypes.size(), CERTIFICATE_TYPES_LENGTH_BITS);
    for (ClientCertificateType certificateType : certificateTypes) {
      writer.write(certificateType.getCode(), CERTIFICATE_TYPE_BITS);
    }

    writer.write(supportedSignatureAlgorithms.size() * 2, SUPPORTED_SIGNATURE_LENGTH_BITS);
    for (SignatureAndHashAlgorithm signatureAndHashAlgorithm : supportedSignatureAlgorithms) {
      writer.write(signatureAndHashAlgorithm.getHash().getCode(), SUPPORTED_SIGNATURE_BITS);
      writer.write(signatureAndHashAlgorithm.getSignature().getCode(), SUPPORTED_SIGNATURE_BITS);
    }
   
    writer.write(getCertificateAuthoritiesLength(), CERTIFICATE_AUTHORITIES_LENGTH_BITS);
    for (DistinguishedName distinguishedName : certificateAuthorities) {
      // since a distinguished name has variable length, we need to write length field for each name as well, has influence on total length!
      writer.write(distinguishedName.getName().length, CERTIFICATE_AUTHORITY_LENGTH_BITS);
      writer.writeBytes(distinguishedName.getName());
    }

    return writer.toByteArray();
  }

  public static HandshakeMessage fromByteArray(byte[] byteArray) {
    DatagramReader reader = new DatagramReader(byteArray);
   
    int length = reader.read(CERTIFICATE_TYPES_LENGTH_BITS);
    List<ClientCertificateType> certificateTypes = new ArrayList<ClientCertificateType>();
    for (int i = 0; i < length; i++) {
      int code = reader.read(CERTIFICATE_TYPE_BITS);
      certificateTypes.add(ClientCertificateType.getTypeByCode(code));
    }
   
    length = reader.read(SUPPORTED_SIGNATURE_LENGTH_BITS);
    List<SignatureAndHashAlgorithm> supportedSignatureAlgorithms = new ArrayList<SignatureAndHashAlgorithm>();
    for (int i = 0; i < length; i += 2) {
      int codeHash = reader.read(SUPPORTED_SIGNATURE_BITS);
      int codeSignature = reader.read(SUPPORTED_SIGNATURE_BITS);
      supportedSignatureAlgorithms.add(new SignatureAndHashAlgorithm(HashAlgorithm.getAlgorithmByCode(codeHash), SignatureAlgorithm.getAlgorithmByCode(codeSignature)));
    }
   
    length = reader.read(CERTIFICATE_AUTHORITIES_LENGTH_BITS);
    List<DistinguishedName> certificateAuthorities = new ArrayList<DistinguishedName>();
    while (length > 0) {
      int nameLength = reader.read(CERTIFICATE_AUTHORITY_LENGTH_BITS);
      byte[] name = reader.readBytes(nameLength);
      certificateAuthorities.add(new DistinguishedName(name));
     
      length -= 2 + name.length;
     
    }
   
    return new CertificateRequest(certificateTypes, supportedSignatureAlgorithms, certificateAuthorities);

  }

  // Enums //////////////////////////////////////////////////////////

  /**
   * Certificate types that the client may offer. See <a
   * href="http://tools.ietf.org/html/rfc5246#section-7.4.4">RFC 5246</a> for
   * details.
   *
   * @author Stefan Jucker
   *
   */
  public enum ClientCertificateType {
    RSA_SIGN(1), DSS_SIGN(2), RSA_FIXED_DH(3), DSS_FIXED_DH(4), RSA_EPHEMERAL_DH_RESERVED(5), DSS_EPHEMERAL_DH_RESERVED(6), FORTEZZA_DMS_RESERVED(20), ECDSA_SIGN(64), RSA_FIXED_ECDH(65), ECDSA_FIXED_ECDH(66);

    private int code;

    private ClientCertificateType(int code) {
      this.code = code;
    }

    public int getCode() {
      return code;
    }
   
    public static ClientCertificateType getTypeByCode(int code) {
      switch (code) {
      case 1:
        return RSA_SIGN;
      case 2:
        return DSS_SIGN;
      case 3:
        return RSA_FIXED_DH;
      case 4:
        return DSS_FIXED_DH;
      case 5:
        return RSA_EPHEMERAL_DH_RESERVED;
      case 6:
        return DSS_EPHEMERAL_DH_RESERVED;
      case 20:
        return FORTEZZA_DMS_RESERVED;
      case 64:
        return ECDSA_SIGN;
      case 65:
        return RSA_FIXED_ECDH;
      case 66:
        return ECDSA_FIXED_ECDH;

      default:
        return null;
      }
    }
  }

  /**
   * See <a href="http://tools.ietf.org/html/rfc5246#appendix-A.4.1">RFC
   * 5246</a> for details. Code is at most 255 (1 byte needed for
   * representation).
   *
   * @author Stefan Jucker
   *
   */
  public enum HashAlgorithm {
    NONE(0), MD5(1), SHA1(2), SHA224(3), SHA256(4), SHA384(5), SHA512(6);

    private int code;

    private HashAlgorithm(int code) {
      this.code = code;
    }
   
    public static HashAlgorithm getAlgorithmByCode(int code) {
      switch (code) {
      case 0:
        return NONE;
      case 1:
        return MD5;
      case 2:
        return SHA1;
      case 3:
        return SHA224;
      case 4:
        return SHA256;
      case 5:
        return SHA384;
      case 6:
        return SHA512;

      default:
        return null;
      }
    }

    public int getCode() {
      return code;
    }

    public void setCode(int code) {
      this.code = code;
    }

    @Override
    public String toString() {
      switch (code) {
      case 0:
        return "NONE";
      case 1:
        return "MD5";
      case 2:
        return "SHA1";
      case 3:
        return "SHA224";
      case 4:
        return "SHA256";
      case 5:
        return "SHA384";
      case 6:
        return "SHA512";

      default:
        return "";
      }
    }
  }

  /**
   * See <a href="http://tools.ietf.org/html/rfc5246#appendix-A.4.1">RFC
   * 5246</a> for details. Code is at most 255 (1 byte needed for
   * representation).
   *
   * @author Stefan Jucker
   *
   */
  public enum SignatureAlgorithm {
    ANONYMOUS(0), RSA(1), DSA(2), ECDSA(3);

    private int code;

    private SignatureAlgorithm(int code) {
      this.code = code;
    }
   
    public static SignatureAlgorithm getAlgorithmByCode(int code) {
      switch (code) {
      case 0:
        return ANONYMOUS;
      case 1:
        return RSA;
      case 2:
        return DSA;
      case 3:
        return ECDSA;

      default:
        return null;
      }
    }

    public int getCode() {
      return code;
    }

    public void setCode(int code) {
      this.code = code;
    }
   
    @Override
    public String toString() {
      switch (code) {
      case 0:
        return "Anonymous";
      case 1:
        return "RSA";
      case 2:
        return "DSA";
      case 3:
        return "ECDSA";

      default:
        return "";
      }
    }
  }

  /**
   * A distinguished name is between 1 and 2<sup>16</sup>-1 bytes long. See <a
   * href="http://tools.ietf.org/html/rfc5246#section-7.4.4">RFC 5246 -
   * Certificate Request</a> for details.
   *
   * @author Stefan Jucker
   *
   */
  public static class DistinguishedName {
    private byte[] name;

    public DistinguishedName(byte[] name) {
      this.name = name;
    }

    public byte[] getName() {
      return name;
    }

  }
 
  // Getters and Setters ////////////////////////////////////////////

  public void addCertificateType(ClientCertificateType certificateType) {
    certificateTypes.add(certificateType);
  }

  public void addSignatureAlgorithm(SignatureAndHashAlgorithm signatureAndHashAlgorithm) {
    supportedSignatureAlgorithms.add(signatureAndHashAlgorithm);
  }

  public void addCertificateAuthority(DistinguishedName authority) {
    certificateAuthorities.add(authority);
  }
 
  /**
   * Takes a list of trusted certificates, extracts the subject principal and
   * adds the DER-encoded distinguished name to the certificate authorities.
   *
   * @param certificateAuthorities
   *            trusted certificates.
   */
  public void addCertificateAuthorities(Certificate[] certificateAuthorities) {
    for (Certificate certificate : certificateAuthorities) {
      byte[] ca = ((X509Certificate) certificate).getSubjectX500Principal().getEncoded();
      addCertificateAuthority(new DistinguishedName(ca));
    }
  }

  public List<ClientCertificateType> getCertificateTypes() {
    return certificateTypes;
  }

  public List<SignatureAndHashAlgorithm> getSupportedSignatureAlgorithms() {
    return supportedSignatureAlgorithms;
  }

  public List<DistinguishedName> getCertificateAuthorities() {
    return certificateAuthorities;
  }

}
TOP

Related Classes of ch.ethz.inf.vs.scandium.dtls.CertificateRequest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.