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-2020 Ping Identity Corporation
 * All Rights Reserved.
 */
/*
 * Copyright 2017-2020 Ping Identity Corporation
 *
 * 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.
 */
/*
 * Copyright (C) 2017-2020 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.NotNull;
import com.unboundid.util.Nullable;
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.
  @NotNull private final BigInteger xCoordinate;

  // The y coordinate for the public key.
  @Nullable 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(@NotNull final BigInteger xCoordinate,
                         @NotNull 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(@NotNull 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(@NotNull final ASN1BitString subjectPublicKey)
       throws CertException
  {
    try
    {
      final byte[] xBytes;
      final byte[] yBytes;
      final byte[] keyBytes = subjectPublicKey.getBytes();
      switch (keyBytes.length)
      {
        case 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(
                      keyBytes.length, StaticUtils.toHex(keyBytes[0])));
          }

          xBytes = new byte[32];
          System.arraycopy(keyBytes, 1, xBytes, 0, 32);
          xCoordinate = new BigInteger(xBytes);
          break;

        case 49:
          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(
                      keyBytes.length, StaticUtils.toHex(keyBytes[0])));
          }

          xBytes = new byte[48];
          System.arraycopy(keyBytes, 1, xBytes, 0, 48);
          xCoordinate = new BigInteger(xBytes);
          break;

        case 65:
          if (keyBytes[0] != 0x04)
          {
            throw new CertException(
                 ERR_EC_PUBLIC_KEY_PARSE_UNEXPECTED_UNCOMPRESSED_FIRST_BYTE.get(
                      keyBytes.length, StaticUtils.toHex(keyBytes[0])));
          }

          xBytes = new byte[32];
          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);
          break;

        case 97:
          if (keyBytes[0] != 0x04)
          {
            throw new CertException(
                 ERR_EC_PUBLIC_KEY_PARSE_UNEXPECTED_UNCOMPRESSED_FIRST_BYTE.get(
                      keyBytes.length, StaticUtils.toHex(keyBytes[0])));
          }

          xBytes = new byte[48];
          yBytes = new byte[48];
          System.arraycopy(keyBytes, 1, xBytes, 0, 48);
          System.arraycopy(keyBytes, 49, yBytes, 0, 48);
          xCoordinate = new BigInteger(xBytes);
          yCoordinate = new BigInteger(yBytes);
          yCoordinateIsEven = ((keyBytes[96] & 0x01) == 0x00);
          break;

        default:
          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.
   */
  @NotNull()
  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.
   */
  @NotNull()
  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.
   */
  @Nullable()
  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(@NotNull 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 - 2025 Weber Informatics LLC | Privacy Policy