Fixing checkstyle issues.

Signed-off-by: Max Lambrecht <maxlambrecht@gmail.com>
This commit is contained in:
Max Lambrecht 2020-06-26 14:55:54 -03:00
parent 3e81bee7ff
commit 7268c54a28
13 changed files with 127 additions and 84 deletions

View File

@ -55,7 +55,7 @@ public enum Algorithm {
PS512("PS512"), PS512("PS512"),
/** /**
* Non-Supported algorithm * Non-Supported algorithm.
*/ */
OTHER("OTHER"); OTHER("OTHER");
@ -95,7 +95,7 @@ public enum Algorithm {
} }
public static Family parse(final String s) { public static Family parse(final String s) {
Family family; final Family family;
if (s.equals(RSA.getName())) { if (s.equals(RSA.getName())) {
family = RSA; family = RSA;
} else if (s.equals(EC.getName())) { } else if (s.equals(EC.getName())) {
@ -108,7 +108,7 @@ public enum Algorithm {
} }
public static Algorithm parse(final String s) { public static Algorithm parse(final String s) {
Algorithm algorithm; final Algorithm algorithm;
if (s.equals(RS256.getName())) { if (s.equals(RS256.getName())) {
algorithm = RS256; algorithm = RS256;
} else if (s.equals(RS384.getName())) { } else if (s.equals(RS384.getName())) {

View File

@ -67,12 +67,14 @@ public class JwtBundle implements BundleSource<JwtBundle> {
* @throws JwtBundleException if there is an error reading or parsing the file, or if a keyId is empty * @throws JwtBundleException if there is an error reading or parsing the file, or if a keyId is empty
* @throws KeyException if the bundle file contains a key type that is not supported * @throws KeyException if the bundle file contains a key type that is not supported
*/ */
public static JwtBundle load(@NonNull final TrustDomain trustDomain, @NonNull final Path bundlePath) throws KeyException, JwtBundleException { public static JwtBundle load(@NonNull final TrustDomain trustDomain, @NonNull final Path bundlePath)
throws KeyException, JwtBundleException {
try { try {
val jwkSet = JWKSet.load(bundlePath.toFile()); val jwkSet = JWKSet.load(bundlePath.toFile());
return toJwtBundle(trustDomain, jwkSet); return toJwtBundle(trustDomain, jwkSet);
} catch (IOException | ParseException | JOSEException e) { } catch (IOException | ParseException | JOSEException e) {
throw new JwtBundleException(String.format("Could not load bundle from file: %s", bundlePath.toString()), e); val error = "Could not load bundle from file: %s";
throw new JwtBundleException(String.format(error, bundlePath.toString()), e);
} }
} }
@ -127,7 +129,7 @@ public class JwtBundle implements BundleSource<JwtBundle> {
* @throws AuthorityNotFoundException if no Authority is found associated to the Key ID * @throws AuthorityNotFoundException if no Authority is found associated to the Key ID
*/ */
public PublicKey findJwtAuthority(final String keyId) throws AuthorityNotFoundException { public PublicKey findJwtAuthority(final String keyId) throws AuthorityNotFoundException {
PublicKey key = jwtAuthorities.get(keyId); val key = jwtAuthorities.get(keyId);
if (key != null) { if (key != null) {
return key; return key;
} }
@ -168,7 +170,7 @@ public class JwtBundle implements BundleSource<JwtBundle> {
} }
private static JwtBundle toJwtBundle(final TrustDomain trustDomain, final JWKSet jwkSet) throws JwtBundleException, JOSEException, ParseException, KeyException { private static JwtBundle toJwtBundle(final TrustDomain trustDomain, final JWKSet jwkSet) throws JwtBundleException, JOSEException, ParseException, KeyException {
Map<String, PublicKey> authorities = new ConcurrentHashMap<>(); final Map<String, PublicKey> authorities = new ConcurrentHashMap<>();
for (JWK jwk : jwkSet.getKeys()) { for (JWK jwk : jwkSet.getKeys()) {
String keyId = getKeyId(jwk); String keyId = getKeyId(jwk);
PublicKey publicKey = getPublicKey(jwk); PublicKey publicKey = getPublicKey(jwk);
@ -188,7 +190,7 @@ public class JwtBundle implements BundleSource<JwtBundle> {
private static PublicKey getPublicKey(final JWK jwk) throws JOSEException, ParseException, KeyException { private static PublicKey getPublicKey(final JWK jwk) throws JOSEException, ParseException, KeyException {
val family = Algorithm.Family.parse(jwk.getKeyType().getValue()); val family = Algorithm.Family.parse(jwk.getKeyType().getValue());
PublicKey publicKey; final PublicKey publicKey;
switch (family) { switch (family) {
case EC: case EC:
publicKey = ECKey.parse(jwk.toJSONString()).toPublicKey(); publicKey = ECKey.parse(jwk.toJSONString()).toPublicKey();

View File

@ -31,7 +31,7 @@ public class JwtBundleSet implements BundleSource<JwtBundle> {
* @return a {@link JwtBundleSet} * @return a {@link JwtBundleSet}
*/ */
public static JwtBundleSet of(@NonNull final Collection<JwtBundle> bundles) { public static JwtBundleSet of(@NonNull final Collection<JwtBundle> bundles) {
Map<TrustDomain, JwtBundle> bundleMap = new ConcurrentHashMap<>(); final Map<TrustDomain, JwtBundle> bundleMap = new ConcurrentHashMap<>();
for (JwtBundle bundle : bundles) { for (JwtBundle bundle : bundles) {
bundleMap.put(bundle.getTrustDomain(), bundle); bundleMap.put(bundle.getTrustDomain(), bundle);
} }

View File

@ -61,13 +61,16 @@ public class X509Bundle implements BundleSource<X509Bundle> {
* @throws IOException in case of failure accessing the given bundle path * @throws IOException in case of failure accessing the given bundle path
* @throws CertificateException if the bundle cannot be parsed * @throws CertificateException if the bundle cannot be parsed
*/ */
public static X509Bundle load(@NonNull final TrustDomain trustDomain, @NonNull final Path bundlePath) throws IOException, CertificateException { public static X509Bundle load(@NonNull final TrustDomain trustDomain, @NonNull final Path bundlePath)
byte[] bundleBytes; throws IOException, CertificateException {
final byte[] bundleBytes;
try { try {
bundleBytes = Files.readAllBytes(bundlePath); bundleBytes = Files.readAllBytes(bundlePath);
} catch (NoSuchFileException e) { } catch (NoSuchFileException e) {
throw new IOException("Unable to load X.509 bundle file", e); throw new IOException("Unable to load X.509 bundle file", e);
} }
val x509Certificates = CertificateUtils.generateCertificates(bundleBytes); val x509Certificates = CertificateUtils.generateCertificates(bundleBytes);
val x509CertificateSet = new HashSet<>(x509Certificates); val x509CertificateSet = new HashSet<>(x509Certificates);
return new X509Bundle(trustDomain, x509CertificateSet); return new X509Bundle(trustDomain, x509CertificateSet);
@ -84,7 +87,8 @@ public class X509Bundle implements BundleSource<X509Bundle> {
* *
* @throws CertificateException if the bundle cannot be parsed * @throws CertificateException if the bundle cannot be parsed
*/ */
public static X509Bundle parse(@NonNull final TrustDomain trustDomain, @NonNull final byte[] bundleBytes) throws CertificateException { public static X509Bundle parse(@NonNull final TrustDomain trustDomain, @NonNull final byte[] bundleBytes)
throws CertificateException {
val x509Certificates = CertificateUtils.generateCertificates(bundleBytes); val x509Certificates = CertificateUtils.generateCertificates(bundleBytes);
val x509CertificateSet = new HashSet<>(x509Certificates); val x509CertificateSet = new HashSet<>(x509Certificates);
return new X509Bundle(trustDomain, x509CertificateSet); return new X509Bundle(trustDomain, x509CertificateSet);

View File

@ -31,7 +31,7 @@ public class X509BundleSet implements BundleSource<X509Bundle> {
* @return a {@link X509BundleSet} initialized with the list of bundles * @return a {@link X509BundleSet} initialized with the list of bundles
*/ */
public static X509BundleSet of(@NonNull final Collection<X509Bundle> bundles) { public static X509BundleSet of(@NonNull final Collection<X509Bundle> bundles) {
Map<TrustDomain, X509Bundle> bundleMap = new ConcurrentHashMap<>(); final Map<TrustDomain, X509Bundle> bundleMap = new ConcurrentHashMap<>();
for (X509Bundle bundle : bundles) { for (X509Bundle bundle : bundles) {
bundleMap.put(bundle.getTrustDomain(), bundle); bundleMap.put(bundle.getTrustDomain(), bundle);
} }
@ -58,7 +58,7 @@ public class X509BundleSet implements BundleSource<X509Bundle> {
@Override @Override
public X509Bundle getBundleForTrustDomain(@NonNull final TrustDomain trustDomain) throws BundleNotFoundException { public X509Bundle getBundleForTrustDomain(@NonNull final TrustDomain trustDomain) throws BundleNotFoundException {
val bundle = bundles.get(trustDomain); val bundle = bundles.get(trustDomain);
if (bundle == null){ if (bundle == null) {
throw new BundleNotFoundException(String.format("No X.509 bundle for trust domain %s", trustDomain)); throw new BundleNotFoundException(String.format("No X.509 bundle for trust domain %s", trustDomain));
} }
return bundle; return bundle;

View File

@ -15,7 +15,7 @@ import java.util.stream.Collectors;
* @see <a href="https://github.com/spiffe/spiffe/blob/master/standards/SPIFFE-ID.md">https://github.com/spiffe/spiffe/blob/master/standards/SPIFFE-ID.md</a> * @see <a href="https://github.com/spiffe/spiffe/blob/master/standards/SPIFFE-ID.md">https://github.com/spiffe/spiffe/blob/master/standards/SPIFFE-ID.md</a>
*/ */
@Value @Value
public class SpiffeId { public final class SpiffeId {
public static final String SPIFFE_SCHEME = "spiffe"; public static final String SPIFFE_SCHEME = "spiffe";
@ -70,7 +70,8 @@ public class SpiffeId {
* Returns true if the trust domain of this SPIFFE ID is the same as trust domain given as parameter. * Returns true if the trust domain of this SPIFFE ID is the same as trust domain given as parameter.
* *
* @param trustDomain an instance of a {@link TrustDomain} * @param trustDomain an instance of a {@link TrustDomain}
* @return <code>true</code> if the given trust domain equals the trust domain of this object, <code>false</code> otherwise * @return <code>true</code> if the given trust domain equals the trust domain of this object,
* <code>false</code> otherwise
*/ */
public boolean memberOf(final TrustDomain trustDomain) { public boolean memberOf(final TrustDomain trustDomain) {
return this.trustDomain.equals(trustDomain); return this.trustDomain.equals(trustDomain);

View File

@ -9,17 +9,19 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isBlank;
/** /**
* Utility class with methods to read SPIFFE IDs using different mechanisms. * Utility class with methods to read SPIFFE IDs using different mechanisms.
*/ */
public class SpiffeIdUtils { public final class SpiffeIdUtils {
private static final char DEFAULT_CHAR_SEPARATOR = ','; private static final char DEFAULT_CHAR_SEPARATOR = ',';
private SpiffeIdUtils() {
}
/** /**
* Reads a file containing a list of SPIFFE IDs and parses them to {@link SpiffeId} instances. * Reads a file containing a list of SPIFFE IDs and parses them to {@link SpiffeId} instances.
* <p> * <p>
@ -31,7 +33,7 @@ public class SpiffeIdUtils {
* @throws IllegalArgumentException if any of the SPIFFE IDs in the file cannot be parsed * @throws IllegalArgumentException if any of the SPIFFE IDs in the file cannot be parsed
*/ */
public static Set<SpiffeId> getSpiffeIdSetFromFile(final Path spiffeIdsFile) throws IOException { public static Set<SpiffeId> getSpiffeIdSetFromFile(final Path spiffeIdsFile) throws IOException {
try (Stream<String> lines = Files.lines(spiffeIdsFile)) { try (val lines = Files.lines(spiffeIdsFile)) {
return lines return lines
.map(SpiffeId::parse) .map(SpiffeId::parse)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
@ -68,7 +70,4 @@ public class SpiffeIdUtils {
public static Set<SpiffeId> toSetOfSpiffeIds(final String spiffeIds) { public static Set<SpiffeId> toSetOfSpiffeIds(final String spiffeIds) {
return toSetOfSpiffeIds(spiffeIds, DEFAULT_CHAR_SEPARATOR); return toSetOfSpiffeIds(spiffeIds, DEFAULT_CHAR_SEPARATOR);
} }
private SpiffeIdUtils() {
}
} }

View File

@ -85,7 +85,7 @@ public class TrustDomain {
} }
} }
private static String normalize(String s) { private static String normalize(final String s) {
String result = s.toLowerCase().trim(); String result = s.toLowerCase().trim();
if (!result.contains("://")) { if (!result.contains("://")) {
result = SpiffeId.SPIFFE_SCHEME.concat("://").concat(result); result = SpiffeId.SPIFFE_SCHEME.concat("://").concat(result);

View File

@ -88,17 +88,22 @@ public class JwtSvid {
* The JWT-SVID signature is verified using the JWT bundle source. * The JWT-SVID signature is verified using the JWT bundle source.
* *
* @param token a token as a string that is parsed and validated * @param token a token as a string that is parsed and validated
* @param jwtBundleSource an implementation of a {@link BundleSource} that provides the JWT authorities to verify the signature * @param jwtBundleSource an implementation of a {@link BundleSource} that provides the JWT authorities to
* verify the signature
* @param audience audience as a list of strings used to validate the 'aud' claim * @param audience audience as a list of strings used to validate the 'aud' claim
* @return an instance of a {@link JwtSvid} with a SPIFFE ID parsed from the 'sub', audience from 'aud', and expiry * @return an instance of a {@link JwtSvid} with a SPIFFE ID parsed from the 'sub', audience from 'aud', and expiry
* from 'exp' claim. * from 'exp' claim.
* @throws JwtSvidException when the token expired or the expiration claim is missing, * @throws JwtSvidException when the token expired or the expiration claim is missing,
* when the algorithm is not supported, when the header 'kid' is missing, when the signature cannot be verified, or * when the algorithm is not supported, when the header 'kid' is missing,
* when the 'aud' claim has an audience that is not in the audience list provided as parameter * when the signature cannot be verified, or
* when the 'aud' claim has an audience that is not in the audience list
* provided as parameter
* @throws IllegalArgumentException when the token is blank or cannot be parsed * @throws IllegalArgumentException when the token is blank or cannot be parsed
* @throws BundleNotFoundException if the bundle for the trust domain of the spiffe id from the 'sub' cannot be found * @throws BundleNotFoundException if the bundle for the trust domain of the spiffe id from the 'sub'
* cannot be found
* in the JwtBundleSource * in the JwtBundleSource
* @throws AuthorityNotFoundException if the authority cannot be found in the bundle using the value from the 'kid' header * @throws AuthorityNotFoundException if the authority cannot be found in the bundle using the value from
* the 'kid' header
*/ */
public static JwtSvid parseAndValidate(@NonNull final String token, public static JwtSvid parseAndValidate(@NonNull final String token,
@NonNull final BundleSource<JwtBundle> jwtBundleSource, @NonNull final BundleSource<JwtBundle> jwtBundleSource,
@ -230,7 +235,7 @@ public class JwtSvid {
private static JWSVerifier getJwsVerifier(final PublicKey jwtAuthority, final String algorithm) throws JOSEException, JwtSvidException { private static JWSVerifier getJwsVerifier(final PublicKey jwtAuthority, final String algorithm) throws JOSEException, JwtSvidException {
JWSVerifier verifier; JWSVerifier verifier;
final Algorithm alg = Algorithm.parse(algorithm); val alg = Algorithm.parse(algorithm);
if (Algorithm.Family.EC.contains(alg)) { if (Algorithm.Family.EC.contains(alg)) {
verifier = new ECDSAVerifier((ECPublicKey) jwtAuthority); verifier = new ECDSAVerifier((ECPublicKey) jwtAuthority);
} else if (Algorithm.Family.RSA.contains(alg)) { } else if (Algorithm.Family.RSA.contains(alg)) {

View File

@ -1,12 +1,13 @@
package io.spiffe.svid.x509svid; package io.spiffe.svid.x509svid;
import io.spiffe.exception.X509SvidException; import io.spiffe.exception.X509SvidException;
import io.spiffe.internal.AsymmetricKeyAlgorithm;
import io.spiffe.internal.CertificateUtils; import io.spiffe.internal.CertificateUtils;
import io.spiffe.internal.KeyFileFormat; import io.spiffe.internal.KeyFileFormat;
import io.spiffe.internal.AsymmetricKeyAlgorithm;
import io.spiffe.spiffeid.SpiffeId; import io.spiffe.spiffeid.SpiffeId;
import lombok.NonNull; import lombok.NonNull;
import lombok.Value; import lombok.Value;
import lombok.val;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
@ -73,15 +74,16 @@ public class X509Svid {
* @return an instance of {@link X509Svid} * @return an instance of {@link X509Svid}
* @throws X509SvidException if there is an error parsing the given certsFilePath or the privateKeyFilePath * @throws X509SvidException if there is an error parsing the given certsFilePath or the privateKeyFilePath
*/ */
public static X509Svid load(@NonNull final Path certsFilePath, @NonNull final Path privateKeyFilePath) throws X509SvidException { public static X509Svid load(@NonNull final Path certsFilePath, @NonNull final Path privateKeyFilePath)
byte[] certsBytes; throws X509SvidException {
final byte[] certsBytes;
try { try {
certsBytes = Files.readAllBytes(certsFilePath); certsBytes = Files.readAllBytes(certsFilePath);
} catch (IOException e) { } catch (IOException e) {
throw new X509SvidException("Cannot read certificate file", e); throw new X509SvidException("Cannot read certificate file", e);
} }
byte[] privateKeyBytes; final byte[] privateKeyBytes;
try { try {
privateKeyBytes = Files.readAllBytes(privateKeyFilePath); privateKeyBytes = Files.readAllBytes(privateKeyFilePath);
} catch (IOException e) { } catch (IOException e) {
@ -101,7 +103,8 @@ public class X509Svid {
* @return a {@link X509Svid} parsed from the given certBytes and privateKeyBytes * @return a {@link X509Svid} parsed from the given certBytes and privateKeyBytes
* @throws X509SvidException if the given certsBytes or privateKeyBytes cannot be parsed * @throws X509SvidException if the given certsBytes or privateKeyBytes cannot be parsed
*/ */
public static X509Svid parse(@NonNull final byte[] certsBytes, @NonNull final byte[] privateKeyBytes) throws X509SvidException { public static X509Svid parse(@NonNull final byte[] certsBytes, @NonNull final byte[] privateKeyBytes)
throws X509SvidException {
return createX509Svid(certsBytes, privateKeyBytes, KeyFileFormat.PEM); return createX509Svid(certsBytes, privateKeyBytes, KeyFileFormat.PEM);
} }
@ -116,7 +119,8 @@ public class X509Svid {
* @return a {@link X509Svid} parsed from the given certBytes and privateKeyBytes * @return a {@link X509Svid} parsed from the given certBytes and privateKeyBytes
* @throws X509SvidException if the given certsBytes or privateKeyBytes cannot be parsed * @throws X509SvidException if the given certsBytes or privateKeyBytes cannot be parsed
*/ */
public static X509Svid parseRaw(@NonNull final byte[] certsBytes, @NonNull final byte[] privateKeyBytes) throws X509SvidException { public static X509Svid parseRaw(@NonNull final byte[] certsBytes,
@NonNull final byte[] privateKeyBytes) throws X509SvidException {
return createX509Svid(certsBytes, privateKeyBytes, KeyFileFormat.DER); return createX509Svid(certsBytes, privateKeyBytes, KeyFileFormat.DER);
} }
@ -127,10 +131,13 @@ public class X509Svid {
return chain.toArray(new X509Certificate[0]); return chain.toArray(new X509Certificate[0]);
} }
private static X509Svid createX509Svid(final byte[] certsBytes, final byte[] privateKeyBytes, KeyFileFormat keyFileFormat) throws X509SvidException { private static X509Svid createX509Svid(final byte[] certsBytes,
List<X509Certificate> x509Certificates = generateX509Certificates(certsBytes); final byte[] privateKeyBytes,
PrivateKey privateKey = generatePrivateKey(privateKeyBytes, keyFileFormat, x509Certificates); final KeyFileFormat keyFileFormat) throws X509SvidException {
SpiffeId spiffeId = getSpiffeId(x509Certificates);
val x509Certificates = generateX509Certificates(certsBytes);
val privateKey = generatePrivateKey(privateKeyBytes, keyFileFormat, x509Certificates);
val spiffeId = getSpiffeId(x509Certificates);
validatePrivateKey(privateKey, x509Certificates); validatePrivateKey(privateKey, x509Certificates);
validateLeafCertificate(x509Certificates.get(0)); validateLeafCertificate(x509Certificates.get(0));
@ -144,7 +151,7 @@ public class X509Svid {
} }
private static SpiffeId getSpiffeId(final List<X509Certificate> x509Certificates) throws X509SvidException { private static SpiffeId getSpiffeId(final List<X509Certificate> x509Certificates) throws X509SvidException {
SpiffeId spiffeId; final SpiffeId spiffeId;
try { try {
spiffeId = CertificateUtils.getSpiffeId(x509Certificates.get(0)); spiffeId = CertificateUtils.getSpiffeId(x509Certificates.get(0));
} catch (CertificateException e) { } catch (CertificateException e) {
@ -153,9 +160,14 @@ public class X509Svid {
return spiffeId; return spiffeId;
} }
private static PrivateKey generatePrivateKey(final byte[] privateKeyBytes, final KeyFileFormat keyFileFormat, final List<X509Certificate> x509Certificates) throws X509SvidException { private static PrivateKey generatePrivateKey(final byte[] privateKeyBytes,
AsymmetricKeyAlgorithm algorithm = AsymmetricKeyAlgorithm.parse(x509Certificates.get(0).getPublicKey().getAlgorithm()); final KeyFileFormat keyFileFormat,
PrivateKey privateKey; final List<X509Certificate> x509Certificates)
throws X509SvidException {
val publicKeyCertAlgorithm = x509Certificates.get(0).getPublicKey().getAlgorithm();
val algorithm = AsymmetricKeyAlgorithm.parse(publicKeyCertAlgorithm);
final PrivateKey privateKey;
try { try {
privateKey = CertificateUtils.generatePrivateKey(privateKeyBytes, algorithm, keyFileFormat); privateKey = CertificateUtils.generatePrivateKey(privateKeyBytes, algorithm, keyFileFormat);
} catch (InvalidKeySpecException | InvalidKeyException | NoSuchAlgorithmException e) { } catch (InvalidKeySpecException | InvalidKeyException | NoSuchAlgorithmException e) {
@ -208,7 +220,8 @@ public class X509Svid {
} }
} }
private static void validatePrivateKey(final PrivateKey privateKey, final List<X509Certificate> x509Certificates) throws X509SvidException { private static void validatePrivateKey(final PrivateKey privateKey, final List<X509Certificate> x509Certificates)
throws X509SvidException {
try { try {
CertificateUtils.validatePrivateKey(privateKey, x509Certificates.get(0)); CertificateUtils.validatePrivateKey(privateKey, x509Certificates.get(0));
} catch (InvalidKeyException e) { } catch (InvalidKeyException e) {

View File

@ -19,16 +19,19 @@ import java.util.function.Supplier;
/** /**
* Provides methods to validate a chain of X.509 certificates using an X.509 bundle source. * Provides methods to validate a chain of X.509 certificates using an X.509 bundle source.
*/ */
public class X509SvidValidator { public final class X509SvidValidator {
private X509SvidValidator() {
}
/** /**
* Verifies that a chain of certificates can be chained to one authority in the given X.509 bundle source. * Verifies that a chain of certificates can be chained to one authority in the given X.509 bundle source.
* *
* @param chain a list representing the chain of X.509 certificates to be validated * @param chain a list representing the chain of X.509 certificates to be validated
* @param x509BundleSource a {@link BundleSource } to provide the authorities * @param x509BundleSource a {@link BundleSource } to provide the authorities
* @throws CertificateException is the chain cannot be verified with an authority from the X.509 bundle source * @throws CertificateException is the chain cannot be verified with an authority from the X.509 bundle source
* @throws BundleNotFoundException if no X.509 bundle for the trust domain could be found in the X.509 bundle source * @throws BundleNotFoundException if no X.509 bundle for the trust domain could be found in the X.509 bundle source
* @throws NullPointerException if the given chain or 509BundleSource are null * @throws NullPointerException if the given chain or 509BundleSource are null
*/ */
public static void verifyChain( public static void verifyChain(
@NonNull final List<X509Certificate> chain, @NonNull final List<X509Certificate> chain,
@ -48,10 +51,11 @@ public class X509SvidValidator {
/** /**
* Checks that the X.509 SVID provided has a SPIFFE ID that is in the Set of accepted SPIFFE IDs supplied. * Checks that the X.509 SVID provided has a SPIFFE ID that is in the Set of accepted SPIFFE IDs supplied.
* *
* @param x509Certificate a {@link X509Svid} with a SPIFFE ID to be verified * @param x509Certificate a {@link X509Svid} with a SPIFFE ID to be verified
* @param acceptedSpiffeIdsSupplier a {@link Supplier} of a Set of SPIFFE IDs that are accepted * @param acceptedSpiffeIdsSupplier a {@link Supplier} of a Set of SPIFFE IDs that are accepted
* @throws CertificateException if the SPIFFE ID in x509Certificate is not in the Set supplied by acceptedSpiffeIdsSupplier, * @throws CertificateException if the SPIFFE ID in x509Certificate is not in the Set supplied by
* or if the SPIFFE ID cannot be parsed from the x509Certificate * acceptedSpiffeIdsSupplier, or if the SPIFFE ID cannot be parsed from the
* x509Certificate
* @throws NullPointerException if the given x509Certificate or acceptedSpiffeIdsSupplier are null * @throws NullPointerException if the given x509Certificate or acceptedSpiffeIdsSupplier are null
*/ */
public static void verifySpiffeId(@NonNull final X509Certificate x509Certificate, public static void verifySpiffeId(@NonNull final X509Certificate x509Certificate,
@ -60,10 +64,8 @@ public class X509SvidValidator {
val spiffeIdSet = acceptedSpiffeIdsSupplier.get(); val spiffeIdSet = acceptedSpiffeIdsSupplier.get();
val spiffeId = CertificateUtils.getSpiffeId(x509Certificate); val spiffeId = CertificateUtils.getSpiffeId(x509Certificate);
if (!spiffeIdSet.contains(spiffeId)) { if (!spiffeIdSet.contains(spiffeId)) {
throw new CertificateException(String.format("SPIFFE ID %s in X.509 certificate is not accepted", spiffeId)); final String error = "SPIFFE ID %s in X.509 certificate is not accepted";
throw new CertificateException(String.format(error, spiffeId));
} }
} }
private X509SvidValidator() {
}
} }

View File

@ -24,6 +24,9 @@ public class Address {
private static final String TCP_SCHEME = "tcp"; private static final String TCP_SCHEME = "tcp";
private static final Set<String> VALID_SCHEMES = Sets.newHashSet(UNIX_SCHEME, TCP_SCHEME); private static final Set<String> VALID_SCHEMES = Sets.newHashSet(UNIX_SCHEME, TCP_SCHEME);
private Address() {
}
/** /**
* @return the default Workload API address hold by the system environment variable * @return the default Workload API address hold by the system environment variable
* defined by SOCKET_ENV_VARIABLE. * defined by SOCKET_ENV_VARIABLE.
@ -43,7 +46,8 @@ public class Address {
* The scheme and path components are mandatory, and no other component may be set. * The scheme and path components are mandatory, and no other component may be set.
* <p> * <p>
* If the scheme is set to tcp, then the host component of the authority MUST be set to an IP address, * If the scheme is set to tcp, then the host component of the authority MUST be set to an IP address,
* and the port component of the authority MUST be set to the TCP port number of the SPIFFE Workload Endpoint TCP listen socket. * and the port component of the authority MUST be set to the TCP port number of the
* SPIFFE Workload Endpoint TCP listen socket.
* The scheme, host, and port components are mandatory, and no other component may be set. * The scheme, host, and port components are mandatory, and no other component may be set.
* As an example, tcp://127.0.0.1:8000 is valid, and tcp://127.0.0.1:8000/foo is not. * As an example, tcp://127.0.0.1:8000 is valid, and tcp://127.0.0.1:8000/foo is not.
* *
@ -58,7 +62,8 @@ public class Address {
val parsedAddress = parseUri(address); val parsedAddress = parseUri(address);
val scheme = parsedAddress.getScheme(); val scheme = parsedAddress.getScheme();
if (isSchemeNotValid(scheme)) { if (isSchemeNotValid(scheme)) {
throw new SocketEndpointAddressException(String.format("Workload endpoint socket URI must have a tcp:// or unix:// scheme: %s", address)); val error = "Workload endpoint socket URI must have a tcp:// or unix:// scheme: %s";
throw new SocketEndpointAddressException(String.format(error, address));
} }
if (UNIX_SCHEME.equals(scheme)) { if (UNIX_SCHEME.equals(scheme)) {
@ -75,77 +80,88 @@ public class Address {
try { try {
parsedAddress = new URI(address); parsedAddress = new URI(address);
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
throw new SocketEndpointAddressException(String.format("Workload endpoint socket is not a valid URI: %s", address), e); val error = "Workload endpoint socket is not a valid URI: %s";
throw new SocketEndpointAddressException(String.format(error, address), e);
} }
return parsedAddress; return parsedAddress;
} }
private static void validateUnixAddress(final URI parsedAddress) throws SocketEndpointAddressException { private static void validateUnixAddress(final URI parsedAddress) throws SocketEndpointAddressException {
if (parsedAddress.isOpaque()) { if (parsedAddress.isOpaque()) {
throw new SocketEndpointAddressException(String.format("Workload endpoint unix socket URI must not be opaque: %s", parsedAddress)); val error = "Workload endpoint unix socket URI must not be opaque: %s";
throw new SocketEndpointAddressException(String.format(error, parsedAddress));
} }
if (StringUtils.isNotBlank(parsedAddress.getRawAuthority())) { if (StringUtils.isNotBlank(parsedAddress.getRawAuthority())) {
throw new SocketEndpointAddressException(String.format("Workload endpoint unix socket URI must not include authority component: %s", parsedAddress)); val error = "Workload endpoint unix socket URI must not include authority component: %s";
throw new SocketEndpointAddressException(String.format(error, parsedAddress));
} }
if (hasEmptyPath(parsedAddress.getPath())) { if (hasEmptyPath(parsedAddress.getPath())) {
throw new SocketEndpointAddressException(String.format("Workload endpoint unix socket path cannot be blank: %s", parsedAddress)); val error = "Workload endpoint unix socket path cannot be blank: %s";
throw new SocketEndpointAddressException(String.format(error, parsedAddress));
} }
if (StringUtils.isNotBlank(parsedAddress.getRawQuery())) { if (StringUtils.isNotBlank(parsedAddress.getRawQuery())) {
throw new SocketEndpointAddressException(String.format("Workload endpoint unix socket URI must not include query values: %s", parsedAddress)); val error = "Workload endpoint unix socket URI must not include query values: %s";
throw new SocketEndpointAddressException(String.format(error, parsedAddress));
} }
if (StringUtils.isNotBlank(parsedAddress.getFragment())) { if (StringUtils.isNotBlank(parsedAddress.getFragment())) {
throw new SocketEndpointAddressException(String.format("Workload endpoint unix socket URI must not include a fragment: %s", parsedAddress)); val error = "Workload endpoint unix socket URI must not include a fragment: %s";
throw new SocketEndpointAddressException(String.format(error, parsedAddress));
} }
} }
private static void validateTcpAddress(final URI parsedAddress) throws SocketEndpointAddressException { private static void validateTcpAddress(final URI parsedAddress) throws SocketEndpointAddressException {
if (parsedAddress.isOpaque()) { if (parsedAddress.isOpaque()) {
throw new SocketEndpointAddressException(String.format("Workload endpoint tcp socket URI must not be opaque: %s", parsedAddress)); val error = "Workload endpoint tcp socket URI must not be opaque: %s";
throw new SocketEndpointAddressException(String.format(error, parsedAddress));
} }
if (StringUtils.isNotBlank(parsedAddress.getUserInfo())) { if (StringUtils.isNotBlank(parsedAddress.getUserInfo())) {
throw new SocketEndpointAddressException(String.format("Workload endpoint tcp socket URI must not include user info: %s", parsedAddress)); val error = "Workload endpoint tcp socket URI must not include user info: %s";
throw new SocketEndpointAddressException(String.format(error, parsedAddress));
} }
if (StringUtils.isBlank(parsedAddress.getHost())) { if (StringUtils.isBlank(parsedAddress.getHost())) {
throw new SocketEndpointAddressException(String.format("Workload endpoint tcp socket URI must include a host: %s", parsedAddress)); final String error = "Workload endpoint tcp socket URI must include a host: %s";
throw new SocketEndpointAddressException(String.format(error, parsedAddress));
} }
if (StringUtils.isNotBlank(parsedAddress.getPath())) { if (StringUtils.isNotBlank(parsedAddress.getPath())) {
throw new SocketEndpointAddressException(String.format("Workload endpoint tcp socket URI must not include a path: %s", parsedAddress)); val error = "Workload endpoint tcp socket URI must not include a path: %s";
throw new SocketEndpointAddressException(String.format(error, parsedAddress));
} }
if (StringUtils.isNotBlank(parsedAddress.getRawQuery())) { if (StringUtils.isNotBlank(parsedAddress.getRawQuery())) {
throw new SocketEndpointAddressException(String.format("Workload endpoint tcp socket URI must not include query values: %s", parsedAddress)); val error = "Workload endpoint tcp socket URI must not include query values: %s";
throw new SocketEndpointAddressException(String.format(error, parsedAddress));
} }
if (StringUtils.isNotBlank(parsedAddress.getFragment())) { if (StringUtils.isNotBlank(parsedAddress.getFragment())) {
throw new SocketEndpointAddressException(String.format("Workload endpoint tcp socket URI must not include a fragment: %s", parsedAddress)); val error = "Workload endpoint tcp socket URI must not include a fragment: %s";
throw new SocketEndpointAddressException(String.format(error, parsedAddress));
} }
val ipValid = InetAddressValidator.getInstance().isValid(parsedAddress.getHost()); val ipValid = InetAddressValidator.getInstance().isValid(parsedAddress.getHost());
if (!ipValid) { if (!ipValid) {
throw new SocketEndpointAddressException(String.format("Workload endpoint tcp socket URI host component must be an IP:port: %s", parsedAddress)); val error = "Workload endpoint tcp socket URI host component must be an IP:port: %s";
throw new SocketEndpointAddressException(String.format(error, parsedAddress));
} }
int port = parsedAddress.getPort(); int port = parsedAddress.getPort();
if (port == -1) { if (port == -1) {
throw new SocketEndpointAddressException(String.format("Workload endpoint tcp socket URI host component must include a port: %s", parsedAddress)); final String error = "Workload endpoint tcp socket URI host component must include a port: %s";
throw new SocketEndpointAddressException(String.format(error, parsedAddress));
} }
} }
private static boolean hasEmptyPath(final String path) { private static boolean hasEmptyPath(final String path) {
return StringUtils.isBlank(path) || path.equals("/"); return StringUtils.isBlank(path) || "/".equals(path);
} }
private static boolean isSchemeNotValid(final String scheme) { private static boolean isSchemeNotValid(final String scheme) {
return !VALID_SCHEMES.contains(scheme); return !VALID_SCHEMES.contains(scheme);
} }
private Address() {
}
} }

View File

@ -22,31 +22,32 @@ import java.util.Map;
/** /**
* Utility methods for converting GRPC objects to JAVA-SPIFFE domain objects. * Utility methods for converting GRPC objects to JAVA-SPIFFE domain objects.
*/ */
class GrpcConversionUtils { final class GrpcConversionUtils {
private GrpcConversionUtils() {} private GrpcConversionUtils() {
}
static X509Context toX509Context(final Workload.X509SVIDResponse x509SVIDResponse) static X509Context toX509Context(final Workload.X509SVIDResponse x509SvidResponse)
throws CertificateException, X509SvidException { throws CertificateException, X509SvidException {
val x509SvidList = getListOfX509Svid(x509SVIDResponse); val x509SvidList = getListOfX509Svid(x509SvidResponse);
val x509BundleList = getListOfX509Bundles(x509SVIDResponse); val x509BundleList = getListOfX509Bundles(x509SvidResponse);
val bundleSet = X509BundleSet.of(x509BundleList); val bundleSet = X509BundleSet.of(x509BundleList);
return new X509Context(x509SvidList, bundleSet); return new X509Context(x509SvidList, bundleSet);
} }
static List<X509Bundle> getListOfX509Bundles(final Workload.X509SVIDResponse x509SVIDResponse) static List<X509Bundle> getListOfX509Bundles(final Workload.X509SVIDResponse x509SvidResponse)
throws CertificateException { throws CertificateException {
final List<X509Bundle> x509BundleList = new ArrayList<>(); final List<X509Bundle> x509BundleList = new ArrayList<>();
for (Workload.X509SVID x509Svid : x509SVIDResponse.getSvidsList()) { for (Workload.X509SVID x509Svid : x509SvidResponse.getSvidsList()) {
val spiffeId = SpiffeId.parse(x509Svid.getSpiffeId()); val spiffeId = SpiffeId.parse(x509Svid.getSpiffeId());
val bundle = X509Bundle.parse(spiffeId.getTrustDomain(), x509Svid.getBundle().toByteArray()); val bundle = X509Bundle.parse(spiffeId.getTrustDomain(), x509Svid.getBundle().toByteArray());
x509BundleList.add(bundle); x509BundleList.add(bundle);
} }
// Process federated bundles // Process federated bundles
for (Map.Entry<String, ByteString> bundleEntry : x509SVIDResponse.getFederatedBundlesMap().entrySet()) { for (Map.Entry<String, ByteString> bundleEntry : x509SvidResponse.getFederatedBundlesMap().entrySet()) {
val bundle = X509Bundle.parse(TrustDomain.of(bundleEntry.getKey()), bundleEntry.getValue().toByteArray()); val bundle = X509Bundle.parse(TrustDomain.of(bundleEntry.getKey()), bundleEntry.getValue().toByteArray());
x509BundleList.add(bundle); x509BundleList.add(bundle);
} }
@ -54,12 +55,12 @@ class GrpcConversionUtils {
return x509BundleList; return x509BundleList;
} }
private static List<X509Svid> getListOfX509Svid(final Workload.X509SVIDResponse x509SVIDResponse) private static List<X509Svid> getListOfX509Svid(final Workload.X509SVIDResponse x509SvidResponse)
throws X509SvidException { throws X509SvidException {
final List<X509Svid> x509SvidList = new ArrayList<>(); final List<X509Svid> x509SvidList = new ArrayList<>();
for (Workload.X509SVID x509SVID : x509SVIDResponse.getSvidsList()) { for (Workload.X509SVID x509SVID : x509SvidResponse.getSvidsList()) {
val svid = X509Svid.parseRaw(x509SVID.getX509Svid().toByteArray(), x509SVID.getX509SvidKey().toByteArray()); val svid = X509Svid.parseRaw(x509SVID.getX509Svid().toByteArray(), x509SVID.getX509SvidKey().toByteArray());
x509SvidList.add(svid); x509SvidList.add(svid);