com.authlete.common.conf.AuthletePropertiesConfiguration Maven / Gradle / Ivy
Show all versions of authlete-java-common Show documentation
/*
* Copyright (C) 2014-2022 Authlete, Inc.
*
* 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.authlete.common.conf;
import java.util.logging.Logger;
import com.authlete.common.util.PropertiesLoader;
import com.authlete.common.util.TypedProperties;
import com.neovisionaries.security.AESCipher;
/**
* Implementation of {@link AuthleteConfiguration} based on
* a properties file.
*
*
* This is a utility class to load a configuration file that includes
* properties related to Authlete. Below is the list of configuration
* properties.
*
*
*
*
* - {@code base_url}
* -
* The base URL of Authlete Web API. The default value is
* {@code "https://api.authlete.com"}.
*
*
*
* - {@code service_owner.api_key}
* -
* The service owner API key issued by Authlete.
*
*
*
* - {@code service_owner.api_secret.encrypted}
* -
* The service owner API secret issued by Authlete, encrypted by
* {@code "AES/CBC/PKCS5Padding"} and encoded in Base64.
* The secret key and the initial vector of the encryption
* have to be passed to the constructor of this class.
*
*
*
* - {@code service_owner.api_secret}
* -
* The service owner API secret issued by Authlete. The value
* of this configuration property is referred to only when
* {@code service_owner.api_secret.encrypted} is not found in
* the configuration file.
*
*
*
* - {@code service.api_key}
* -
* The service API key issued by Authlete.
*
*
*
* - {@code service.api_secret.encrypted}
* -
* The service API secret issued by Authlete, encrypted by
* {@code "AES/CBC/PKCS5Padding"} and encoded in Base64.
* The secret key and the initial vector of the encryption
* have to be passed to the constructor of this class.
*
*
*
* - {@code service.api_secret}
* -
* The service API secret issued by Authlete. The value of
* of this configuration property is referred to only when
* {@code service.api_secret.encrypted} is not found in the
* configuration file.
*
*
*
*
*
* The value of {@code service_owner.api_secret.encrypted} can be
* generated using {@code openssl} command like the following.
*
*
*
* echo -n "{Service-Owner-API-Secret}" | openssl aes-128-cbc -e -a \
* -K "{Your-Secret-Key-in-Hex}" -iv "{Your-Initial-Vector-in-Hex}"
*
*
* "{Service-Owner-API-Secret}" is the service owner API secret
* issued by Authlete. Values of "{Your-Secret-Key-in-Hex}" and
* "{Your-Initial-Vector-in-Hex}" are 32-letter hex strings which
* you can determine. The following is an example to generate a random
* 32-letter hex string.
*
*
*
* ruby -e 'puts Random.new.bytes(16).unpack("H*")'
*
*
* Likewise, the value of {@code service.api_secret.encrypted} can be
* generated by {@code openssl}, too.
*
*
*
* If you encrypt your service owner API secret and service API secret
* as shown below:
*
*
*
* // Encrypt service owner API secret.
* $ echo -n "AF4Sms0cqs3HsTNlVrPbnWz5AXi3GtmMcveOklYKVCc" | openssl aes-128-cbc -e -a \
* -K a281ac2de1195e8c91ea383d38d05d1c -iv b6f5d0f0dd7146b0e3915ebd2dd078f3
* sKzcMU98a8xA5lwR23Crfkyu23klZnTuQlWApyllARpHFv84IItoZFZXj70OCrnF
*
* // Encrypt service API secret.
* $ echo -n "9La-ZhyyKK6sV6zsteNmcoTizHmC0NEVTFT9FUrIaYs" | openssl aes-128-cbc -e -a \
* -K a281ac2de1195e8c91ea383d38d05d1c -iv b6f5d0f0dd7146b0e3915ebd2dd078f3
* ERxV45wkpjJWXs+Mg9m6UyGHHGzBG5/2ytX0j0x3qNPuz5oWyciqkWjkBznLTWxb
*
*
* The configuration file will look like the following.
*
*
*
* base_url = https://evaluation-dot-authlete.appspot.com
* service_owner.api_key = etKXFbM0VumfC5j1XD6qGOk3yhHmsdqOILBFFIkDfmw
* service_owner.api_secret.encrypted = sKzcMU98a8xA5lwR23Crfkyu23klZnTuQlWApyllARpHFv84IItoZFZXj70OCrnF
* service.api_key = KNiA4bWqj2Ht0CJTqr4DTBgTIXeCskCHQ_vONBeth6M
* service.api_secret.encrypted = ERxV45wkpjJWXs+Mg9m6UyGHHGzBG5/2ytX0j0x3qNPuz5oWyciqkWjkBznLTWxb
*
*
* And to load the configuration file, an {@code AuthletePropertiesConfiguration}
* instance needs to be constructed as follows:
*
*
*
* String key = "a281ac2de1195e8c91ea383d38d05d1c";
* String iv = "b6f5d0f0dd7146b0e3915ebd2dd078f3";
*
* {@link AuthleteConfiguration} conf = new {@link #AuthletePropertiesConfiguration(String, String)
* AuthletePropertiesConfiguration(key, iv)};
*
*
* Constructors without {@code file} parameter use {@code "authlete.properties"}
* as the name of the configuration file and search the file system and then
* the classpath for the file.
*
*/
public class AuthletePropertiesConfiguration implements AuthleteConfiguration
{
/**
* The default value of the secret key to decode encrypted property values
* ({@code a281ac2de1195e8c91ea383d38d05d1c}).
*
* @since 1.24
*/
public static final String DEFAULT_KEY = "a281ac2de1195e8c91ea383d38d05d1c";
/**
* The default value of the initial vector to decode encrypted property values
* ({@code b6f5d0f0dd7146b0e3915ebd2dd078f3}).
*
* @since 1.24
*/
public static final String DEFAULT_IV = "b6f5d0f0dd7146b0e3915ebd2dd078f3";
/**
* The default value of the name of the configuration file
* ({@code authlete.properties}).
*
* @since 1.24
*/
public static final String DEFAULT_FILE = "authlete.properties";
/**
* The system property key to specify the name of an Authlete
* configuration file ({@code authlete.configuration.file}).
* When this system property has a value, it is used as the name
* of the configuration file. Otherwise, the default file
* ({@code authlete.properties}) is used.
*
* @since 1.29
*/
public static final String SYSTEM_PROPERTY_AUTHLETE_CONFIGURATION_FILE =
"authlete.configuration.file";
/**
* Property key to specify the base URL ({@code base_url}).
*/
private static final String PROPERTY_KEY_BASE_URL = "base_url";
/**
* Property key to specify the service owner API key
* ({@code service_owner.api_key}).
*/
private static final String PROPERTY_KEY_SERVICE_OWNER_API_KEY = "service_owner.api_key";
/**
* Property key to specify the encrypted service owner API secret
* ({@code service_owner.api_secret.encrypted}).
*/
private static final String PROPERTY_KEY_SERVICE_OWNER_API_SECRET_ENCRYPTED = "service_owner.api_secret.encrypted";
/**
* Property key to specify the service owner API secret
* ({@code service_owner.api_secret}).
*/
private static final String PROPERTY_KEY_SERVICE_OWNER_API_SECRET = "service_owner.api_secret";
/**
* Property key to specify the service owner access token
* ({@code service_owner.access_token}).
*/
private static final String PROPERTY_KEY_SERVICE_OWNER_ACCESS_TOKEN = "service_owner.access_token";
/**
* Property key to specify the service API key
* ({@code service.api_key}).
*/
private static final String PROPERTY_KEY_SERVICE_API_KEY = "service.api_key";
/**
* Property key to specify the encrypted service API secret
* ({@code service.api_secret.encrypted}).
*/
private static final String PROPERTY_KEY_SERVICE_API_SECRET_ENCRYPTED = "service.api_secret.encrypted";
/**
* Property key to specify the service API secret
* ({@code service.api_secret}).
*/
private static final String PROPERTY_KEY_SERVICE_API_SECRET = "service.api_secret";
/**
* Property key to specify the service access token
* ({@code service.access_token}).
*/
private static final String PROPERTY_KEY_SERVICE_ACCESS_TOKEN = "service.access_token";
/**
* Property key to specify the dpop signing key
* ({@code service.dpop_key}).
*/
private static final String PROPERTY_KEY_DPOP_KEY = "service.dpop_key";
/**
* Property key to specify the client's MTLS certificate
* ({@code service.client_certificate}).
*/
private static final String PROPERTY_KEY_CLIENT_CERTIFICATE = "service.client_certificate";
/**
* Property key to specify the API version.
* ({@code api_version}).
*/
private static final String PROPERTY_KEY_API_VERSION = "api_version";
/**
* The default value of the base URL ({@code https://api.authlete.com}).
*/
private static final String BASE_URL_DEFAULT = "https://api.authlete.com";
/**
* Base URL.
*/
private String mBaseUrl;
/**
* Service owner API key.
*/
private String mServiceOwnerApiKey;
/**
* Service owner API secret.
*/
private String mServiceOwnerApiSecret;
/**
* Service owner access token.
*/
private String mServiceOwnerAccessToken;
/**
* Service API key.
*/
private String mServiceApiKey;
/**
* Service API secret.
*/
private String mServiceApiSecret;
/**
* Service access token.
*/
private String mServiceAccessToken;
/**
* DPoP Signing key pair
*/
private String mDpopKey;
/**
* Client MTLS certificate.
*/
private String mClientCertificate;
/**
* Supported API Version.
*/
private String mApiVersion;
/**
* Constructor with a pair of secret key and initial vector to decode
* encrypted property values.
*
*
* This constructor is an alias of {@link #AuthletePropertiesConfiguration(
* String, String, String) this}(file, key, iv)
where
* file
is either {@link #DEFAULT_FILE
* authlete.properties} or the value of the system property {@link
* #SYSTEM_PROPERTY_AUTHLETE_CONFIGURATION_FILE authlete.configuration.file}
* if the value is not empty.
*
*
* @param key
* The secret key to decode encrypted property values in hex.
* For example, {@code "9543837d590ef25312e7d156a435feda"}.
*
* @param iv
* The initial vector to decode encrypted property values.
* For example, {@code "e90ce45e6134d37e0aa2c3c870003639"}.
*
* @throws IllegalArgumentException
*
* - {@code key} is {@code null}
*
- {@code iv} is {@code null}
*
*
* @throws NumberFormatException
*
* - {@code key} is not a valid hex string.
*
- {@code iv} is not a valid hex string.
*
*/
public AuthletePropertiesConfiguration(String key, String iv)
{
this(getFile(), key, iv);
}
/**
* Constructor with a pair of secret key and initial vector to decode
* encrypted property values.
*
*
* This constructor is an alias of {@link #AuthletePropertiesConfiguration(
* String, byte[], byte[]) this}(file, key, iv)
where
* file
is either {@link #DEFAULT_FILE
* authlete.properties} or the value of the system property {@link
* #SYSTEM_PROPERTY_AUTHLETE_CONFIGURATION_FILE authlete.configuration.file}
* if the value is not empty.
*
*
* @param key
* The secret key to decode encrypted property values.
*
* @param iv
* The initial vector to decode encrypted property values.
*
* @throws IllegalArgumentException
*
* - {@code key} is {@code null}
*
- {@code iv} is {@code null}
*
*/
public AuthletePropertiesConfiguration(byte[] key, byte[] iv)
{
this(getFile(), key, iv);
}
/**
* Constructor with a configuration file name and a pair of secret key
* and initial vector to decode encrypted property values.
*
* @param file
* The name of the configuration file. The file system and then
* the classpath are searched for the file.
*
* @param key
* The secret key to decode encrypted property values in hex.
* For example, {@code "9543837d590ef25312e7d156a435feda"}.
*
* @param iv
* The initial vector to decode encrypted property values.
* For example, {@code "e90ce45e6134d37e0aa2c3c870003639"}.
*
* @throws IllegalArgumentException
*
* - {@code file} is {@code null}
*
- {@code key} is {@code null}
*
- {@code iv} is {@code null}
*
*
* @throws NumberFormatException
*
* - {@code key} is not a valid hex string.
*
- {@code iv} is not a valid hex string.
*
*/
public AuthletePropertiesConfiguration(String file, String key, String iv)
{
this(file, convertHexStringToBytes("key", key), convertHexStringToBytes("iv", iv));
}
/**
* Constructor with a configuration file name.
*
*
* This constructor is an alias of {@link #AuthletePropertiesConfiguration(
* String, String, String) this}{@code (file, }{@link #DEFAULT_KEY}{@code
* , }{@link #DEFAULT_IV}{@code )}.
*
*
* @param file
* The name of the configuration file. The file system and then
* the classpath are searched for the file.
*
* @throws IllegalArgumentException
* {@code file} is {@code null}.
*
* @since 1.24
*/
public AuthletePropertiesConfiguration(String file)
{
this(file, DEFAULT_KEY, DEFAULT_IV);
}
/**
* Constructor with no argument.
*
*
* This constructor is an alias of {@link #AuthletePropertiesConfiguration(
* String, String, String) this}(file,
{@link
* #DEFAULT_KEY}{@code , }{@link #DEFAULT_IV}{@code )} where
* file
is either {@link #DEFAULT_FILE
* authlete.properties} or the value of the system property {@link
* #SYSTEM_PROPERTY_AUTHLETE_CONFIGURATION_FILE authlete.configuration.file}
* if the value is not empty.
*
*
* @since 1.24
*/
public AuthletePropertiesConfiguration()
{
this(getFile(), DEFAULT_KEY, DEFAULT_IV);
}
/**
* Constructor with a configuration file name and a pair of secret key
* and initial vector to decode encrypted property values.
*
* @param file
* The name of the configuration file. The file system and then
* the classpath are searched for the file.
*
* @param key
* The secret key to decode encrypted property values.
*
* @param iv
* The initial vector to decode encrypted property values.
*
* @throws IllegalArgumentException
*
* - {@code file} is {@code null}
*
- {@code key} is {@code null}
*
- {@code iv} is {@code null}
*
*/
public AuthletePropertiesConfiguration(String file, byte[] key, byte[] iv)
{
// Load the configuration file.
TypedProperties props = PropertiesLoader.load(file);
// If failed.
if (props == null)
{
// Failed to load the configuration file.
String message = String.format("Failed to load '%s'.", file);
Logger.getLogger(AuthletePropertiesConfiguration.class.getName()).severe(message);
return;
}
// Base URL of Authlete API.
mBaseUrl = props.getString(PROPERTY_KEY_BASE_URL, BASE_URL_DEFAULT);
// Service owner API key issued by Authlete.
mServiceOwnerApiKey = props.getString(PROPERTY_KEY_SERVICE_OWNER_API_KEY);
// Service owner API secret issued by Authlete.
String encryptedServiceOwnerApiSecret = props.getString(PROPERTY_KEY_SERVICE_OWNER_API_SECRET_ENCRYPTED);
if (encryptedServiceOwnerApiSecret != null)
{
mServiceOwnerApiSecret = createCipher(key,iv).decrypt(encryptedServiceOwnerApiSecret);
}
else
{
mServiceOwnerApiSecret = props.getString(PROPERTY_KEY_SERVICE_OWNER_API_SECRET);
}
// Service API key issued by Authlete.
mServiceApiKey = props.getString(PROPERTY_KEY_SERVICE_API_KEY);
// Service API secret issued by Authlete.
String encryptedServiceApiSecret = props.getString(PROPERTY_KEY_SERVICE_API_SECRET_ENCRYPTED);
if (encryptedServiceApiSecret != null)
{
mServiceApiSecret = createCipher(key,iv).decrypt(encryptedServiceApiSecret);
}
else
{
mServiceApiSecret = props.getString(PROPERTY_KEY_SERVICE_API_SECRET);
}
mServiceAccessToken = props.getString(PROPERTY_KEY_SERVICE_ACCESS_TOKEN);
mServiceOwnerAccessToken = props.getString(PROPERTY_KEY_SERVICE_OWNER_ACCESS_TOKEN);
mDpopKey = props.getString(PROPERTY_KEY_DPOP_KEY);
mClientCertificate = props.getString(PROPERTY_KEY_CLIENT_CERTIFICATE);
mApiVersion = props.getString(PROPERTY_KEY_API_VERSION);
}
private static String getFile()
{
// The name of the authlete configuration file specified via the system property.
String file = System.getProperty(SYSTEM_PROPERTY_AUTHLETE_CONFIGURATION_FILE);
if (file != null && file.length() != 0)
{
return file;
}
// The default file name.
return DEFAULT_FILE;
}
private static AESCipher createCipher(byte[] key, byte[] iv)
{
ensureNonNull("key", key);
ensureNonNull("iv", iv);
// Create a cipher for AES/CBC/PKCS5Padding + Base64
// (the combination is the default of AECipher()).
return new AESCipher().setKey(key, iv);
}
private static byte[] convertHexStringToBytes(String name, String value)
{
ensureNonNull(name, value);
int len = value.length();
byte[] bytes = new byte[(len + 1) / 2];
for (int i = 0; i < len; ++i)
{
char c = value.charAt(i);
int n = convertHexCharToInt(c);
if (i % 2 == 0)
{
bytes[i / 2] = (byte)((n << 4) & 0xFF);
}
else
{
bytes[i / 2] |= (byte)(n & 0xFF);
}
}
return bytes;
}
private static int convertHexCharToInt(char c)
{
if ('0' <= c && c <= '9')
{
return (c - '0');
}
else if ('a' <= c && c <= 'f')
{
return (c - 'a' + 10);
}
else
{
return (c - 'A' + 10);
}
}
private static void ensureNonNull(String name, Object value)
{
if (value == null)
{
throw new IllegalArgumentException(name + " is null.");
}
}
/**
* Get the Authlete API version.
*/
@Override
public String getApiVersion()
{
return mApiVersion;
}
/**
* Get the base URL.
*/
@Override
public String getBaseUrl()
{
return mBaseUrl;
}
/**
* Get the service owner API key.
*/
@Override
public String getServiceOwnerApiKey()
{
return mServiceOwnerApiKey;
}
/**
* Get the service owner API secret.
*/
@Override
public String getServiceOwnerApiSecret()
{
return mServiceOwnerApiSecret;
}
@Override
public String getServiceOwnerAccessToken()
{
return mServiceOwnerAccessToken;
}
/**
* Get the service API key.
*/
@Override
public String getServiceApiKey()
{
return mServiceApiKey;
}
/**
* Get the service API secret.
*/
@Override
public String getServiceApiSecret()
{
return mServiceApiSecret;
}
@Override
public String getServiceAccessToken()
{
return mServiceAccessToken;
}
@Override
public String getDpopKey()
{
return mDpopKey;
}
@Override
public String getClientCertificate()
{
return mClientCertificate;
}
}