001/*
002 * Copyright 2023 Emmanuel Bourg
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package net.jsign.jca;
018
019import java.io.IOException;
020
021/**
022 * AWS credentials
023 *
024 * @since 5.0
025 */
026public class AmazonCredentials {
027
028    private final String accessKey;
029    private final String secretKey;
030    private final String sessionToken;
031
032    public AmazonCredentials(String accessKey, String secretKey, String sessionToken) {
033        this.accessKey = accessKey;
034        this.secretKey = secretKey;
035        this.sessionToken = sessionToken;
036    }
037
038    public String getAccessKey() {
039        return accessKey;
040    }
041
042    public String getSecretKey() {
043        return secretKey;
044    }
045
046    public String getSessionToken() {
047        return sessionToken;
048    }
049
050    /**
051     * Parses the concatenated AWS credentials
052     *
053     * @param credentials <tt>accessKey|secretKey|sessionToken</tt> (the session token is optional)
054     */
055    public static AmazonCredentials parse(String credentials) throws IllegalArgumentException {
056        // parse the credentials
057        String[] elements = credentials.split("\\|", 3);
058        if (elements.length < 2) {
059            throw new IllegalArgumentException("Invalid AWS credentials: " + credentials);
060        }
061        String accessKey = elements[0];
062        String secretKey = elements[1];
063        String sessionToken = elements.length > 2 ? elements[2] : null;
064
065        return new AmazonCredentials(accessKey, secretKey, sessionToken);   
066    }
067
068    /**
069     * Returns the default AWS credentials, fetched from the following sources in order:
070     * <ul>
071     *   <li>The environment variables <tt>AWS_ACCESS_KEY_ID</tt> (or <tt>AWS_ACCESS_KEY</tt>),
072     *       <tt>AWS_SECRET_KEY</tt> (or <tt>AWS_SECRET_ACCESS_KEY</tt>) and <tt>AWS_SESSION_TOKEN</tt></li>
073     *   <li>The ECS container credentials service (ECS, EKS, Greengrass, Fargate)</li>
074     *   <li>The EC2 instance metadata service (IMDSv2)</li>
075     * </ul>
076     */
077    public static AmazonCredentials getDefault() throws IOException {
078        if (getenv("AWS_ACCESS_KEY_ID") != null || getenv("AWS_ACCESS_KEY") != null) {
079            String accessKey = getenv("AWS_ACCESS_KEY_ID");
080            if (accessKey == null) {
081                accessKey = getenv("AWS_ACCESS_KEY");
082            }
083            String secretKey = getenv("AWS_SECRET_KEY");
084            if (secretKey == null) {
085                secretKey = getenv("AWS_SECRET_ACCESS_KEY");
086            }
087            String sessionToken = getenv("AWS_SESSION_TOKEN");
088
089            return new AmazonCredentials(accessKey, secretKey, sessionToken);
090        } else {
091            try {
092                return new AmazonECSCredentialsClient().getCredentials();
093            } catch (IOException ecsException) {
094                try {
095                    return new AmazonIMDS2Client().getCredentials();
096                } catch (IOException imds2Exception) {
097                    ecsException.addSuppressed(imds2Exception);
098                    throw ecsException;
099                }
100            }
101        }
102    }
103
104    static String getenv(String name) {
105        return System.getenv(name);
106    }
107}