com.nimbusds.jose.jwk.JWKMatcher Maven / Gradle / Ivy
/*
* 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 java.util.*;
import com.nimbusds.jose.Algorithm;
import net.jcip.annotations.Immutable;
/**
* JSON Web Key (JWK) matcher. May be used to ensure a JWK matches a set of
* application-specific criteria.
*
* Supported key matching criteria:
*
*
* - Any, unspecified, one or more key types (typ).
*
- Any, unspecified, one or more key uses (use).
*
- Any, unspecified, one or more key operations (key_ops).
*
- Any, unspecified, one or more key algorithms (alg).
*
- Any, unspecified, one or more key identifiers (kid).
*
- Private only key.
*
- Public only key.
*
- Minimum, maximum or exact key sizes.
*
- Any, unspecified, one or more curves for EC keys (crv).
*
*
* Matching by X.509 certificate URL, thumbprint and chain is not supported.
*
* @author Vladimir Dzhuvinov
* @version 2016-08-24
*/
@Immutable
public class JWKMatcher {
/**
* The key types to match.
*/
private final Set types;
/**
* The public key uses to match.
*/
private final Set uses;
/**
* The key operations to match.
*/
private final Set ops;
/**
* The algorithms to match.
*/
private final Set algs;
/**
* The key IDs to match.
*/
private final Set ids;
/**
* {@code true} to match a key with a set use.
*/
private final boolean hasUse;
/**
* {@code true} to match a key with a set ID.
*/
private final boolean hasID;
/**
* {@code true} to match a private key.
*/
private final boolean privateOnly;
/**
* {@code true} to match a public only key.
*/
private final boolean publicOnly;
/**
* The minimum key size in bits, zero implies no minimum size limit.
*/
private final int minSizeBits;
/**
* The maximum key size in bits, zero implies no maximum size limit.
*/
private final int maxSizeBits;
/**
* The key sizes in bits.
*/
private final Set sizesBits;
/**
* The curves to match (for EC keys).
*/
private final Set curves;
/**
* Builder for constructing JWK matchers.
*
* Example usage:
*
*
* JWKMatcher matcher = new JWKMatcher().keyID("123").build();
*
*/
public static class Builder {
/**
* The key types to match.
*/
private Set types;
/**
* The public key uses to match.
*/
private Set uses;
/**
* The key operations to match.
*/
private Set ops;
/**
* The algorithms to match.
*/
private Set algs;
/**
* The key IDs to match.
*/
private Set ids;
/**
* {@code true} to match a key with a set use.
*/
private boolean hasUse = false;
/**
* {@code true} to match a key with a set ID.
*/
private boolean hasID = false;
/**
* {@code true} to match a private key.
*/
private boolean privateOnly = false;
/**
* {@code true} to match a public only key.
*/
private boolean publicOnly = false;
/**
* The minimum key size in bits, zero implies no minimum size
* limit.
*/
private int minSizeBits = 0;
/**
* The maximum key size in bits, zero implies no maximum size
* limit.
*/
private int maxSizeBits = 0;
/**
* The key sizes in bits.
*/
private Set sizesBits;
/**
* The curves to match (for EC keys).
*/
private Set curves;
/**
* Sets a single key type to match.
*
* @param kty The key type, {@code null} if not specified.
*
* @return This builder.
*/
public Builder keyType(final KeyType kty) {
if (kty == null) {
types = null;
} else {
types = new HashSet<>(Collections.singletonList(kty));
}
return this;
}
/**
* Sets multiple key types to match.
*
* @param types The key types.
*
* @return This builder.
*/
public Builder keyTypes(final KeyType ... types) {
keyTypes(new LinkedHashSet<>(Arrays.asList(types)));
return this;
}
/**
* Sets multiple key types to match.
*
* @param types The key types, {@code null} if not specified.
*
* @return This builder.
*/
public Builder keyTypes(final Set types) {
this.types = types;
return this;
}
/**
* Sets a single public key use to match.
*
* @param use The public key use, {@code null} if not
* specified.
*
* @return This builder.
*/
public Builder keyUse(final KeyUse use) {
if (use == null) {
uses = null;
} else {
uses = new HashSet<>(Collections.singletonList(use));
}
return this;
}
/**
* Sets multiple public key uses to match.
*
* @param uses The public key uses.
*
* @return This builder.
*/
public Builder keyUses(final KeyUse... uses) {
keyUses(new LinkedHashSet<>(Arrays.asList(uses)));
return this;
}
/**
* Sets multiple public key uses to match.
*
* @param uses The public key uses, {@code null} if not
* specified.
*
* @return This builder.
*/
public Builder keyUses(final Set uses) {
this.uses = uses;
return this;
}
/**
* Sets a single key operation to match.
*
* @param op The key operation, {@code null} if not specified.
*
* @return This builder.
*/
public Builder keyOperation(final KeyOperation op) {
if (op == null) {
ops = null;
} else {
ops = new HashSet<>(Collections.singletonList(op));
}
return this;
}
/**
* Sets multiple key operations to match.
*
* @param ops The key operations.
*
* @return This builder.
*/
public Builder keyOperations(final KeyOperation... ops) {
keyOperations(new LinkedHashSet<>(Arrays.asList(ops)));
return this;
}
/**
* Sets multiple key operations to match.
*
* @param ops The key operations, {@code null} if not
* specified.
*
* @return This builder.
*/
public Builder keyOperations(final Set ops) {
this.ops = ops;
return this;
}
/**
* Sets a single JOSE algorithm to match.
*
* @param alg The JOSE algorithm, {@code null} if not
* specified.
*
* @return This builder.
*/
public Builder algorithm(final Algorithm alg) {
if (alg == null) {
algs = null;
} else {
algs = new HashSet<>(Collections.singletonList(alg));
}
return this;
}
/**
* Sets multiple JOSE algorithms to match.
*
* @param algs The JOSE algorithms.
*
* @return This builder.
*/
public Builder algorithms(final Algorithm ... algs) {
algorithms(new LinkedHashSet<>(Arrays.asList(algs)));
return this;
}
/**
* Sets multiple JOSE algorithms to match.
*
* @param algs The JOSE algorithms, {@code null} if not
* specified.
*
* @return This builder.
*/
public Builder algorithms(final Set algs) {
this.algs = algs;
return this;
}
/**
* Sets a single key ID to match.
*
* @param id The key ID, {@code null} if not specified.
*
* @return This builder.
*/
public Builder keyID(final String id) {
if (id == null) {
ids = null;
} else {
ids = new HashSet<>(Collections.singletonList(id));
}
return this;
}
/**
* Sets multiple key IDs to match.
*
* @param ids The key IDs.
*
* @return This builder.
*/
public Builder keyIDs(final String ... ids) {
keyIDs(new LinkedHashSet<>(Arrays.asList(ids)));
return this;
}
/**
* Sets multiple key IDs to match.
*
* @param ids The key IDs, {@code null} if not specified.
*
* @return This builder.
*/
public Builder keyIDs(final Set ids) {
this.ids = ids;
return this;
}
/**
* Sets key use presence matching.
*
* @param hasUse {@code true} to match a key with a set use.
*
* @return This builder.
*/
public Builder hasKeyUse(final boolean hasUse) {
this.hasUse = hasUse;
return this;
}
/**
* Sets key ID presence matching.
*
* @param hasID {@code true} to match a key with a set ID.
*
* @return This builder.
*/
public Builder hasKeyID(final boolean hasID) {
this.hasID = hasID;
return this;
}
/**
* Sets the private key matching policy.
*
* @param privateOnly {@code true} to match a private key.
*
* @return This builder.
*/
public Builder privateOnly(final boolean privateOnly) {
this.privateOnly = privateOnly;
return this;
}
/**
* Sets the public key matching policy.
*
* @param publicOnly {@code true} to match a public only key.
*
* @return This builder.
*/
public Builder publicOnly(final boolean publicOnly) {
this.publicOnly = publicOnly;
return this;
}
/**
* Sets the minimal key size.
*
* @param minSizeBits The minimum key size in bits, zero
* implies no minimum key size limit.
*
* @return This builder.
*/
public Builder minKeySize(final int minSizeBits) {
this.minSizeBits = minSizeBits;
return this;
}
/**
* Sets the maximum key size.
*
* @param maxSizeBits The maximum key size in bits, zero
* implies no maximum key size limit.
*
* @return This builder.
*/
public Builder maxKeySize(final int maxSizeBits) {
this.maxSizeBits = maxSizeBits;
return this;
}
/**
* Sets the key size.
*
* @param keySizeBits The key size in bits, zero if not
* specified.
*
* @return This builder.
*/
public Builder keySize(final int keySizeBits) {
if (keySizeBits <= 0) {
sizesBits = null;
} else {
sizesBits = Collections.singleton(keySizeBits);
}
return this;
}
/**
* Sets the key sizes.
*
* @param keySizesBits The key sizes in bits.
*
* @return This builder.
*/
public Builder keySizes(final int... keySizesBits) {
Set sizesSet = new LinkedHashSet<>();
for (int keySize: keySizesBits) {
sizesSet.add(keySize);
}
keySizes(sizesSet);
return this;
}
/**
* Sets the key sizes.
*
* @param keySizesBits The key sizes in bits.
*
* @return This builder.
*/
public Builder keySizes(final Set keySizesBits) {
this.sizesBits = keySizesBits;
return this;
}
/**
* Sets a single curve to match (for EC keys).
*
* @param curve The curve, {@code null} if not specified.
*
* @return This builder.
*/
public Builder curve(final ECKey.Curve curve) {
if (curve == null) {
curves = null;
} else {
curves = new HashSet<>(Collections.singletonList(curve));
}
return this;
}
/**
* Sets multiple curves to match (for EC keys).
*
* @param curves The curves.
*
* @return This builder.
*/
public Builder curves(final ECKey.Curve... curves) {
curves(new LinkedHashSet<>(Arrays.asList(curves)));
return this;
}
/**
* Sets multiple curves to match (for EC keys).
*
* @param curves The curves, {@code null} if not specified.
*
* @return This builder.
*/
public Builder curves(final Set curves) {
this.curves = curves;
return this;
}
/**
* Builds a new JWK matcher.
*
* @return The JWK matcher.
*/
public JWKMatcher build() {
return new JWKMatcher(types, uses, ops, algs, ids, hasUse, hasID, privateOnly, publicOnly, minSizeBits, maxSizeBits, sizesBits, curves);
}
}
/**
* Creates a new JSON Web Key (JWK) matcher.
*
* @param types The key types to match, {@code null} if not
* specified.
* @param uses The public key uses to match, {@code null} if not
* specified.
* @param ops The key operations to match, {@code null} if not
* specified.
* @param algs The JOSE algorithms to match, {@code null} if not
* specified.
* @param ids The key IDs to match, {@code null} if not
* specified.
* @param privateOnly {@code true} to match a private key.
* @param publicOnly {@code true} to match a public only key.
*/
@Deprecated
public JWKMatcher(final Set types,
final Set uses,
final Set ops,
final Set algs,
final Set ids,
final boolean privateOnly,
final boolean publicOnly) {
this(types, uses, ops, algs, ids, privateOnly, publicOnly, 0, 0);
}
/**
* Creates a new JSON Web Key (JWK) matcher.
*
* @param types The key types to match, {@code null} if not
* specified.
* @param uses The public key uses to match, {@code null} if not
* specified.
* @param ops The key operations to match, {@code null} if not
* specified.
* @param algs The JOSE algorithms to match, {@code null} if not
* specified.
* @param ids The key IDs to match, {@code null} if not
* specified.
* @param privateOnly {@code true} to match a private key.
* @param publicOnly {@code true} to match a public only key.
* @param minSizeBits The minimum key size in bits, zero implies no
* minimum size limit.
* @param maxSizeBits The maximum key size in bits, zero implies no
* maximum size limit.
*/
@Deprecated
public JWKMatcher(final Set types,
final Set uses,
final Set ops,
final Set algs,
final Set ids,
final boolean privateOnly,
final boolean publicOnly,
final int minSizeBits,
final int maxSizeBits) {
this(types, uses, ops, algs, ids, privateOnly, publicOnly, minSizeBits, maxSizeBits, null);
}
/**
* Creates a new JSON Web Key (JWK) matcher.
*
* @param types The key types to match, {@code null} if not
* specified.
* @param uses The public key uses to match, {@code null} if not
* specified.
* @param ops The key operations to match, {@code null} if not
* specified.
* @param algs The JOSE algorithms to match, {@code null} if not
* specified.
* @param ids The key IDs to match, {@code null} if not
* specified.
* @param privateOnly {@code true} to match a private key.
* @param publicOnly {@code true} to match a public only key.
* @param minSizeBits The minimum key size in bits, zero implies no
* minimum size limit.
* @param maxSizeBits The maximum key size in bits, zero implies no
* maximum size limit.
* @param curves The curves to match (for EC keys), {@code null}
* if not specified.
*/
@Deprecated
public JWKMatcher(final Set types,
final Set uses,
final Set ops,
final Set algs,
final Set ids,
final boolean privateOnly,
final boolean publicOnly,
final int minSizeBits,
final int maxSizeBits,
final Set curves) {
this(types, uses, ops, algs, ids, privateOnly, publicOnly, minSizeBits, maxSizeBits, null, curves);
}
/**
* Creates a new JSON Web Key (JWK) matcher.
*
* @param types The key types to match, {@code null} if not
* specified.
* @param uses The public key uses to match, {@code null} if not
* specified.
* @param ops The key operations to match, {@code null} if not
* specified.
* @param algs The JOSE algorithms to match, {@code null} if not
* specified.
* @param ids The key IDs to match, {@code null} if not
* specified.
* @param privateOnly {@code true} to match a private key.
* @param publicOnly {@code true} to match a public only key.
* @param minSizeBits The minimum key size in bits, zero implies no
* minimum size limit.
* @param maxSizeBits The maximum key size in bits, zero implies no
* maximum size limit.
* @param sizesBits The key sizes in bits, {@code null} if not
* specified.
* @param curves The curves to match (for EC keys), {@code null}
* if not specified.
*/
@Deprecated
public JWKMatcher(final Set types,
final Set uses,
final Set ops,
final Set algs,
final Set ids,
final boolean privateOnly,
final boolean publicOnly,
final int minSizeBits,
final int maxSizeBits,
final Set sizesBits,
final Set curves) {
this(types, uses, ops, algs, ids, false, false, privateOnly, publicOnly, minSizeBits, maxSizeBits, sizesBits, curves);
}
/**
* Creates a new JSON Web Key (JWK) matcher.
*
* @param types The key types to match, {@code null} if not
* specified.
* @param uses The public key uses to match, {@code null} if not
* specified.
* @param ops The key operations to match, {@code null} if not
* specified.
* @param algs The JOSE algorithms to match, {@code null} if not
* specified.
* @param ids The key IDs to match, {@code null} if not
* specified.
* @param hasUse {@code true} to match a key with a set use.
* @param hasID {@code true} to match a key with a set ID.
* @param privateOnly {@code true} to match a private key.
* @param publicOnly {@code true} to match a public only key.
* @param minSizeBits The minimum key size in bits, zero implies no
* minimum size limit.
* @param maxSizeBits The maximum key size in bits, zero implies no
* maximum size limit.
* @param sizesBits The key sizes in bits, {@code null} if not
* specified.
* @param curves The curves to match (for EC keys), {@code null}
* if not specified.
*/
public JWKMatcher(final Set types,
final Set uses,
final Set ops,
final Set algs,
final Set ids,
final boolean hasUse,
final boolean hasID,
final boolean privateOnly,
final boolean publicOnly,
final int minSizeBits,
final int maxSizeBits,
final Set sizesBits,
final Set curves) {
this.types = types;
this.uses = uses;
this.ops = ops;
this.algs = algs;
this.ids = ids;
this.hasUse = hasUse;
this.hasID = hasID;
this.privateOnly = privateOnly;
this.publicOnly = publicOnly;
this.minSizeBits = minSizeBits;
this.maxSizeBits = maxSizeBits;
this.sizesBits = sizesBits;
this.curves = curves;
}
/**
* Returns the key types to match.
*
* @return The key types, {@code null} if not specified.
*/
public Set getKeyTypes() {
return types;
}
/**
* Returns the public key uses to match.
*
* @return The public key uses, {@code null} if not specified.
*/
public Set getKeyUses() {
return uses;
}
/**
* Returns the key operations to match.
*
* @return The key operations, {@code null} if not specified.
*/
public Set getKeyOperations() {
return ops;
}
/**
* Returns the JOSE algorithms to match.
*
* @return The JOSE algorithms, {@code null} if not specified.
*/
public Set getAlgorithms() {
return algs;
}
/**
* Returns the key IDs to match.
*
* @return The key IDs, {@code null} if not specified.
*/
public Set getKeyIDs() {
return ids;
}
/**
* Returns {@code true} if keys with a set use are matched.
*
* @return {@code true} if keys with a set use are matched, else
* {@code false}.
*/
public boolean hasKeyUse() {
return hasUse;
}
/**
* Returns {@code true} if keys with a set use are matched.
*
* @return {@code true} if keys with a set ID are matched, else
* {@code false}.
*/
public boolean hasKeyID() {
return hasID;
}
/**
* Returns {@code true} if only private keys are matched.
*
* @return {@code true} if only private keys are matched, else
* {@code false}.
*/
public boolean isPrivateOnly() {
return privateOnly;
}
/**
* Returns {@code true} if only public keys are matched.
*
* @return {@code true} if only public keys are selected, else
* {@code false}.
*/
public boolean isPublicOnly() {
return publicOnly;
}
/**
* Returns the minimum key size. Use {@link #getMinKeySize()} instead.
*
* @return The minimum key size in bits, zero implies no minimum size
* limit.
*/
@Deprecated
public int getMinSize() {
return getMinKeySize();
}
/**
* Returns the minimum key size.
*
* @return The minimum key size in bits, zero implies no minimum size
* limit.
*/
public int getMinKeySize() {
return minSizeBits;
}
/**
* Returns the maximum key size. Use {@link #getMaxKeySize()} instead.
*
* @return The maximum key size in bits, zero implies no maximum size
* limit.
*/
@Deprecated
public int getMaxSize() {
return getMaxKeySize();
}
/**
* Returns the maximum key size.
*
* @return The maximum key size in bits, zero implies no maximum size
* limit.
*/
public int getMaxKeySize() {
return maxSizeBits;
}
/**
* Returns the key sizes.
*
* @return The key sizes in bits, {@code null} if not specified.
*/
public Set getKeySizes() {
return sizesBits;
}
/**
* Returns the curves to match (for EC keys).
*
* @return The curves, {@code null} if not specified.
*/
public Set getCurves() {
return curves;
}
/**
* Returns {@code true} if the specified JWK matches.
*
* @param key The JSON Web Key (JWK). Must not be {@code null}.
*
* @return {@code true} if the JWK matches, else {@code false}.
*/
public boolean matches(final JWK key) {
if (hasUse && key.getKeyUse() == null)
return false;
if (hasID && (key.getKeyID() == null || key.getKeyID().trim().isEmpty()))
return false;
if (privateOnly && ! key.isPrivate())
return false;
if (publicOnly && key.isPrivate())
return false;
if (types != null && ! types.contains(key.getKeyType()))
return false;
if (uses != null && ! uses.contains(key.getKeyUse()))
return false;
if (ops != null) {
if (ops.contains(null) && key.getKeyOperations() == null) {
// pass
} else if (key.getKeyOperations() != null && ops.containsAll(key.getKeyOperations())) {
// pass
} else {
return false;
}
}
if (algs != null && ! algs.contains(key.getAlgorithm()))
return false;
if (ids != null && ! ids.contains(key.getKeyID()))
return false;
if (minSizeBits > 0) {
if (key.size() < minSizeBits)
return false;
}
if (maxSizeBits > 0) {
if (key.size() > maxSizeBits)
return false;
}
if (sizesBits != null) {
if (! sizesBits.contains(key.size()))
return false;
}
if (curves != null) {
if (! (key instanceof ECKey))
return false;
ECKey ecKey = (ECKey)key;
if (! curves.contains(ecKey.getCurve()))
return false;
}
return true;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
append(sb, "kty", types);
append(sb, "use", uses);
append(sb, "key_ops", ops);
append(sb, "alg", algs);
append(sb, "kid", ids);
if (hasUse) {
sb.append("has_use=true ");
}
if (hasID) {
sb.append("has_id=true ");
}
if (privateOnly) {
sb.append("private_only=true ");
}
if (publicOnly) {
sb.append("public_only=true ");
}
if (minSizeBits > 0) {
sb.append("min_size=" + minSizeBits + " ");
}
if (maxSizeBits > 0) {
sb.append("max_size=" + maxSizeBits + " ");
}
append(sb, "size", sizesBits);
append(sb, "crv", curves);
return sb.toString().trim();
}
/**
* Appends the specified JWK matcher parameter to a string builder.
*
* @param sb The string builder. Must not be {@code null}.
* @param key The parameter key. Must not be {@code null}.
* @param values The parameter value, {@code null} if not specified.
*/
private static void append(final StringBuilder sb, final String key, final Set> values) {
if (values != null) {
sb.append(key);
sb.append('=');
if (values.size() == 1) {
Object value = values.iterator().next();
if (value == null) {
sb.append("ANY");
} else {
sb.append(value.toString().trim());
}
} else {
sb.append(values.toString().trim());
}
sb.append(' ');
}
}
}