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

com.unboundid.util.ssl.cert.EllipticCurvePublicKey Maven / Gradle / Ivy

/*
 * Copyright 2017-2018 Ping Identity Corporation
 * All Rights Reserved.
 */
/*
 * Copyright (C) 2017-2018 Ping Identity Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License (GPLv2 only)
 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see .
 */
package com.unboundid.util.ssl.cert;



import java.math.BigInteger;

import com.unboundid.asn1.ASN1BitString;
import com.unboundid.util.Debug;
import com.unboundid.util.NotMutable;
import com.unboundid.util.StaticUtils;
import com.unboundid.util.ThreadSafety;
import com.unboundid.util.ThreadSafetyLevel;

import static com.unboundid.util.ssl.cert.CertMessages.*;



/**
 * This class provides a data structure for representing the information
 * contained in an elliptic curve public key in an X.509 certificate.  As per
 * RFC 5480 section 2.2,
 * and the Standards for Efficient Cryptography SEC 1 document.
 */
@NotMutable()
@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
public final class EllipticCurvePublicKey
       extends DecodedPublicKey
{
  /**
   * The serial version UID for this serializable class.
   */
  private static final long serialVersionUID = 7537378153089968013L;



  // Indicates whether the y coordinate is even or odd.
  private final boolean yCoordinateIsEven;

  // The x coordinate for the public key.
  private final BigInteger xCoordinate;

  // The y coordinate for the public key.
  private final BigInteger yCoordinate;



  /**
   * Creates a new elliptic curve public key with the provided information.
   *
   * @param  xCoordinate  The x coordinate for the public key.  This must not be
   *                      {@code null}.
   * @param  yCoordinate  The y coordinate for the public key.  This must not be
   *                      {@code null}.
   */
  EllipticCurvePublicKey(final BigInteger xCoordinate,
                         final BigInteger yCoordinate)
  {
    this.xCoordinate = xCoordinate;
    this.yCoordinate = yCoordinate;
    yCoordinateIsEven =
         yCoordinate.mod(BigInteger.valueOf(2L)).equals(BigInteger.ZERO);
  }



  /**
   * Creates a new elliptic curve public key with the provided information.
   *
   * @param  xCoordinate        The x coordinate for the public key.  This must
   *                            not be {@code null}.
   * @param  yCoordinateIsEven  Indicates whether the y coordinate for the
   *                            public key is even.
   */
  EllipticCurvePublicKey(final BigInteger xCoordinate,
                         final boolean yCoordinateIsEven)
  {
    this.xCoordinate = xCoordinate;
    this.yCoordinateIsEven = yCoordinateIsEven;

    yCoordinate = null;
  }



  /**
   * Creates a new elliptic curve decoded public key from the provided bit
   * string.
   *
   * @param  subjectPublicKey  The bit string containing the encoded public key.
   *
   * @throws  CertException  If the provided public key cannot be decoded as an
   *                         elliptic curve public key.
   */
  EllipticCurvePublicKey(final ASN1BitString subjectPublicKey)
       throws CertException
  {
    try
    {
      final byte[] keyBytes = subjectPublicKey.getBytes();
      if (keyBytes.length == 65)
      {
        if (keyBytes[0] != 0x04)
        {
          throw new CertException(
               ERR_EC_PUBLIC_KEY_PARSE_UNEXPECTED_UNCOMPRESSED_FIRST_BYTE.get(
                    StaticUtils.toHex(keyBytes[0])));
        }

        final byte[] xBytes = new byte[32];
        final byte[] yBytes = new byte[32];
        System.arraycopy(keyBytes, 1, xBytes, 0, 32);
        System.arraycopy(keyBytes, 33, yBytes, 0, 32);
        xCoordinate = new BigInteger(xBytes);
        yCoordinate = new BigInteger(yBytes);
        yCoordinateIsEven = ((keyBytes[64] & 0x01) == 0x00);
      }
      else if (keyBytes.length == 33)
      {
        yCoordinate = null;
        if (keyBytes[0] == 0x02)
        {
          yCoordinateIsEven = true;
        }
        else if (keyBytes[0] == 0x03)
        {
          yCoordinateIsEven = false;
        }
        else
        {
          throw new CertException(
               ERR_EC_PUBLIC_KEY_PARSE_UNEXPECTED_COMPRESSED_FIRST_BYTE.get(
                    StaticUtils.toHex(keyBytes[0])));
        }

        final byte[] xBytes = new byte[32];
        System.arraycopy(keyBytes, 1, xBytes, 0, 32);
        xCoordinate = new BigInteger(xBytes);
      }
      else
      {
        throw new CertException(
             ERR_EC_PUBLIC_KEY_PARSE_UNEXPECTED_SIZE.get(keyBytes.length));
      }
    }
    catch (final CertException e)
    {
      Debug.debugException(e);
      throw e;
    }
    catch (final Exception e)
    {
      Debug.debugException(e);
      throw new CertException(
           ERR_EC_PUBLIC_KEY_PARSE_ERROR.get(
                StaticUtils.getExceptionMessage(e)),
           e);
    }
  }



  /**
   * Encodes this elliptic curve public key.
   *
   * @return  The encoded public key.
   *
   * @throws  CertException  If a problem is encountered while encoding this
   *                         public key.
   */
  ASN1BitString encode()
       throws CertException
  {
    final byte[] publicKeyBytes;
    if (yCoordinate == null)
    {
      publicKeyBytes = new byte[33];
      if (yCoordinateIsEven)
      {
        publicKeyBytes[0] = 0x02;
      }
      else
      {
        publicKeyBytes[0] = 0x03;
      }
    }
    else
    {
      publicKeyBytes = new byte[65];
      publicKeyBytes[0] = 0x04;
    }

    final byte[] xCoordinateBytes = xCoordinate.toByteArray();
    if (xCoordinateBytes.length > 32)
    {
      throw new CertException(ERR_EC_PUBLIC_KEY_ENCODE_X_TOO_LARGE.get(
           toString(), xCoordinateBytes.length));
    }

    final int xStartPos = 33 - xCoordinateBytes.length;
    System.arraycopy(xCoordinateBytes, 0, publicKeyBytes, xStartPos,
         xCoordinateBytes.length);

    if (yCoordinate != null)
    {
      final byte[] yCoordinateBytes = yCoordinate.toByteArray();
      if (yCoordinateBytes.length > 32)
      {
        throw new CertException(ERR_EC_PUBLIC_KEY_ENCODE_Y_TOO_LARGE.get(
             toString(), yCoordinateBytes.length));
      }

      final int yStartPos = 65 - yCoordinateBytes.length;
      System.arraycopy(yCoordinateBytes, 0, publicKeyBytes, yStartPos,
           yCoordinateBytes.length);
    }

    final boolean[] bits = ASN1BitString.getBitsForBytes(publicKeyBytes);
    return new ASN1BitString(bits);
  }



  /**
   * Indicates whether the public key uses the compressed form (which merely
   * contains the x coordinate and an indication as to whether the y coordinate
   * is even or odd) or the uncompressed form (which contains both the x and
   * y coordinate values).
   *
   * @return  {@code true} if the public key uses the compressed form, or
   *          {@code false} if it uses the uncompressed form.
   */
  public boolean usesCompressedForm()
  {
    return (yCoordinate == null);
  }



  /**
   * Retrieves the value of the x coordinate.  This will always be available.
   *
   * @return  The value of the x coordinate.
   */
  public BigInteger getXCoordinate()
  {
    return xCoordinate;
  }



  /**
   * Retrieves the value of the y coordinate.  This will only be available if
   * the key was encoded in the uncompressed form.
   *
   * @return  The value of the y coordinate, or {@code null} if the key was
   *          encoded in the compressed form.
   */
  public BigInteger getYCoordinate()
  {
    return yCoordinate;
  }



  /**
   * Indicates whether the y coordinate is even or odd.
   *
   * @return  {@code true} if the y coordinate is even, or {@code false} if the
   *          y coordinate is odd.
   */
  public boolean yCoordinateIsEven()
  {
    return yCoordinateIsEven;
  }



  /**
   * {@inheritDoc}
   */
  @Override()
  public void toString(final StringBuilder buffer)
  {
    buffer.append("EllipticCurvePublicKey(usesCompressedForm=");
    buffer.append(yCoordinate == null);
    buffer.append(", xCoordinate=");
    buffer.append(xCoordinate);

    if (yCoordinate == null)
    {
      buffer.append(", yCoordinateIsEven=");
      buffer.append(yCoordinateIsEven);
    }
    else
    {
      buffer.append(", yCoordinate=");
      buffer.append(yCoordinate);
    }

    buffer.append(')');
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy