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

org.eclipse.californium.scandium.dtls.CertificateRequest Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2015 Institute for Pervasive Computing, ETH Zurich and others.
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 * 
 * The Eclipse Public License is available at
 *    http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at
 *    http://www.eclipse.org/org/documents/edl-v10.html.
 * 
 * Contributors:
 *    Matthias Kovatsch - creator and main architect
 *    Stefan Jucker - DTLS implementation
 *    Kai Hudalla (Bosch Software Innovations GmbH) - add accessor for peer address
 ******************************************************************************/
package org.eclipse.californium.scandium.dtls;

import java.net.InetSocketAddress;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import javax.security.auth.x500.X500Principal;

import org.eclipse.californium.scandium.util.DatagramReader;
import org.eclipse.californium.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 RFC 5246, 7.4.4.
 * Certificate Request.
 */
public final 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 certificateTypes;

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

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

	// Constructors ///////////////////////////////////////////////////
	
	/**
	 * Initializes an empty certificate request.
	 * 
	 * @param peerAddress the IP address and port of the peer this
	 *           message has been received from or should be sent to
	 */
	public CertificateRequest(InetSocketAddress peerAddress) {
		this(
			new ArrayList(),
			new ArrayList(),
			new ArrayList(),
			peerAddress);
	}

	/**
	 * 
	 * @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.
	 * @param peerAddress the IP address and port of the peer this
	 *            message has been received from or should be sent to
	 */
	public CertificateRequest(
			List certificateTypes,
			List supportedSignatureAlgorithms,
			List certificateAuthorities,
			InetSocketAddress peerAddress) {
		super(peerAddress);
		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.isEmpty()) {
			sb.append("\t\tClient certificate type:\n");
			for (ClientCertificateType type : certificateTypes) {
				sb.append("\t\t\t").append(type).append("\n");
			}
		}
		if (!supportedSignatureAlgorithms.isEmpty()) {
			sb.append("\t\tSignature and hash algorithm:\n");
			for (SignatureAndHashAlgorithm algo : supportedSignatureAlgorithms) {
				sb.append("\t\t\t").append(algo).append("\n");
			}
		}
		if (!certificateAuthorities.isEmpty()) {
			sb.append("\t\tCertificate authorities:\n");
			for (DistinguishedName name : certificateAuthorities) {
				X500Principal principal = new X500Principal(name.getName());
				sb.append("\t\t\t").append(principal.getName()).append("\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, InetSocketAddress peerAddress) {
		DatagramReader reader = new DatagramReader(byteArray);
		
		int length = reader.read(CERTIFICATE_TYPES_LENGTH_BITS);
		List certificateTypes = new ArrayList();
		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 supportedSignatureAlgorithms = new ArrayList();
		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 certificateAuthorities = new ArrayList();
		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, peerAddress);

	}

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

	/**
	 * Certificate types that the client may offer. See RFC 5246 for
	 * details.
	 */
	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 RFC
	 * 5246 for details. Code is at most 255 (1 byte needed for
	 * representation).
	 */
	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 RFC
	 * 5246 for details. Code is at most 255 (1 byte needed for
	 * representation).
	 */
	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 216-1 bytes long. See RFC 5246 -
	 * Certificate Request for details.
	 */
	public static class DistinguishedName {
		private final byte[] name;

		public DistinguishedName(byte[] name) {
			this.name = Arrays.copyOf(name, name.length);
		}

		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) {
		if (certificateAuthorities != null){
			for (Certificate certificate : certificateAuthorities) {
				byte[] ca = ((X509Certificate) certificate).getSubjectX500Principal().getEncoded();
				addCertificateAuthority(new DistinguishedName(ca));
			}
		}
	}

	public List getCertificateTypes() {
		return Collections.unmodifiableList(certificateTypes);
	}

	public List getSupportedSignatureAlgorithms() {
		return Collections.unmodifiableList(supportedSignatureAlgorithms);
	}

	public List getCertificateAuthorities() {
		return Collections.unmodifiableList(certificateAuthorities);
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy