com.alliander.osgp.oslp.OslpEnvelope.java Source code

Java tutorial

Introduction

Here is the source code for com.alliander.osgp.oslp.OslpEnvelope.java

Source

/**
 * Copyright 2015 Smart Society Services B.V.
 *
 * 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
 */
package com.alliander.osgp.oslp;

import java.io.Serializable;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Arrays;

import org.apache.commons.lang3.ArrayUtils;

import com.alliander.osgp.oslp.Oslp.Message;

/**
 * Envelope class which holds the OSLP payload.
 */
public class OslpEnvelope implements Serializable {
    /**
     * Serial Version UID.
     */
    private static final long serialVersionUID = -7877297705451116796L;

    /**
     * Constant for security configuration errors.
     */
    private static final String SECURITY_CONFIG_EXCEPTION = "Given security settings caused an error.";

    /**
     * Length of the security hash NOTE: length for ECDSA could be smaller (73),
     * but leave it for now on 128 for testing with AME.
     */
    public static final int SECURITY_KEY_LENGTH = 128;

    /**
     * Length of the sequence number.
     */
    public static final int SEQUENCE_NUMBER_LENGTH = 2;

    /**
     * Length of the manufacturer id.
     */
    public static final int MANUFACTURER_ID_LENGTH = 2;

    /**
     * Length of the device id.
     */
    public static final int DEVICE_ID_LENGTH = 10;

    /**
     * Length of the length.
     */
    public static final int LENGTH_INDICATOR_LENGTH = 2;

    /**
     * Buffer for security key bytes.
     */
    private byte[] securityKey = new byte[SECURITY_KEY_LENGTH];

    /**
     * Buffer for sequence number bytes.
     */
    private byte[] sequenceNumber = new byte[SEQUENCE_NUMBER_LENGTH];

    /**
     * Buffer for deviceid bytes.
     */
    private byte[] deviceId = new byte[DEVICE_ID_LENGTH + MANUFACTURER_ID_LENGTH];

    /**
     * Buffer for OSLP payload.
     */
    private Message payloadMessage;

    /**
     * Identification of signature algorithm.
     */
    private String signature;

    /**
     * Provider of signature algorithm.
     */
    private String provider;

    /**
     * PrivateKey used for signing.
     */
    private PrivateKey privateKey;

    /**
     * Indicates whether message is valid. Only available after validate method
     * has been called.
     */
    private boolean valid;

    /**
     * Default constructor.
     */
    public OslpEnvelope() {
    }

    /**
     * Private constructor.
     *
     * @param signature
     *            signature algorithm
     * @param provider
     *            algorithm provider
     * @param privateKey
     *            privatekey for signing
     * @param publicKey
     *            publickey for validation
     * @param securityKey
     *            securitykey containing validation bytes
     * @param sequenceNumber
     *            sequencenumber
     * @param manufacturerId
     *            manufacturerid of the device, 2 bytes
     * @param deviceId
     *            deviceid of the device, 10 bytes
     * @param payloadMessage
     *            payload to deliver
     */
    private OslpEnvelope(final String signature, final String provider, final PrivateKey privateKey,
            final byte[] securityKey, final byte[] sequenceNumber, final byte[] deviceId,
            final Message payloadMessage) {
        this.signature = signature;
        this.provider = provider;
        this.privateKey = privateKey;
        this.setSequenceNumber(sequenceNumber);
        this.setDeviceId(deviceId);
        this.setPayloadMessage(payloadMessage);

        // Generate new securityKey when not available
        if (securityKey == null || ArrayUtils.isEmpty(securityKey)) {
            this.setSecurityKey();
        } else {
            this.setSecurityKey(securityKey);
        }
    }

    /**
     * @return security key bytes.
     */
    public byte[] getSecurityKey() {
        return Arrays.copyOf(this.securityKey, this.securityKey.length);
    }

    /**
     * set security key bytes.
     *
     * @param newSecurityKey
     */
    public void setSecurityKey(final byte[] newSecurityKey) {
        if (newSecurityKey == null) {
            this.securityKey = new byte[0];
        } else {
            if (newSecurityKey.length != SECURITY_KEY_LENGTH) {
                throw new IllegalArgumentException("SecurityKey is not of expected Length: " + SECURITY_KEY_LENGTH);
            }
            this.securityKey = Arrays.copyOf(newSecurityKey, newSecurityKey.length);
        }
    }

    /**
     * @return length indicator, based on overall message length including
     *         envelope.
     */
    public byte[] getLengthIndicator() {
        int messageLength = 0;

        if (this.payloadMessage != null) {
            messageLength += this.payloadMessage.getSerializedSize();
        }

        final byte[] sizeBytes = new byte[LENGTH_INDICATOR_LENGTH];
        sizeBytes[0] = (byte) ((messageLength >>> 8) & 0xFF);
        sizeBytes[1] = (byte) (messageLength & 0xFF);

        return sizeBytes;
    }

    /**
     * @return sequence number
     */
    public byte[] getSequenceNumber() {
        return Arrays.copyOf(this.sequenceNumber, this.sequenceNumber.length);
    }

    /**
     * Set new sequence number.
     *
     * @param newSequenceNumber
     */
    public void setSequenceNumber(final byte[] newSequenceNumber) {
        if (newSequenceNumber == null) {
            this.sequenceNumber = new byte[0];
        } else {
            if (newSequenceNumber.length != SEQUENCE_NUMBER_LENGTH) {
                throw new IllegalArgumentException(
                        "SequenceNumber is not of expected Length: " + SEQUENCE_NUMBER_LENGTH);
            }
            this.sequenceNumber = Arrays.copyOf(newSequenceNumber, newSequenceNumber.length);
        }
    }

    /**
     * @return deviceid
     */
    public byte[] getDeviceId() {
        return Arrays.copyOf(this.deviceId, this.deviceId.length);
    }

    /**
     * Set new device id and new manufacturer id. Both values will be combined
     * into one array.
     *
     * @param newDeviceId
     */
    public void setDeviceId(final byte[] newDeviceId) {
        // Check if the parameter is null.
        if (newDeviceId == null) {
            this.deviceId = new byte[0];
        } else {
            // Check the length.
            if (newDeviceId.length != (DEVICE_ID_LENGTH + MANUFACTURER_ID_LENGTH)) {
                throw new IllegalArgumentException("ManufacturerId + DeviceId is not of expected Length: "
                        + (DEVICE_ID_LENGTH + MANUFACTURER_ID_LENGTH));
            }

            // Set the combined Manufacturer ID and Device ID in the deviceId
            // field.
            this.deviceId = Arrays.copyOf(newDeviceId, newDeviceId.length);
        }
    }

    /**
     * @return OSLP payload
     */
    public Message getPayloadMessage() {
        return this.payloadMessage;
    }

    /**
     * Sets new OSLP payload.
     *
     * @param payloadMessage
     */
    public void setPayloadMessage(final Message payloadMessage) {
        this.payloadMessage = payloadMessage;
    }

    /**
     * Validates the envelope + payload (excluding securityKey) using signature
     * algorithm.
     *
     * @return true when securityKey and calculated signature match or false
     *         otherwise.
     */
    public boolean validate(final PublicKey publicKey) {
        try {
            this.valid = OslpUtils.validateSignature(OslpUtils.createSignBytes(this), this.getSecurityKey(),
                    publicKey, this.signature, this.provider);
        } catch (final GeneralSecurityException e) {
            throw new IllegalArgumentException(SECURITY_CONFIG_EXCEPTION, e);
        }

        return this.valid;
    }

    /**
     * Indicates whether message is valid. Only available after validate method
     * has been called.
     */
    public boolean isValid() {
        return this.valid;
    }

    /**
     * Calculate the new securityKey based on envelope + payload (excluding
     * securityKey) using signature algorithm.
     */
    private void setSecurityKey() {
        try {
            // Calculate and encrypt hash
            final byte[] sig = OslpUtils.createSignature(OslpUtils.createSignBytes(this), this.privateKey,
                    this.signature, this.provider);
            System.arraycopy(sig, 0, this.securityKey, 0, sig.length);
        } catch (final GeneralSecurityException e) {
            throw new IllegalArgumentException(SECURITY_CONFIG_EXCEPTION, e);
        }
    }

    @Override
    public boolean equals(final Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || this.getClass() != o.getClass()) {
            return false;
        }
        final OslpEnvelope that = (OslpEnvelope) o;
        if (this.signature != null ? !this.signature.equals(that.signature) : that.signature != null) {
            return false;
        }
        if (!Arrays.equals(this.deviceId, that.deviceId)) {
            return false;
        }
        if (this.provider != null ? !this.provider.equals(that.provider) : that.provider != null) {
            return false;
        }
        if (this.payloadMessage != null ? !this.payloadMessage.equals(that.payloadMessage)
                : that.payloadMessage != null) {
            return false;
        }
        if (this.privateKey != null ? !this.privateKey.equals(that.privateKey) : that.privateKey != null) {
            return false;
        }
        if (!Arrays.equals(this.securityKey, that.securityKey)) {
            return false;
        }
        if (!Arrays.equals(this.sequenceNumber, that.sequenceNumber)) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int result = this.securityKey != null ? Arrays.hashCode(this.securityKey) : 0;
        result = 31 * result + (this.sequenceNumber != null ? Arrays.hashCode(this.sequenceNumber) : 0);
        result = 31 * result + (this.deviceId != null ? Arrays.hashCode(this.deviceId) : 0);
        result = 31 * result + (this.payloadMessage != null ? this.payloadMessage.hashCode() : 0);
        result = 31 * result + (this.signature != null ? this.signature.hashCode() : 0);
        result = 31 * result + (this.provider != null ? this.provider.hashCode() : 0);
        result = 31 * result + (this.privateKey != null ? this.privateKey.hashCode() : 0);
        return result;
    }

    public int getSize() {
        return OslpEnvelope.SECURITY_KEY_LENGTH + OslpEnvelope.SEQUENCE_NUMBER_LENGTH
                + OslpEnvelope.MANUFACTURER_ID_LENGTH + OslpEnvelope.DEVICE_ID_LENGTH
                + OslpEnvelope.LENGTH_INDICATOR_LENGTH + this.payloadMessage.getSerializedSize();
    }

    /**
     * Builder which constructs an OSLP envelope.
     */
    public static class Builder {
        private String signature;
        private String provider;
        private PrivateKey privateKey;
        private byte[] securityKey;
        private byte[] sequenceNumber = new byte[SEQUENCE_NUMBER_LENGTH];
        private byte[] deviceId = new byte[DEVICE_ID_LENGTH + MANUFACTURER_ID_LENGTH];
        private Message payloadMessage = Message.getDefaultInstance();

        public Builder withSignature(final String signature) {
            this.signature = signature;
            return this;
        }

        public Builder withProvider(final String provider) {
            this.provider = provider;
            return this;
        }

        public Builder withPrimaryKey(final PrivateKey privateKey) {
            this.privateKey = privateKey;
            return this;
        }

        public Builder withSecurityKey(final byte[] newSecurityKey) {
            if (newSecurityKey == null) {
                this.securityKey = new byte[0];
            } else {
                this.securityKey = Arrays.copyOf(newSecurityKey, newSecurityKey.length);
            }

            return this;
        }

        public Builder withSequenceNumber(final byte[] newSequenceNumber) {
            if (newSequenceNumber == null) {
                this.sequenceNumber = new byte[0];
            } else {
                this.sequenceNumber = Arrays.copyOf(newSequenceNumber, newSequenceNumber.length);
            }

            return this;
        }

        public Builder withDeviceId(final byte[] newDeviceId) {
            if (newDeviceId == null) {
                this.deviceId = new byte[0];
            } else {
                this.deviceId = Arrays.copyOf(newDeviceId, newDeviceId.length);
            }

            return this;
        }

        public Builder withPayloadMessage(final Message payloadMessage) {
            this.payloadMessage = payloadMessage;
            return this;
        }

        public OslpEnvelope build() {
            return new OslpEnvelope(this.signature, this.provider, this.privateKey, this.securityKey,
                    this.sequenceNumber, this.deviceId, this.payloadMessage);
        }
    }
}