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

com.nimbusds.jose.jwk.JWK Maven / Gradle / Ivy

Go to download

Java library for Javascript Object Signing and Encryption (JOSE) and JSON Web Tokens (JWT)

There is a newer version: 9.47
Show newest version
/*
 * nimbus-jose-jwt
 *
 * Copyright 2012-2016, Connect2id Ltd.
 *
 * 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.
 */

package com.nimbusds.jose.jwk;


import com.nimbusds.jose.Algorithm;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.util.Base64;
import com.nimbusds.jose.util.*;
import com.nimbusds.jwt.util.DateUtils;

import java.io.Serializable;
import java.net.URI;
import java.security.*;
import java.security.cert.X509Certificate;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.ECParameterSpec;
import java.text.ParseException;
import java.util.*;


/**
 * The base abstract class for JSON Web Keys (JWKs). It serialises to a JSON
 * object.
 *
 * 

The following JSON object members are common to all JWK types: * *

    *
  • {@link #getKeyType kty} (required) *
  • {@link #getKeyUse use} (optional) *
  • {@link #getKeyOperations key_ops} (optional) *
  • {@link #getKeyID kid} (optional) *
  • {@link #getX509CertURL() x5u} (optional) *
  • {@link #getX509CertThumbprint() x5t} (optional) *
  • {@link #getX509CertSHA256Thumbprint() x5t#S256} (optional) *
  • {@link #getX509CertChain() x5c} (optional) *
  • {@link #getExpirationTime() exp} (optional) *
  • {@link #getNotBeforeTime() nbf} (optional) *
  • {@link #getIssueTime() iat} (optional) *
  • {@link #getKeyRevocation() revoked} (optional) *
  • {@link #getKeyStore()} *
* *

Example JWK (of the Elliptic Curve type): * *

 * {
 *   "kty" : "EC",
 *   "crv" : "P-256",
 *   "x"   : "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
 *   "y"   : "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
 *   "use" : "enc",
 *   "kid" : "1"
 * }
 * 
* * @author Vladimir Dzhuvinov * @author Justin Richer * @author Stefan Larsson * @version 2024-04-27 */ public abstract class JWK implements Serializable { private static final long serialVersionUID = 1L; /** * The MIME type of JWK objects: * {@code application/jwk+json; charset=UTF-8} */ public static final String MIME_TYPE = "application/jwk+json; charset=UTF-8"; /** * The key type, required. */ private final KeyType kty; /** * The key use, optional. */ private final KeyUse use; /** * The key operations, optional. */ private final Set ops; /** * The intended JOSE algorithm for the key, optional. */ private final Algorithm alg; /** * The key ID, optional. */ private final String kid; /** * X.509 certificate URL, optional. */ private final URI x5u; /** * X.509 certificate SHA-1 thumbprint, optional. */ @Deprecated private final Base64URL x5t; /** * X.509 certificate SHA-256 thumbprint, optional. */ private final Base64URL x5t256; /** * The X.509 certificate chain, optional. */ private final List x5c; /** * The key expiration time, optional. */ private final Date exp; /** * The key not-before time, optional. */ private final Date nbf; /** * The key issued-at time, optional. */ private final Date iat; /** * The key revocation, optional. */ private final KeyRevocation revocation; /** * The parsed X.509 certificate chain, optional. */ private final List parsedX5c; /** * Reference to the underlying key store, {@code null} if none. */ private final KeyStore keyStore; /** * Creates a new JSON Web Key (JWK). * * @param kty The key type. Must not be {@code null}. * @param use The key use, {@code null} if not specified or if the * key is intended for signing as well as encryption. * @param ops The key operations, {@code null} if not specified. * @param alg The intended JOSE algorithm for the key, {@code null} * if not specified. * @param kid The key ID, {@code null} if not specified. * @param x5u The X.509 certificate URL, {@code null} if not * specified. * @param x5t The X.509 certificate thumbprint, {@code null} if not * specified. * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null} * if not specified. * @param x5c The X.509 certificate chain, {@code null} if not * specified. * @param ks Reference to the underlying key store, {@code null} if * none. */ @Deprecated protected JWK(final KeyType kty, final KeyUse use, final Set ops, final Algorithm alg, final String kid, final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List x5c, final KeyStore ks) { this(kty, use, ops, alg, kid, x5u, x5t, x5t256, x5c, null, null, null, ks); } /** * Creates a new JSON Web Key (JWK). * * @param kty The key type. Must not be {@code null}. * @param use The key use, {@code null} if not specified or if the * key is intended for signing as well as encryption. * @param ops The key operations, {@code null} if not specified. * @param alg The intended JOSE algorithm for the key, {@code null} * if not specified. * @param kid The key ID, {@code null} if not specified. * @param x5u The X.509 certificate URL, {@code null} if not * specified. * @param x5t The X.509 certificate thumbprint, {@code null} if not * specified. * @param x5t256 The X.509 certificate SHA-256 thumbprint, {@code null} * if not specified. * @param x5c The X.509 certificate chain, {@code null} if not * specified. * @param exp The key expiration time, {@code null} if not * specified. * @param nbf The key not-before time, {@code null} if not * specified. * @param iat The key issued-at time, {@code null} if not specified. * @param ks Reference to the underlying key store, {@code null} if * none. */ @Deprecated protected JWK(final KeyType kty, final KeyUse use, final Set ops, final Algorithm alg, final String kid, final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List x5c, final Date exp, final Date nbf, final Date iat, final KeyStore ks) { this(kty, use, ops, alg, kid, x5u, x5t, x5t256, x5c, exp, nbf, iat, null, ks); } /** * Creates a new JSON Web Key (JWK). * * @param kty The key type. Must not be {@code null}. * @param use The key use, {@code null} if not specified or if * the key is intended for signing as well as * encryption. * @param ops The key operations, {@code null} if not specified. * @param alg The intended JOSE algorithm for the key, * {@code null} if not specified. * @param kid The key ID, {@code null} if not specified. * @param x5u The X.509 certificate URL, {@code null} if not * specified. * @param x5t The X.509 certificate thumbprint, {@code null} if * not specified. * @param x5t256 The X.509 certificate SHA-256 thumbprint, * {@code null} if not specified. * @param x5c The X.509 certificate chain, {@code null} if not * specified. * @param exp The key expiration time, {@code null} if not * specified. * @param nbf The key not-before time, {@code null} if not * specified. * @param iat The key issued-at time, {@code null} if not * specified. * @param revocation The key revocation, {@code null} if not specified. * @param ks Reference to the underlying key store, * {@code null} if none. */ protected JWK(final KeyType kty, final KeyUse use, final Set ops, final Algorithm alg, final String kid, final URI x5u, final Base64URL x5t, final Base64URL x5t256, final List x5c, final Date exp, final Date nbf, final Date iat, final KeyRevocation revocation, final KeyStore ks) { this.kty = Objects.requireNonNull(kty, "The key type \"" + JWKParameterNames.KEY_TYPE + "\" parameter must not be null"); if (! KeyUseAndOpsConsistency.areConsistent(use, ops)) { throw new IllegalArgumentException("The key use \"" + JWKParameterNames.PUBLIC_KEY_USE + "\" and key options \"" + JWKParameterNames.KEY_OPS + "\" parameters are not consistent, " + "see RFC 7517, section 4.3"); } this.use = use; this.ops = ops; this.alg = alg; this.kid = kid; this.x5u = x5u; this.x5t = x5t; this.x5t256 = x5t256; if (x5c != null && x5c.isEmpty()) { throw new IllegalArgumentException("The X.509 certificate chain \"" + JWKParameterNames.X_509_CERT_CHAIN + "\" must not be empty"); } this.x5c = x5c; try { parsedX5c = X509CertChainUtils.parse(x5c); } catch (ParseException e) { throw new IllegalArgumentException("Invalid X.509 certificate chain \"" + JWKParameterNames.X_509_CERT_CHAIN + "\": " + e.getMessage(), e); } this.exp = exp; this.nbf = nbf; this.iat = iat; this.revocation = revocation; this.keyStore = ks; } /** * Returns the type ({@code kty}) of this JWK. * * @return The key type. */ public KeyType getKeyType() { return kty; } /** * Returns the use ({@code use}) of this JWK. * * @return The key use, {@code null} if not specified or if the key is * intended for signing as well as encryption. */ public KeyUse getKeyUse() { return use; } /** * Returns the operations ({@code key_ops}) for this JWK. * * @return The key operations, {@code null} if not specified. */ public Set getKeyOperations() { return ops; } /** * Returns the intended JOSE algorithm ({@code alg}) for this JWK. * * @return The intended JOSE algorithm, {@code null} if not specified. */ public Algorithm getAlgorithm() { return alg; } /** * Returns the ID ({@code kid}) of this JWK. The key ID can be used to * match a specific key. This can be used, for instance, to choose a * key within a {@link JWKSet} during key rollover. The key ID may also * correspond to a JWS/JWE {@code kid} header parameter value. * * @return The key ID, {@code null} if not specified. */ public String getKeyID() { return kid; } /** * Returns the X.509 certificate URL ({@code x5u}) of this JWK. * * @return The X.509 certificate URL, {@code null} if not specified. */ public URI getX509CertURL() { return x5u; } /** * Returns the X.509 certificate SHA-1 thumbprint ({@code x5t}) of this * JWK. * * @return The X.509 certificate SHA-1 thumbprint, {@code null} if not * specified. */ @Deprecated public Base64URL getX509CertThumbprint() { return x5t; } /** * Returns the X.509 certificate SHA-256 thumbprint ({@code x5t#S256}) * of this JWK. * * @return The X.509 certificate SHA-256 thumbprint, {@code null} if * not specified. */ public Base64URL getX509CertSHA256Thumbprint() { return x5t256; } /** * Returns the X.509 certificate chain ({@code x5c}) of this JWK. * * @return The X.509 certificate chain as a unmodifiable list, * {@code null} if not specified. */ public List getX509CertChain() { if (x5c == null) { return null; } return Collections.unmodifiableList(x5c); } /** * Returns the parsed X.509 certificate chain ({@code x5c}) of this * JWK. * * @return The X.509 certificate chain as a unmodifiable list, * {@code null} if not specified. */ public List getParsedX509CertChain() { if (parsedX5c == null) { return null; } return Collections.unmodifiableList(parsedX5c); } /** * Returns the expiration time ({@code exp}) if this JWK. * * @return The expiration time, {@code null} if not specified. */ public Date getExpirationTime() { return exp; } /** * Returns the not-before ({@code nbf}) of this JWK. * * @return The not-before time, {@code null} if not specified. */ public Date getNotBeforeTime() { return nbf; } /** * Returns the issued-at ({@code iat}) time of this JWK. * * @return The issued-at time, {@code null} if not specified. */ public Date getIssueTime() { return iat; } /** * Returns the key revocation ({@code revoked}) of this JWK. * * @return The key revocation, {@code null} if not specified. */ public KeyRevocation getKeyRevocation() { return revocation; } /** * Returns a reference to the underlying key store. * * @return The underlying key store, {@code null} if none. */ public KeyStore getKeyStore() { return keyStore; } /** * Returns the required JWK parameters. Intended as input for JWK * thumbprint computation. See RFC 7638 for more information. * * @return The required JWK parameters, sorted alphanumerically by key * name and ready for JSON serialisation. */ public abstract LinkedHashMap getRequiredParams(); /** * Computes the SHA-256 thumbprint of this JWK. See RFC 7638 for more * information. * * @return The SHA-256 thumbprint. * * @throws JOSEException If the SHA-256 hash algorithm is not * supported. */ public Base64URL computeThumbprint() throws JOSEException { return computeThumbprint("SHA-256"); } /** * Computes the thumbprint of this JWK using the specified hash * algorithm. See RFC 7638 for more information. * * @param hashAlg The hash algorithm. Must not be {@code null}. * * @return The SHA-256 thumbprint. * * @throws JOSEException If the hash algorithm is not supported. */ public Base64URL computeThumbprint(final String hashAlg) throws JOSEException { return ThumbprintUtils.compute(hashAlg, this); } /** * Computes the SHA-256 thumbprint URI of this JWK. See RFC 7638 and * draft-ietf-oauth-jwk-thumbprint-uri for more information. * * @return The SHA-256 thumbprint URI. * * @throws JOSEException If the SHA-256 hash algorithm is not * supported. */ public ThumbprintURI computeThumbprintURI() throws JOSEException { return new ThumbprintURI("sha-256", computeThumbprint("SHA-256")); } /** * Returns {@code true} if this JWK contains private or sensitive * (non-public) parameters. * * @return {@code true} if this JWK contains private parameters, else * {@code false}. */ public abstract boolean isPrivate(); /** * Creates a copy of this JWK with all private or sensitive parameters * removed. * * @return The newly created public JWK, or {@code null} if none can be * created. */ public abstract JWK toPublicJWK(); /** * Returns the size of this JWK. * * @return The JWK size, in bits. */ public abstract int size(); /** * Casts this JWK to an RSA JWK. * * @return The RSA JWK. */ public RSAKey toRSAKey() { return (RSAKey)this; } /** * Casts this JWK to an EC JWK. * * @return The EC JWK. */ public ECKey toECKey() { return (ECKey)this; } /** * Casts this JWK to an octet sequence JWK. * * @return The octet sequence JWK. */ public OctetSequenceKey toOctetSequenceKey() { return (OctetSequenceKey)this; } /** * Casts this JWK to an octet key pair JWK. * * @return The octet key pair JWK. */ public OctetKeyPair toOctetKeyPair() { return (OctetKeyPair)this; } /** * Returns a JSON object representation of this JWK. This method is * intended to be called from extending classes. * *

Example: * *

	 * {
	 *   "kty" : "RSA",
	 *   "use" : "sig",
	 *   "kid" : "fd28e025-8d24-48bc-a51a-e2ffc8bc274b"
	 * }
	 * 
* * @return The JSON object representation. */ public Map toJSONObject() { Map o = JSONObjectUtils.newJSONObject(); o.put(JWKParameterNames.KEY_TYPE, kty.getValue()); if (use != null) { o.put(JWKParameterNames.PUBLIC_KEY_USE, use.identifier()); } if (ops != null) { List stringValues = JSONArrayUtils.newJSONArray(); for (KeyOperation op: ops) { stringValues.add(op.identifier()); } o.put(JWKParameterNames.KEY_OPS, stringValues); } if (alg != null) { o.put(JWKParameterNames.ALGORITHM, alg.getName()); } if (kid != null) { o.put(JWKParameterNames.KEY_ID, kid); } if (x5u != null) { o.put(JWKParameterNames.X_509_CERT_URL, x5u.toString()); } if (x5t != null) { o.put(JWKParameterNames.X_509_CERT_SHA_1_THUMBPRINT, x5t.toString()); } if (x5t256 != null) { o.put(JWKParameterNames.X_509_CERT_SHA_256_THUMBPRINT, x5t256.toString()); } if (x5c != null) { List stringValues = JSONArrayUtils.newJSONArray(); for (Base64 base64: x5c) { stringValues.add(base64.toString()); } o.put(JWKParameterNames.X_509_CERT_CHAIN, stringValues); } if (exp != null) { o.put(JWKParameterNames.EXPIRATION_TIME, DateUtils.toSecondsSinceEpoch(exp)); } if (nbf != null) { o.put(JWKParameterNames.NOT_BEFORE, DateUtils.toSecondsSinceEpoch(nbf)); } if (iat != null) { o.put(JWKParameterNames.ISSUED_AT, DateUtils.toSecondsSinceEpoch(iat)); } if (revocation != null) { o.put(JWKParameterNames.REVOKED, revocation.toJSONObject()); } return o; } /** * Returns the JSON object string representation of this JWK. * * @return The JSON object string representation. */ public String toJSONString() { return JSONObjectUtils.toJSONString(toJSONObject()); } /** * @see #toJSONString */ @Override public String toString() { return JSONObjectUtils.toJSONString(toJSONObject()); } /** * Parses a JWK from the specified JSON object string representation. * The JWK must be an {@link ECKey}, an {@link RSAKey}, or a * {@link OctetSequenceKey}. * * @param s The JSON object string to parse. Must not be {@code null}. * * @return The JWK. * * @throws ParseException If the string couldn't be parsed to a * supported JWK. */ public static JWK parse(final String s) throws ParseException { return parse(JSONObjectUtils.parse(s)); } /** * Parses a JWK from the specified JSON object representation. The JWK * must be an {@link ECKey}, an {@link RSAKey}, or a * {@link OctetSequenceKey}. * * @param jsonObject The JSON object to parse. Must not be * {@code null}. * * @return The JWK. * * @throws ParseException If the JSON object couldn't be parsed to a * supported JWK. */ public static JWK parse(final Map jsonObject) throws ParseException { String ktyString = JSONObjectUtils.getString(jsonObject, JWKParameterNames.KEY_TYPE); if (ktyString == null) { throw new ParseException("Missing key type \"kty\" parameter", 0); } KeyType kty = KeyType.parse(ktyString); if (kty == KeyType.EC) { return ECKey.parse(jsonObject); } else if (kty == KeyType.RSA) { return RSAKey.parse(jsonObject); } else if (kty == KeyType.OCT) { return OctetSequenceKey.parse(jsonObject); } else if (kty == KeyType.OKP) { return OctetKeyPair.parse(jsonObject); } else { throw new ParseException("Unsupported key type \"" + JWKParameterNames.KEY_TYPE + "\" parameter: " + kty, 0); } } /** * Parses a public {@link RSAKey RSA} or {@link ECKey EC JWK} from the * specified X.509 certificate. Requires BouncyCastle. * *

Important: The X.509 certificate is not * validated! * *

Sets the following JWK parameters: * *

    *
  • For an EC key the curve is obtained from the subject public * key info algorithm parameters. *
  • The JWK use inferred by {@link KeyUse#from}. *
  • The JWK ID from the X.509 serial number (in base 10). *
  • The JWK X.509 certificate chain (this certificate only). *
  • The JWK X.509 certificate SHA-256 thumbprint. *
* * @param cert The X.509 certificate. Must not be {@code null}. * * @return The public RSA or EC JWK. * * @throws JOSEException If parsing failed. */ public static JWK parse(final X509Certificate cert) throws JOSEException { if (cert.getPublicKey() instanceof RSAPublicKey) { return RSAKey.parse(cert); } else if (cert.getPublicKey() instanceof ECPublicKey) { return ECKey.parse(cert); } else { throw new JOSEException("Unsupported public key algorithm: " + cert.getPublicKey().getAlgorithm()); } } /** * Parses a public {@link RSAKey RSA} or {@link ECKey EC JWK} from the * specified PEM-encoded X.509 certificate. Requires BouncyCastle. * *

Important: The X.509 certificate is not * validated! * *

Sets the following JWK parameters: * *

    *
  • For an EC key the curve is obtained from the subject public * key info algorithm parameters. *
  • The JWK use inferred by {@link KeyUse#from}. *
  • The JWK ID from the X.509 serial number (in base 10). *
  • The JWK X.509 certificate chain (this certificate only). *
  • The JWK X.509 certificate SHA-256 thumbprint. *
* * @param pemEncodedCert The PEM-encoded X.509 certificate. Must not be * {@code null}. * * @return The public RSA or EC JWK. * * @throws JOSEException If parsing failed. */ public static JWK parseFromPEMEncodedX509Cert(final String pemEncodedCert) throws JOSEException { X509Certificate cert = X509CertUtils.parse(pemEncodedCert); if (cert == null) { throw new JOSEException("Couldn't parse PEM-encoded X.509 certificate"); } return parse(cert); } /** * Loads a JWK from the specified JCE key store. The JWK can be a * public / private {@link RSAKey RSA key}, a public / private * {@link ECKey EC key}, or a {@link OctetSequenceKey secret key}. * Requires BouncyCastle. * *

Important: The X.509 certificate is not * validated! * * @param keyStore The key store. Must not be {@code null}. * @param alias The alias. Must not be {@code null}. * @param pin The pin to unlock the private key if any, empty or * {@code null} if not required. * * @return The public / private RSA or EC JWK, or secret JWK, or * {@code null} if no key with the specified alias was found. * * @throws KeyStoreException On a key store exception. * @throws JOSEException If RSA or EC key loading failed. */ public static JWK load(final KeyStore keyStore, final String alias, final char[] pin) throws KeyStoreException, JOSEException { java.security.cert.Certificate cert = keyStore.getCertificate(alias); if (cert == null) { // Try secret key return OctetSequenceKey.load(keyStore, alias, pin); } if (cert.getPublicKey() instanceof RSAPublicKey) { return RSAKey.load(keyStore, alias, pin); } else if (cert.getPublicKey() instanceof ECPublicKey) { return ECKey.load(keyStore, alias, pin); } else { throw new JOSEException("Unsupported public key algorithm: " + cert.getPublicKey().getAlgorithm()); } } /** * Parses an RSA or EC JWK from the specified string of one or more * PEM-encoded object(s): * *

    *
  • X.509 certificate (PEM header: BEGIN CERTIFICATE) *
  • PKCS#1 RSAPublicKey (PEM header: BEGIN RSA PUBLIC KEY) *
  • X.509 SubjectPublicKeyInfo (PEM header: BEGIN PUBLIC KEY) *
  • PKCS#1 RSAPrivateKey (PEM header: BEGIN RSA PRIVATE KEY) *
  • PKCS#8 PrivateKeyInfo (PEM header: BEGIN PRIVATE KEY) *
  • matching pair of the above *
* *

Requires BouncyCastle. * * @param pemEncodedObjects The string of PEM-encoded object(s). * * @return The public / (private) RSA or EC JWK. * * @throws JOSEException If RSA or EC key parsing failed. */ public static JWK parseFromPEMEncodedObjects(final String pemEncodedObjects) throws JOSEException { final List keys = PEMEncodedKeyParser.parseKeys(pemEncodedObjects); if (keys.isEmpty()) { throw new JOSEException("No PEM-encoded keys found"); } final KeyPair pair = mergeKeyPairs(keys); final PublicKey publicKey = pair.getPublic(); final PrivateKey privateKey = pair.getPrivate(); if (publicKey == null) { // For EC keys, for RSA the public can be reconstructed throw new JOSEException("Missing PEM-encoded public key to construct JWK"); } if (publicKey instanceof ECPublicKey) { final ECPublicKey ecPubKey = (ECPublicKey) publicKey; final ECParameterSpec pubParams = ecPubKey.getParams(); if (privateKey instanceof ECPrivateKey) { validateEcCurves(ecPubKey, (ECPrivateKey) privateKey); } if (privateKey != null && !(privateKey instanceof ECPrivateKey)) { throw new JOSEException("Unsupported " + KeyType.EC.getValue() + " private key type: " + privateKey); } final Curve curve = Curve.forECParameterSpec(pubParams); final ECKey.Builder builder = new ECKey.Builder(curve, (ECPublicKey) publicKey); if (privateKey != null) { builder.privateKey((ECPrivateKey) privateKey); } return builder.build(); } if (publicKey instanceof RSAPublicKey) { final RSAKey.Builder builder = new RSAKey.Builder((RSAPublicKey) publicKey); if (privateKey instanceof RSAPrivateKey) { builder.privateKey((RSAPrivateKey) privateKey); } else if (privateKey != null) { throw new JOSEException("Unsupported " + KeyType.RSA.getValue() + " private key type: " + privateKey); } return builder.build(); } throw new JOSEException("Unsupported algorithm of PEM-encoded key: " + publicKey.getAlgorithm()); } private static void validateEcCurves(ECPublicKey publicKey, ECPrivateKey privateKey) throws JOSEException { final ECParameterSpec pubParams = publicKey.getParams(); final ECParameterSpec privParams = privateKey.getParams(); if (!pubParams.getCurve().equals(privParams.getCurve())) { throw new JOSEException("Public/private " + KeyType.EC.getValue() + " key curve mismatch: " + publicKey); } if (pubParams.getCofactor() != privParams.getCofactor()) { throw new JOSEException("Public/private " + KeyType.EC.getValue() + " key cofactor mismatch: " + publicKey); } if (!pubParams.getGenerator().equals(privParams.getGenerator())) { throw new JOSEException("Public/private " + KeyType.EC.getValue() + " key generator mismatch: " + publicKey); } if (!pubParams.getOrder().equals(privParams.getOrder())) { throw new JOSEException("Public/private " + KeyType.EC.getValue() + " key order mismatch: " + publicKey); } } private static KeyPair mergeKeyPairs(final List keys) throws JOSEException { final KeyPair pair; if (keys.size() == 1) { // Assume public key, or private key easy to convert to public, // otherwise not representable as a JWK pair = keys.get(0); } else if (keys.size() == 2) { // If two keys, assume public + private keys separated pair = twoKeysToKeyPair(keys); } else { throw new JOSEException("Expected key or pair of PEM-encoded keys"); } return pair; } private static KeyPair twoKeysToKeyPair(final List keys) throws JOSEException { final KeyPair key1 = keys.get(0); final KeyPair key2 = keys.get(1); if (key1.getPublic() != null && key2.getPrivate() != null) { return new KeyPair(key1.getPublic(), key2.getPrivate()); } else if (key1.getPrivate() != null && key2.getPublic() != null) { return new KeyPair(key2.getPublic(), key1.getPrivate()); } else { throw new JOSEException("Not a public/private key pair"); } } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof JWK)) return false; JWK jwk = (JWK) o; return Objects.equals(kty, jwk.kty) && Objects.equals(use, jwk.use) && Objects.equals(ops, jwk.ops) && Objects.equals(alg, jwk.alg) && Objects.equals(kid, jwk.kid) && Objects.equals(x5u, jwk.x5u) && Objects.equals(x5t, jwk.x5t) && Objects.equals(x5t256, jwk.x5t256) && Objects.equals(x5c, jwk.x5c) && Objects.equals(exp, jwk.exp) && Objects.equals(nbf, jwk.nbf) && Objects.equals(iat, jwk.iat) && Objects.equals(revocation, jwk.revocation) && Objects.equals(keyStore, jwk.keyStore); } @Override public int hashCode() { return Objects.hash(kty, use, ops, alg, kid, x5u, x5t, x5t256, x5c, exp, nbf, iat, revocation, keyStore); } }