com.unboundid.util.ssl.cert.EllipticCurvePublicKey Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of unboundid-ldapsdk Show documentation
Show all versions of unboundid-ldapsdk Show documentation
The UnboundID LDAP SDK for Java is a fast, comprehensive, and easy-to-use
Java API for communicating with LDAP directory servers and performing
related tasks like reading and writing LDIF, encoding and decoding data
using base64 and ASN.1 BER, and performing secure communication. This
package contains the Standard Edition of the LDAP SDK, which is a
complete, general-purpose library for communicating with LDAPv3 directory
servers.
/*
* Copyright 2017-2022 Ping Identity Corporation
* All Rights Reserved.
*/
/*
* Copyright 2017-2022 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-2022 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(')');
}
}