diff --git a/README.md b/README.md index 0f57fe8..29a6e6f 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,11 @@ URI uri = URI.create("https://sandbox.api.mastercard.com/service"); String method = "POST"; String payload = "Hello world!"; Charset charset = StandardCharsets.UTF_8; -String authHeader = OAuth.getAuthorizationHeader(uri, method, payload, charset, consumerKey, signingKey); +String authHeader = OAuth.getAuthorizationHeader(uri, method, payload, charset, consumerKey, signingKey); // uses RSA_SHA256 as the default signature method +``` +Alternatively, you can specify the signature method as well: +```java +String authHeader = OAuth.getAuthorizationHeader(uri, method, payload, charset, consumerKey, signingKey, SignatureMethod.RSA_PSS_SHA256); ``` ### Signing HTTP Client Request Objects @@ -113,7 +117,13 @@ HttpsURLConnection con = (HttpsURLConnection)url.openConnection(); con.setRequestMethod("POST"); con.setRequestProperty("Content-Type", "application/json; charset=" + charset.name()); -HttpsUrlConnectionSigner signer = new HttpsUrlConnectionSigner(charset, consumerKey, signingKey); +HttpsUrlConnectionSigner signer = new HttpsUrlConnectionSigner(charset, consumerKey, signingKey); // uses RSA_SHA256 as the default signature method +signer.sign(con, payload); +``` + +You can also specify the signature method when creating the signer object: +```java +HttpsUrlConnectionSigner signer = new HttpsUrlConnectionSigner(charset, consumerKey, signingKey, SignatureMethod.RSA_PSS_SHA256); signer.sign(con, payload); ``` @@ -125,7 +135,9 @@ HttpClient httpClient = HttpClientBuilder.create().build(); HttpPost httpPost = new HttpPost("https://sandbox.api.mastercard.com/service"); httpPost.setEntity(new StringEntity(payload, ContentType.APPLICATION_JSON)); -ApacheHttpClient4Signer signer = new ApacheHttpClient4Signer(consumerKey, signingKey); +ApacheHttpClient4Signer signer = new ApacheHttpClient4Signer(consumerKey, signingKey); // uses RSA_SHA256 as the default signature method +// You can also specify the signature method: +// ApacheHttpClient4Signer signer = new ApacheHttpClient4Signer(consumerKey, signingKey, SignatureMethod.RSA_PSS_SHA256); signer.sign(httpPost); ``` @@ -140,7 +152,9 @@ Request.Builder request = new Request.Builder() .url("https://sandbox.api.mastercard.com/service") .post(body); -OkHttpSigner signer = new OkHttpSigner(consumerKey, signingKey); +OkHttpSigner signer = new OkHttpSigner(consumerKey, signingKey); // uses RSA_SHA256 as the default signature method +// You can also specify the signature method: +// OkHttpSigner signer = new OkHttpSigner(consumerKey, signingKey, SignatureMethod.RSA_PSS_SHA256); signer.sign(request); ``` @@ -152,7 +166,9 @@ ClientRequest request = ClientRequest.create(HttpMethod.POST, URI.create("https: .body(BodyInserters.fromValue(new BodyInserterWrapper(yourRequestObject))) .build(); -SpringWebfluxSigner signer = new SpringWebfluxSigner(consumerKey, signingKey); +SpringWebfluxSigner signer = new SpringWebfluxSigner(consumerKey, signingKey); // uses RSA_SHA256 as the default signature method +// You can also specify the signature method: +// SpringWebfluxSigner signer = new SpringWebfluxSigner(consumerKey, signingKey, SignatureMethod.RSA_PSS_SHA256); ClientRequest signedRequest = signer.sign(request); client.exchange(signedRequest); ``` @@ -193,7 +209,11 @@ See also: ApiClient client = new ApiClient(); client.setBasePath("https://sandbox.api.mastercard.com"); List interceptors = client.getHttpClient().interceptors(); -interceptors.add(new OkHttp2OAuth1Interceptor(consumerKey, signingKey)); +interceptors.add( + new OkHttp2OAuth1Interceptor(consumerKey, signingKey) // uses RSA_SHA256 as the default signature method + // if you want to specify the signature method + // new OkHttp2OAuth1Interceptor(consumerKey, signingKey, SignatureMethod.RSA_PSS_SHA256) +); ServiceApi serviceApi = new ServiceApi(client); // ... ``` @@ -207,7 +227,11 @@ client.setHttpClient( client.getHttpClient() .newBuilder() .proxy(proxy) // Optional proxy - .addInterceptor(new OkHttpOAuth1Interceptor(consumerKey, signingKey)) + .addInterceptor( + new OkHttpOAuth1Interceptor(consumerKey, signingKey) // uses RSA_SHA256 as the default signature method + // if you want to specify the signature method + // new OkHttpOAuth1Interceptor(consumerKey, signingKey, SignatureMethod.RSA_PSS_SHA256) + ) .build() ); ServiceApi serviceApi = new ServiceApi(client); @@ -231,7 +255,11 @@ ApiClient client = new ApiClient(); client.setBasePath("https://sandbox.api.mastercard.com"); Feign.Builder feignBuilder = client.getFeignBuilder(); ArrayList interceptors = new ArrayList<>(); -interceptors.add(new OpenFeignOAuth1Interceptor(consumerKey, signingKey, client.getBasePath())); +interceptors.add( + new OpenFeignOAuth1Interceptor(consumerKey, signingKey, client.getBasePath()) // uses RSA_SHA256 as the default signature method + // if you want to specify the signature method + // new OpenFeignOAuth1Interceptor(consumerKey, signingKey, client.getBasePath(), SignatureMethod.RSA_PSS_SHA256) +); feignBuilder.requestInterceptors(interceptors); ServiceApi serviceApi = client.buildClient(ServiceApi.class); // ... @@ -254,7 +282,11 @@ ApiClient client = new ApiClient(); RestAdapter.Builder adapterBuilder = client.getAdapterBuilder(); adapterBuilder.setEndpoint("https://sandbox.api.mastercard.com"); List interceptors = client.getOkClient().interceptors(); -interceptors.add(new OkHttp2OAuth1Interceptor(consumerKey, signingKey)); +interceptors.add( + new OkHttp2OAuth1Interceptor(consumerKey, signingKey) // uses RSA_SHA256 as the default signature method + // if you want to specify the signature method + // new OkHttp2OAuth1Interceptor(consumerKey, signingKey, SignatureMethod.RSA_PSS_SHA256) +); ServiceApi serviceApi = client.createService(ServiceApi.class); // ... ``` @@ -276,7 +308,11 @@ ApiClient client = new ApiClient(); Retrofit.Builder adapterBuilder = client.getAdapterBuilder(); adapterBuilder.baseUrl("https://sandbox.api.mastercard.com"); OkHttpClient.Builder okBuilder = client.getOkBuilder(); -okBuilder.addInterceptor(new OkHttpOAuth1Interceptor(consumerKey, signingKey)); +okBuilder.addInterceptor( + new OkHttpOAuth1Interceptor(consumerKey, signingKey) // uses RSA_SHA256 as the default signature method + // if you want to specify the signature method + // new OkHttpOAuth1Interceptor(consumerKey, signingKey, SignatureMethod.RSA_PSS_SHA256) +); ServiceApi serviceApi = client.createService(ServiceApi.class); // ... ``` @@ -297,7 +333,11 @@ ServiceApi serviceApi = client.createService(ServiceApi.class); HttpRequestInitializer initializer = new HttpRequestInitializer() { @Override public void initialize(HttpRequest request) { - request.setInterceptor(new HttpExecuteOAuth1Interceptor(consumerKey, signingKey)); + request.setInterceptor( + new HttpExecuteOAuth1Interceptor(consumerKey, signingKey) // uses RSA_SHA256 as the default signature method + // if you want to specify the signature method + // new HttpExecuteOAuth1Interceptor(consumerKey, signingKey, SignatureMethod.RSA_PSS_SHA256) + ); } }; ApiClient client = new ApiClient("https://sandbox.api.mastercard.com", null, initializer, null); @@ -320,7 +360,11 @@ ServiceApi serviceApi = client.serviceApi(); ```java WebClient.Builder webClientBuilder = WebClient.builder() .baseUrl("https://api.mastercard.com/service") - .filter(new SpringWebfluxOAuth1Interceptor(consumerKey, signingKey)); + .filter( + new SpringWebfluxOAuth1Interceptor(consumerKey, signingKey) // uses RSA_SHA256 as the default signature method + // if you want to specify the signature method + // new SpringWebfluxOAuth1Interceptor(consumerKey, signingKey, SignatureMethod.RSA_PSS_SHA256) + ); ApiClient apiClient = new ApiClient(webClientBuilder); ServiceApi serviceApi = client.serviceApi(); diff --git a/pom.xml b/pom.xml index bde002d..b7d456f 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.mastercard.developer oauth1-signer - 1.5.6 + 1.6.0 jar Zero dependency library for generating a Mastercard API compliant OAuth signature https://github.com/Mastercard/oauth1-signer-java @@ -113,6 +113,12 @@ 3.27.7 test + + org.mockito + mockito-core + 5.21.0 + test + diff --git a/src/main/java/com/mastercard/developer/interceptors/HttpExecuteOAuth1Interceptor.java b/src/main/java/com/mastercard/developer/interceptors/HttpExecuteOAuth1Interceptor.java index c64c23f..7c080c5 100644 --- a/src/main/java/com/mastercard/developer/interceptors/HttpExecuteOAuth1Interceptor.java +++ b/src/main/java/com/mastercard/developer/interceptors/HttpExecuteOAuth1Interceptor.java @@ -2,6 +2,8 @@ import com.google.api.client.http.HttpExecuteInterceptor; import com.google.api.client.http.HttpRequest; +import com.mastercard.developer.oauth.OAuth; +import com.mastercard.developer.oauth.SignatureMethod; import com.mastercard.developer.signers.GoogleApiClientSigner; import java.io.IOException; @@ -16,7 +18,11 @@ public class HttpExecuteOAuth1Interceptor implements HttpExecuteInterceptor { private final GoogleApiClientSigner signer; public HttpExecuteOAuth1Interceptor(String consumerKey, PrivateKey signingKey) { - this.signer = new GoogleApiClientSigner(consumerKey, signingKey); + this(consumerKey, signingKey, OAuth.DEFAULT_SIGNATURE_METHOD); + } + + public HttpExecuteOAuth1Interceptor(String consumerKey, PrivateKey signingKey, SignatureMethod signatureMethod) { + this.signer = new GoogleApiClientSigner(consumerKey, signingKey, signatureMethod); } public void intercept(HttpRequest request) throws IOException { diff --git a/src/main/java/com/mastercard/developer/interceptors/OkHttp2OAuth1Interceptor.java b/src/main/java/com/mastercard/developer/interceptors/OkHttp2OAuth1Interceptor.java index 9e58b04..31bd13d 100644 --- a/src/main/java/com/mastercard/developer/interceptors/OkHttp2OAuth1Interceptor.java +++ b/src/main/java/com/mastercard/developer/interceptors/OkHttp2OAuth1Interceptor.java @@ -1,5 +1,7 @@ package com.mastercard.developer.interceptors; +import com.mastercard.developer.oauth.OAuth; +import com.mastercard.developer.oauth.SignatureMethod; import com.mastercard.developer.signers.OkHttp2Signer; import com.squareup.okhttp.*; @@ -14,7 +16,11 @@ public class OkHttp2OAuth1Interceptor implements Interceptor { private final OkHttp2Signer signer; public OkHttp2OAuth1Interceptor(String consumerKey, PrivateKey signingKey) { - this.signer = new OkHttp2Signer(consumerKey, signingKey); + this(consumerKey, signingKey, OAuth.DEFAULT_SIGNATURE_METHOD); + } + + public OkHttp2OAuth1Interceptor(String consumerKey, PrivateKey signingKey, SignatureMethod signatureMethod) { + this.signer = new OkHttp2Signer(consumerKey, signingKey, signatureMethod); } @Override diff --git a/src/main/java/com/mastercard/developer/interceptors/OkHttpOAuth1Interceptor.java b/src/main/java/com/mastercard/developer/interceptors/OkHttpOAuth1Interceptor.java index 3b63e30..85b04c3 100644 --- a/src/main/java/com/mastercard/developer/interceptors/OkHttpOAuth1Interceptor.java +++ b/src/main/java/com/mastercard/developer/interceptors/OkHttpOAuth1Interceptor.java @@ -1,5 +1,7 @@ package com.mastercard.developer.interceptors; +import com.mastercard.developer.oauth.OAuth; +import com.mastercard.developer.oauth.SignatureMethod; import com.mastercard.developer.signers.OkHttpSigner; import okhttp3.*; @@ -14,7 +16,11 @@ public class OkHttpOAuth1Interceptor implements Interceptor { private final OkHttpSigner signer; public OkHttpOAuth1Interceptor(String consumerKey, PrivateKey signingKey) { - this.signer = new OkHttpSigner(consumerKey, signingKey); + this(consumerKey, signingKey, OAuth.DEFAULT_SIGNATURE_METHOD); + } + + public OkHttpOAuth1Interceptor(String consumerKey, PrivateKey signingKey, SignatureMethod signatureMethod) { + this.signer = new OkHttpSigner(consumerKey, signingKey, signatureMethod); } @Override diff --git a/src/main/java/com/mastercard/developer/interceptors/OpenFeignOAuth1Interceptor.java b/src/main/java/com/mastercard/developer/interceptors/OpenFeignOAuth1Interceptor.java index 636fcff..280618f 100644 --- a/src/main/java/com/mastercard/developer/interceptors/OpenFeignOAuth1Interceptor.java +++ b/src/main/java/com/mastercard/developer/interceptors/OpenFeignOAuth1Interceptor.java @@ -1,5 +1,7 @@ package com.mastercard.developer.interceptors; +import com.mastercard.developer.oauth.OAuth; +import com.mastercard.developer.oauth.SignatureMethod; import com.mastercard.developer.signers.OpenFeignSigner; import feign.RequestInterceptor; import feign.RequestTemplate; @@ -15,7 +17,11 @@ public class OpenFeignOAuth1Interceptor implements RequestInterceptor { private final OpenFeignSigner signer; public OpenFeignOAuth1Interceptor(String consumerKey, PrivateKey signingKey, String baseUri) { - this.signer = new OpenFeignSigner(consumerKey, signingKey, baseUri); + this(consumerKey, signingKey, baseUri, OAuth.DEFAULT_SIGNATURE_METHOD); + } + + public OpenFeignOAuth1Interceptor(String consumerKey, PrivateKey signingKey, String baseUri, SignatureMethod signatureMethod) { + this.signer = new OpenFeignSigner(consumerKey, signingKey, baseUri, signatureMethod); } @Override diff --git a/src/main/java/com/mastercard/developer/interceptors/SpringWebfluxOAuth1Interceptor.java b/src/main/java/com/mastercard/developer/interceptors/SpringWebfluxOAuth1Interceptor.java index 67e9e48..ca680ff 100644 --- a/src/main/java/com/mastercard/developer/interceptors/SpringWebfluxOAuth1Interceptor.java +++ b/src/main/java/com/mastercard/developer/interceptors/SpringWebfluxOAuth1Interceptor.java @@ -1,5 +1,7 @@ package com.mastercard.developer.interceptors; +import com.mastercard.developer.oauth.OAuth; +import com.mastercard.developer.oauth.SignatureMethod; import com.mastercard.developer.signers.SpringWebfluxSigner; import org.springframework.web.reactive.function.client.ClientRequest; import org.springframework.web.reactive.function.client.ClientResponse; @@ -17,7 +19,11 @@ public class SpringWebfluxOAuth1Interceptor implements ExchangeFilterFunction { private final SpringWebfluxSigner signer; public SpringWebfluxOAuth1Interceptor(String consumerKey, PrivateKey signingKey) { - this.signer = new SpringWebfluxSigner(consumerKey, signingKey); + this(consumerKey, signingKey, OAuth.DEFAULT_SIGNATURE_METHOD); + } + + public SpringWebfluxOAuth1Interceptor(String consumerKey, PrivateKey signingKey, SignatureMethod signatureMethod) { + this.signer = new SpringWebfluxSigner(consumerKey, signingKey, signatureMethod); } @Override diff --git a/src/main/java/com/mastercard/developer/oauth/OAuth.java b/src/main/java/com/mastercard/developer/oauth/OAuth.java index e23d696..29f87e1 100755 --- a/src/main/java/com/mastercard/developer/oauth/OAuth.java +++ b/src/main/java/com/mastercard/developer/oauth/OAuth.java @@ -24,14 +24,15 @@ private OAuth() { public static final String EMPTY_STRING = ""; public static final String AUTHORIZATION_HEADER_NAME = "Authorization"; + public static final SignatureMethod DEFAULT_SIGNATURE_METHOD = SignatureMethod.RSA_SHA256; private static final Logger LOG = Logger.getLogger(OAuth.class.getName()); - private static final String HASH_ALGORITHM = "SHA-256"; + private static final String BODY_HASH_ALGORITHM = "SHA-256"; private static final int NONCE_LENGTH = 16; private static final String ALPHA_NUMERIC_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; /** - * Creates a Mastercard API compliant OAuth Authorization header + * Creates a Mastercard API compliant OAuth Authorization header, using RSA-SHA256 as the signature method * * @param uri Target URI for this request * @param method HTTP method of the request @@ -40,17 +41,34 @@ private OAuth() { * @param consumerKey Consumer key set up in a Mastercard Developer Portal project * @param signingKey The private key that will be used for signing the request that corresponds to the consumerKey * @return Valid OAuth1.0a signature with a body hash when payload is present + * @see #getAuthorizationHeader(URI, String, String, Charset, String, PrivateKey, SignatureMethod) */ public static String getAuthorizationHeader(URI uri, String method, String payload, Charset charset, String consumerKey, PrivateKey signingKey) { + return getAuthorizationHeader(uri, method, payload, charset, consumerKey, signingKey, DEFAULT_SIGNATURE_METHOD); + } + + /** + * Creates a Mastercard API compliant OAuth Authorization header + * + * @param uri Target URI for this request + * @param method HTTP method of the request + * @param payload Payload (nullable) + * @param charset Charset encoding of the request + * @param consumerKey Consumer key set up in a Mastercard Developer Portal project + * @param signingKey The private key that will be used for signing the request that corresponds to the consumerKey + * @param signMethod The signature method to use when signing the request + * @return Valid OAuth1.0a signature with a body hash when payload is present + */ + public static String getAuthorizationHeader(URI uri, String method, String payload, Charset charset, String consumerKey, PrivateKey signingKey, SignatureMethod signMethod) { TreeMap> queryParams = extractQueryParams(uri, charset); HashMap oauthParams = new HashMap<>(); oauthParams.put("oauth_consumer_key", consumerKey); oauthParams.put("oauth_nonce", getNonce()); - oauthParams.put("oauth_signature_method", "RSA-" + HASH_ALGORITHM.replace("-", "")); + oauthParams.put("oauth_signature_method", signMethod.getOauthName()); oauthParams.put("oauth_timestamp", getTimestamp()); oauthParams.put("oauth_version", "1.0"); - oauthParams.put("oauth_body_hash", getBodyHash(payload, charset, HASH_ALGORITHM)); + oauthParams.put("oauth_body_hash", getBodyHash(payload, charset, BODY_HASH_ALGORITHM)); // Combine query and oauth_ parameters into lexicographically sorted string String paramString = toOauthParamString(queryParams, oauthParams); @@ -62,7 +80,7 @@ public static String getAuthorizationHeader(URI uri, String method, String paylo String sbs = getSignatureBaseString(method, baseUri, paramString, charset); // Signature - String signature = signSignatureBaseString(sbs, signingKey, charset); + String signature = signSignatureBaseString(sbs, signingKey, charset, signMethod); oauthParams.put("oauth_signature", Util.percentEncode(signature, charset)); return getAuthorizationString(oauthParams); @@ -252,6 +270,19 @@ static String getBodyHash(String payload, Charset charset, String hashAlg) { return Util.b64Encode(hash); } + /** + * Signs the signature base string using an RSA private key and RSA-SHA256 as the signature method. + * + * @param sbs Signature base string formatted as per https://tools.ietf.org/html/rfc5849#section-3.4.1 + * @param signingKey Private key of the RSA key pair that was established with the service provider + * @param charset Charset encoding of the request + * @return RSA signature matching the contents of signature base string + * @see #signSignatureBaseString(String, PrivateKey, Charset, SignatureMethod) + */ + static String signSignatureBaseString(String sbs, PrivateKey signingKey, Charset charset) { + return signSignatureBaseString(sbs, signingKey, charset, DEFAULT_SIGNATURE_METHOD); + } + /** * Signs the signature base string using an RSA private key. The methodology is described at * https://tools.ietf.org/html/rfc5849#section-3.4.3 but Mastercard uses the stronger SHA-256 algorithm @@ -260,18 +291,22 @@ static String getBodyHash(String payload, Charset charset, String hashAlg) { * @param sbs Signature base string formatted as per https://tools.ietf.org/html/rfc5849#section-3.4.1 * @param signingKey Private key of the RSA key pair that was established with the service provider * @param charset Charset encoding of the request + * @param signMethod The signature method to use when signing the request * @return RSA signature matching the contents of signature base string */ - static String signSignatureBaseString(String sbs, PrivateKey signingKey, Charset charset) { + static String signSignatureBaseString(String sbs, PrivateKey signingKey, Charset charset, SignatureMethod signMethod) { try { - Signature signer = Signature.getInstance("SHA256withRSA"); + Signature signer = Signature.getInstance(signMethod.getJcaName()); + if(signMethod.getAlgorithmParams() != null) { + signer.setParameter(signMethod.getAlgorithmParams()); + } signer.initSign(signingKey); byte[] sbsBytes = sbs.getBytes(charset); signer.update(sbsBytes); byte[] signatureBytes = signer.sign(); return Util.b64Encode(signatureBytes); } catch (GeneralSecurityException e) { - throw new IllegalStateException("Unable to RSA-SHA256 sign the given string with the provided key", e); + throw new IllegalStateException("Unable to sign with method " + signMethod.getOauthName() + " using the provided key", e); } } diff --git a/src/main/java/com/mastercard/developer/oauth/SignatureMethod.java b/src/main/java/com/mastercard/developer/oauth/SignatureMethod.java new file mode 100644 index 0000000..e44588e --- /dev/null +++ b/src/main/java/com/mastercard/developer/oauth/SignatureMethod.java @@ -0,0 +1,39 @@ +package com.mastercard.developer.oauth; + +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.MGF1ParameterSpec; +import java.security.spec.PSSParameterSpec; + +/** + * Supported OAuth 1.0 signature methods + */ +public enum SignatureMethod { + + RSA_SHA256("SHA256withRSA", "RSA-SHA256", null), + RSA_PSS_SHA256("RSASSA-PSS", "RSA-PSS", new PSSParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 32, 1)); + + /** JCA signature algorithm name. */ + private final String jcaName; + /** OAuth signature method name. */ + private final String oAuthName; + private final AlgorithmParameterSpec algorithmParams; + + + SignatureMethod(String jcaName, String oAuthName, AlgorithmParameterSpec algorithmParams) { + this.jcaName = jcaName; + this.oAuthName = oAuthName; + this.algorithmParams = algorithmParams; + } + + String getOauthName() { + return oAuthName; + } + + String getJcaName() { + return jcaName; + } + + AlgorithmParameterSpec getAlgorithmParams() { + return algorithmParams; + } +} diff --git a/src/main/java/com/mastercard/developer/signers/AbstractSigner.java b/src/main/java/com/mastercard/developer/signers/AbstractSigner.java index e8d21f8..04c6239 100644 --- a/src/main/java/com/mastercard/developer/signers/AbstractSigner.java +++ b/src/main/java/com/mastercard/developer/signers/AbstractSigner.java @@ -1,5 +1,8 @@ package com.mastercard.developer.signers; +import com.mastercard.developer.oauth.OAuth; +import com.mastercard.developer.oauth.SignatureMethod; + import java.nio.charset.Charset; import java.security.PrivateKey; @@ -8,14 +11,20 @@ public abstract class AbstractSigner { protected final String consumerKey; protected final PrivateKey signingKey; protected final Charset charset; + protected final SignatureMethod signatureMethod; protected AbstractSigner(String consumerKey, PrivateKey signingKey) { - this(Charset.defaultCharset(), consumerKey, signingKey); + this(Charset.defaultCharset(), consumerKey, signingKey, OAuth.DEFAULT_SIGNATURE_METHOD); + } + + protected AbstractSigner(String consumerKey, PrivateKey signingKey, SignatureMethod signatureMethod) { + this(Charset.defaultCharset(), consumerKey, signingKey, signatureMethod); } - protected AbstractSigner(Charset charset, String consumerKey, PrivateKey signingKey) { + protected AbstractSigner(Charset charset, String consumerKey, PrivateKey signingKey, SignatureMethod signatureMethod) { this.consumerKey = consumerKey; this.signingKey = signingKey; this.charset = charset; + this.signatureMethod = signatureMethod; } } diff --git a/src/main/java/com/mastercard/developer/signers/ApacheHttpClient4Signer.java b/src/main/java/com/mastercard/developer/signers/ApacheHttpClient4Signer.java index 745b605..3198212 100644 --- a/src/main/java/com/mastercard/developer/signers/ApacheHttpClient4Signer.java +++ b/src/main/java/com/mastercard/developer/signers/ApacheHttpClient4Signer.java @@ -4,6 +4,8 @@ import java.io.IOException; import java.nio.charset.Charset; import java.security.PrivateKey; + +import com.mastercard.developer.oauth.SignatureMethod; import org.apache.http.HttpEntity; import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; import org.apache.http.client.methods.HttpRequestBase; @@ -19,6 +21,10 @@ public ApacheHttpClient4Signer(String consumerKey, PrivateKey signingKey) { super(consumerKey, signingKey); } + public ApacheHttpClient4Signer(String consumerKey, PrivateKey signingKey, SignatureMethod signatureMethod) { + super(consumerKey, signingKey, signatureMethod); + } + public void sign(HttpRequestBase req) throws IOException { String payload = null; Charset charset = Charset.defaultCharset(); @@ -36,7 +42,7 @@ public void sign(HttpRequestBase req) throws IOException { } } - String authHeader = OAuth.getAuthorizationHeader(req.getURI(), req.getMethod(), payload, charset, consumerKey, signingKey); + String authHeader = OAuth.getAuthorizationHeader(req.getURI(), req.getMethod(), payload, charset, consumerKey, signingKey, signatureMethod); req.setHeader(OAuth.AUTHORIZATION_HEADER_NAME, authHeader); } } diff --git a/src/main/java/com/mastercard/developer/signers/GoogleApiClientSigner.java b/src/main/java/com/mastercard/developer/signers/GoogleApiClientSigner.java index 6c46e50..e9f6f87 100644 --- a/src/main/java/com/mastercard/developer/signers/GoogleApiClientSigner.java +++ b/src/main/java/com/mastercard/developer/signers/GoogleApiClientSigner.java @@ -3,6 +3,7 @@ import com.google.api.client.http.HttpContent; import com.google.api.client.http.HttpRequest; import com.mastercard.developer.oauth.OAuth; +import com.mastercard.developer.oauth.SignatureMethod; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -20,8 +21,16 @@ public GoogleApiClientSigner(String consumerKey, PrivateKey signingKey) { super(consumerKey, signingKey); } + public GoogleApiClientSigner(String consumerKey, PrivateKey signingKey, SignatureMethod signatureMethod) { + super(consumerKey, signingKey, signatureMethod); + } + public GoogleApiClientSigner(Charset charset, String consumerKey, PrivateKey signingKey) { - super(charset, consumerKey, signingKey); + super(charset, consumerKey, signingKey, OAuth.DEFAULT_SIGNATURE_METHOD); + } + + public GoogleApiClientSigner(Charset charset, String consumerKey, PrivateKey signingKey, SignatureMethod signatureMethod) { + super(charset, consumerKey, signingKey, signatureMethod); } public void sign(HttpRequest request) throws IOException { @@ -36,7 +45,7 @@ public void sign(HttpRequest request) throws IOException { payload = outputStream.toString(charset.name()); } - String authorizationHeader = OAuth.getAuthorizationHeader(uri, method, payload, charset, consumerKey, signingKey); + String authorizationHeader = OAuth.getAuthorizationHeader(uri, method, payload, charset, consumerKey, signingKey, signatureMethod); request.getHeaders().setAuthorization(authorizationHeader); } } diff --git a/src/main/java/com/mastercard/developer/signers/HttpsUrlConnectionSigner.java b/src/main/java/com/mastercard/developer/signers/HttpsUrlConnectionSigner.java index 1420d99..c14764b 100644 --- a/src/main/java/com/mastercard/developer/signers/HttpsUrlConnectionSigner.java +++ b/src/main/java/com/mastercard/developer/signers/HttpsUrlConnectionSigner.java @@ -1,6 +1,8 @@ package com.mastercard.developer.signers; import com.mastercard.developer.oauth.OAuth; +import com.mastercard.developer.oauth.SignatureMethod; + import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.Charset; @@ -13,7 +15,11 @@ public class HttpsUrlConnectionSigner extends AbstractSigner { public HttpsUrlConnectionSigner(Charset charset, String consumerKey, PrivateKey signingKey) { - super(charset, consumerKey, signingKey); + super(charset, consumerKey, signingKey, OAuth.DEFAULT_SIGNATURE_METHOD); + } + + public HttpsUrlConnectionSigner(Charset charset, String consumerKey, PrivateKey signingKey, SignatureMethod signatureMethod) { + super(charset, consumerKey, signingKey, signatureMethod); } public void sign(HttpsURLConnection req, String payload) { @@ -24,7 +30,7 @@ public void sign(HttpsURLConnection req, String payload) { throw new IllegalArgumentException("The provided URL could not be converted to an URI representation", e); } String method = req.getRequestMethod(); - String authHeader = OAuth.getAuthorizationHeader(uri, method, payload, charset, consumerKey, signingKey); + String authHeader = OAuth.getAuthorizationHeader(uri, method, payload, charset, consumerKey, signingKey, signatureMethod); req.setRequestProperty(OAuth.AUTHORIZATION_HEADER_NAME, authHeader); } } diff --git a/src/main/java/com/mastercard/developer/signers/OkHttp2Signer.java b/src/main/java/com/mastercard/developer/signers/OkHttp2Signer.java index 7d02fae..eea2537 100644 --- a/src/main/java/com/mastercard/developer/signers/OkHttp2Signer.java +++ b/src/main/java/com/mastercard/developer/signers/OkHttp2Signer.java @@ -1,6 +1,7 @@ package com.mastercard.developer.signers; import com.mastercard.developer.oauth.OAuth; +import com.mastercard.developer.oauth.SignatureMethod; import com.squareup.okhttp.*; import okio.Buffer; @@ -17,11 +18,19 @@ public class OkHttp2Signer extends AbstractSigner { public OkHttp2Signer(String consumerKey, PrivateKey signingKey) { - super(StandardCharsets.UTF_8, consumerKey, signingKey); + super(StandardCharsets.UTF_8, consumerKey, signingKey, OAuth.DEFAULT_SIGNATURE_METHOD); + } + + public OkHttp2Signer(String consumerKey, PrivateKey signingKey, SignatureMethod signatureMethod) { + super(StandardCharsets.UTF_8, consumerKey, signingKey, signatureMethod); } public OkHttp2Signer(Charset charset, String consumerKey, PrivateKey signingKey) { - super(charset, consumerKey, signingKey); + super(charset, consumerKey, signingKey, OAuth.DEFAULT_SIGNATURE_METHOD); + } + + public OkHttp2Signer(Charset charset, String consumerKey, PrivateKey signingKey, SignatureMethod signatureMethod) { + super(charset, consumerKey, signingKey, signatureMethod); } public void sign(Request.Builder req) throws IOException { @@ -38,7 +47,7 @@ public void sign(Request.Builder req) throws IOException { payload = buffer.readUtf8(); } - String authHeader = OAuth.getAuthorizationHeader(uri, method, payload, charset, consumerKey, signingKey); + String authHeader = OAuth.getAuthorizationHeader(uri, method, payload, charset, consumerKey, signingKey, signatureMethod); req.addHeader(OAuth.AUTHORIZATION_HEADER_NAME, authHeader); } } diff --git a/src/main/java/com/mastercard/developer/signers/OkHttpSigner.java b/src/main/java/com/mastercard/developer/signers/OkHttpSigner.java index 6f8a2f4..fbef819 100644 --- a/src/main/java/com/mastercard/developer/signers/OkHttpSigner.java +++ b/src/main/java/com/mastercard/developer/signers/OkHttpSigner.java @@ -7,6 +7,7 @@ import java.nio.charset.StandardCharsets; import java.security.PrivateKey; +import com.mastercard.developer.oauth.SignatureMethod; import okhttp3.Request; import okhttp3.RequestBody; import okio.Buffer; @@ -17,11 +18,19 @@ public class OkHttpSigner extends AbstractSigner { public OkHttpSigner(String consumerKey, PrivateKey signingKey) { - super(StandardCharsets.UTF_8, consumerKey, signingKey); + super(StandardCharsets.UTF_8, consumerKey, signingKey, OAuth.DEFAULT_SIGNATURE_METHOD); + } + + public OkHttpSigner(String consumerKey, PrivateKey signingKey, SignatureMethod signatureMethod) { + super(StandardCharsets.UTF_8, consumerKey, signingKey, signatureMethod); } public OkHttpSigner(Charset charset, String consumerKey, PrivateKey signingKey) { - super(charset, consumerKey, signingKey); + super(charset, consumerKey, signingKey, OAuth.DEFAULT_SIGNATURE_METHOD); + } + + public OkHttpSigner(Charset charset, String consumerKey, PrivateKey signingKey, SignatureMethod signatureMethod) { + super(charset, consumerKey, signingKey, signatureMethod); } public void sign(Request.Builder req) throws IOException { @@ -38,7 +47,7 @@ public void sign(Request.Builder req) throws IOException { payload = buffer.readUtf8(); } - String authHeader = OAuth.getAuthorizationHeader(uri, method, payload, charset, consumerKey, signingKey); + String authHeader = OAuth.getAuthorizationHeader(uri, method, payload, charset, consumerKey, signingKey, signatureMethod); req.addHeader(OAuth.AUTHORIZATION_HEADER_NAME, authHeader); } } diff --git a/src/main/java/com/mastercard/developer/signers/OpenFeignSigner.java b/src/main/java/com/mastercard/developer/signers/OpenFeignSigner.java index d47b199..5b2e203 100644 --- a/src/main/java/com/mastercard/developer/signers/OpenFeignSigner.java +++ b/src/main/java/com/mastercard/developer/signers/OpenFeignSigner.java @@ -1,6 +1,7 @@ package com.mastercard.developer.signers; import com.mastercard.developer.oauth.OAuth; +import com.mastercard.developer.oauth.SignatureMethod; import feign.RequestTemplate; import java.net.URI; @@ -16,8 +17,18 @@ public OpenFeignSigner(String consumerKey, PrivateKey signingKey, String baseUri this.baseUri = baseUri; } + public OpenFeignSigner(String consumerKey, PrivateKey signingKey, String baseUri, SignatureMethod signatureMethod) { + super(consumerKey, signingKey, signatureMethod); + this.baseUri = baseUri; + } + public OpenFeignSigner(Charset charset, String consumerKey, PrivateKey signingKey, String baseUri) { - super(charset, consumerKey, signingKey); + super(charset, consumerKey, signingKey, OAuth.DEFAULT_SIGNATURE_METHOD); + this.baseUri = baseUri; + } + + public OpenFeignSigner(Charset charset, String consumerKey, PrivateKey signingKey, String baseUri, SignatureMethod signatureMethod) { + super(charset, consumerKey, signingKey, signatureMethod); this.baseUri = baseUri; } @@ -26,7 +37,7 @@ public void sign(RequestTemplate requestTemplate) { String method = requestTemplate.method(); byte[] bodyBytes = requestTemplate.body(); String payload = bodyBytes != null ? new String(bodyBytes, charset) : null; - String authHeader = OAuth.getAuthorizationHeader(uri, method, payload, charset, consumerKey, signingKey); + String authHeader = OAuth.getAuthorizationHeader(uri, method, payload, charset, consumerKey, signingKey, signatureMethod); requestTemplate.header(OAuth.AUTHORIZATION_HEADER_NAME, authHeader); } } diff --git a/src/main/java/com/mastercard/developer/signers/SpringHttpRequestSigner.java b/src/main/java/com/mastercard/developer/signers/SpringHttpRequestSigner.java index e77290e..0745b70 100644 --- a/src/main/java/com/mastercard/developer/signers/SpringHttpRequestSigner.java +++ b/src/main/java/com/mastercard/developer/signers/SpringHttpRequestSigner.java @@ -5,6 +5,7 @@ import java.nio.charset.Charset; import java.security.PrivateKey; +import com.mastercard.developer.oauth.SignatureMethod; import org.springframework.http.HttpMethod; import org.springframework.http.HttpRequest; import org.springframework.http.HttpHeaders; @@ -18,13 +19,17 @@ public class SpringHttpRequestSigner extends AbstractSigner { public SpringHttpRequestSigner(String consumerKey, PrivateKey signingKey) { super(consumerKey, signingKey); } + + public SpringHttpRequestSigner(String consumerKey, PrivateKey signingKey, SignatureMethod signatureMethod) { + super(consumerKey, signingKey, signatureMethod); + } public void sign(HttpRequest request, byte[] bytes) { HttpMethod method = request.getMethod(); HttpHeaders headers = request.getHeaders(); Charset charset = getCharset(headers); String payload = (null == bytes ? null : new String(bytes, charset)); - String authHeader = OAuth.getAuthorizationHeader(request.getURI(), method.toString(), payload, charset, consumerKey, signingKey); + String authHeader = OAuth.getAuthorizationHeader(request.getURI(), method.toString(), payload, charset, consumerKey, signingKey, signatureMethod); headers.add(OAuth.AUTHORIZATION_HEADER_NAME, authHeader); } diff --git a/src/main/java/com/mastercard/developer/signers/SpringWebfluxSigner.java b/src/main/java/com/mastercard/developer/signers/SpringWebfluxSigner.java index 5dcdafe..00595d9 100644 --- a/src/main/java/com/mastercard/developer/signers/SpringWebfluxSigner.java +++ b/src/main/java/com/mastercard/developer/signers/SpringWebfluxSigner.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.mastercard.developer.oauth.OAuth; +import com.mastercard.developer.oauth.SignatureMethod; import org.springframework.http.ReactiveHttpOutputMessage; import org.springframework.web.reactive.function.BodyInserter; import org.springframework.web.reactive.function.BodyInserters; @@ -20,13 +21,17 @@ public SpringWebfluxSigner(String consumerKey, PrivateKey signingKey) { super(consumerKey, signingKey); } + public SpringWebfluxSigner(String consumerKey, PrivateKey signingKey, SignatureMethod signatureMethod) { + super(consumerKey, signingKey, signatureMethod); + } + public ClientRequest sign(ClientRequest request) throws Exception { URI uri = request.url(); String method = request.method().name(); BodyInserterWrapper bodyInserterWrapper = (BodyInserterWrapper) request.body(); String payload = new ObjectMapper().writeValueAsString(bodyInserterWrapper.getBody()); - String authHeader = OAuth.getAuthorizationHeader(uri, method, payload, charset, consumerKey, signingKey); + String authHeader = OAuth.getAuthorizationHeader(uri, method, payload, charset, consumerKey, signingKey, signatureMethod); // Add auth header return Mono.just(ClientRequest.from(request) diff --git a/src/test/java/com/mastercard/developer/interceptors/HttpExecuteOAuth1InterceptorTest.java b/src/test/java/com/mastercard/developer/interceptors/HttpExecuteOAuth1InterceptorTest.java new file mode 100644 index 0000000..e957f15 --- /dev/null +++ b/src/test/java/com/mastercard/developer/interceptors/HttpExecuteOAuth1InterceptorTest.java @@ -0,0 +1,73 @@ +package com.mastercard.developer.interceptors; + +import com.google.api.client.http.HttpRequest; +import com.mastercard.developer.oauth.OAuth; +import com.mastercard.developer.oauth.SignatureMethod; +import com.mastercard.developer.signers.GoogleApiClientSigner; +import com.mastercard.developer.test.TestUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.MockedConstruction; +import org.mockito.Mockito; + +import java.security.PrivateKey; + +class HttpExecuteOAuth1InterceptorTest { + + @Test + void constructor_shouldInstantiateSignerWithDefaultSignatureMethod() throws Exception { + PrivateKey signingKey = TestUtils.getTestSigningKey(); + String consumerKey = "consumer-key"; + MockedConstruction.Context[] capturedContext = new MockedConstruction.Context[1]; + + try (MockedConstruction mocked = Mockito.mockConstruction( + GoogleApiClientSigner.class, + (mock, context) -> capturedContext[0] = context)) { + new HttpExecuteOAuth1Interceptor(consumerKey, signingKey); + + Assertions.assertEquals(1, mocked.constructed().size()); + MockedConstruction.Context context = capturedContext[0]; + Assertions.assertEquals(consumerKey, context.arguments().get(0)); + Assertions.assertEquals(signingKey, context.arguments().get(1)); + Assertions.assertEquals(OAuth.DEFAULT_SIGNATURE_METHOD, context.arguments().get(2)); + } + } + + @ParameterizedTest + @EnumSource(SignatureMethod.class) + void constructor_shouldInstantiateSignerWithGivenSignatureMethod(SignatureMethod signatureMethod) throws Exception { + PrivateKey signingKey = TestUtils.getTestSigningKey(); + String consumerKey = "consumer-key"; + MockedConstruction.Context[] capturedContext = new MockedConstruction.Context[1]; + + try (MockedConstruction mocked = Mockito.mockConstruction( + GoogleApiClientSigner.class, + (mock, context) -> capturedContext[0] = context)) { + new HttpExecuteOAuth1Interceptor(consumerKey, signingKey, signatureMethod); + + Assertions.assertEquals(1, mocked.constructed().size()); + MockedConstruction.Context context = capturedContext[0]; + Assertions.assertEquals(consumerKey, context.arguments().get(0)); + Assertions.assertEquals(signingKey, context.arguments().get(1)); + Assertions.assertEquals(signatureMethod, context.arguments().get(2)); + } + } + + @Test + void intercept_shouldSignRequest() throws Exception { + HttpRequest request = Mockito.mock(HttpRequest.class); + PrivateKey signingKey = TestUtils.getTestSigningKey(); + String consumerKey = "consumer-key"; + + try (MockedConstruction mocked = Mockito.mockConstruction(GoogleApiClientSigner.class)) { + HttpExecuteOAuth1Interceptor instanceUnderTest = new HttpExecuteOAuth1Interceptor(consumerKey, signingKey, SignatureMethod.RSA_SHA256); + GoogleApiClientSigner signerMock = mocked.constructed().get(0); + + instanceUnderTest.intercept(request); + + Mockito.verify(signerMock).sign(request); + } + } +} \ No newline at end of file diff --git a/src/test/java/com/mastercard/developer/interceptors/OkHttp2OAuth1InterceptorTest.java b/src/test/java/com/mastercard/developer/interceptors/OkHttp2OAuth1InterceptorTest.java new file mode 100644 index 0000000..9464ab4 --- /dev/null +++ b/src/test/java/com/mastercard/developer/interceptors/OkHttp2OAuth1InterceptorTest.java @@ -0,0 +1,82 @@ +package com.mastercard.developer.interceptors; + +import com.mastercard.developer.oauth.OAuth; +import com.mastercard.developer.oauth.SignatureMethod; +import com.mastercard.developer.signers.OkHttp2Signer; +import com.mastercard.developer.test.TestUtils; +import com.squareup.okhttp.Interceptor; +import com.squareup.okhttp.Request; +import com.squareup.okhttp.Response; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.MockedConstruction; +import org.mockito.Mockito; + +import java.security.PrivateKey; + +class OkHttp2OAuth1InterceptorTest { + + @Test + void constructor_shouldInstantiateSignerWithDefaultSignatureMethod() throws Exception { + PrivateKey signingKey = TestUtils.getTestSigningKey(); + String consumerKey = "consumer-key"; + MockedConstruction.Context[] capturedContext = new MockedConstruction.Context[1]; + + try (MockedConstruction mocked = Mockito.mockConstruction( + OkHttp2Signer.class, + (mock, context) -> capturedContext[0] = context)) { + new OkHttp2OAuth1Interceptor(consumerKey, signingKey); + + Assertions.assertEquals(1, mocked.constructed().size()); + MockedConstruction.Context context = capturedContext[0]; + Assertions.assertEquals(consumerKey, context.arguments().get(0)); + Assertions.assertEquals(signingKey, context.arguments().get(1)); + Assertions.assertEquals(OAuth.DEFAULT_SIGNATURE_METHOD, context.arguments().get(2)); + } + } + + @ParameterizedTest + @EnumSource(SignatureMethod.class) + void constructor_shouldInstantiateSignerWithGivenSignatureMethod(SignatureMethod signatureMethod) throws Exception { + PrivateKey signingKey = TestUtils.getTestSigningKey(); + String consumerKey = "consumer-key"; + MockedConstruction.Context[] capturedContext = new MockedConstruction.Context[1]; + + try (MockedConstruction mocked = Mockito.mockConstruction( + OkHttp2Signer.class, + (mock, context) -> capturedContext[0] = context)) { + new OkHttp2OAuth1Interceptor(consumerKey, signingKey, signatureMethod); + + Assertions.assertEquals(1, mocked.constructed().size()); + MockedConstruction.Context context = capturedContext[0]; + Assertions.assertEquals(consumerKey, context.arguments().get(0)); + Assertions.assertEquals(signingKey, context.arguments().get(1)); + Assertions.assertEquals(signatureMethod, context.arguments().get(2)); + } + } + + @Test + void intercept_shouldSignRequestAndProceed() throws Exception { + Request request = new Request.Builder().url("https://api.mastercard.com/resource").build(); + Interceptor.Chain chain = Mockito.mock(Interceptor.Chain.class); + Response expectedResponse = Mockito.mock(Response.class); + PrivateKey signingKey = TestUtils.getTestSigningKey(); + String consumerKey = "consumer-key"; + + Mockito.when(chain.request()).thenReturn(request); + Mockito.when(chain.proceed(Mockito.any(Request.class))).thenReturn(expectedResponse); + + try (MockedConstruction mocked = Mockito.mockConstruction(OkHttp2Signer.class)) { + OkHttp2OAuth1Interceptor instanceUnderTest = new OkHttp2OAuth1Interceptor(consumerKey, signingKey, SignatureMethod.RSA_SHA256); + OkHttp2Signer signerMock = mocked.constructed().get(0); + + Response actualResponse = instanceUnderTest.intercept(chain); + + Mockito.verify(signerMock).sign(Mockito.any(Request.Builder.class)); + Mockito.verify(chain).proceed(Mockito.any(Request.class)); + Assertions.assertSame(expectedResponse, actualResponse); + } + } +} diff --git a/src/test/java/com/mastercard/developer/interceptors/OkHttpOAuth1InterceptorTest.java b/src/test/java/com/mastercard/developer/interceptors/OkHttpOAuth1InterceptorTest.java new file mode 100644 index 0000000..f48abe7 --- /dev/null +++ b/src/test/java/com/mastercard/developer/interceptors/OkHttpOAuth1InterceptorTest.java @@ -0,0 +1,82 @@ +package com.mastercard.developer.interceptors; + +import com.mastercard.developer.oauth.OAuth; +import com.mastercard.developer.oauth.SignatureMethod; +import com.mastercard.developer.signers.OkHttpSigner; +import com.mastercard.developer.test.TestUtils; +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.Response; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.MockedConstruction; +import org.mockito.Mockito; + +import java.security.PrivateKey; + +class OkHttpOAuth1InterceptorTest { + + @Test + void constructor_shouldInstantiateSignerWithDefaultSignatureMethod() throws Exception { + PrivateKey signingKey = TestUtils.getTestSigningKey(); + String consumerKey = "consumer-key"; + MockedConstruction.Context[] capturedContext = new MockedConstruction.Context[1]; + + try (MockedConstruction mocked = Mockito.mockConstruction( + OkHttpSigner.class, + (mock, context) -> capturedContext[0] = context)) { + new OkHttpOAuth1Interceptor(consumerKey, signingKey); + + Assertions.assertEquals(1, mocked.constructed().size()); + MockedConstruction.Context context = capturedContext[0]; + Assertions.assertEquals(consumerKey, context.arguments().get(0)); + Assertions.assertEquals(signingKey, context.arguments().get(1)); + Assertions.assertEquals(OAuth.DEFAULT_SIGNATURE_METHOD, context.arguments().get(2)); + } + } + + @ParameterizedTest + @EnumSource(SignatureMethod.class) + void constructor_shouldInstantiateSignerWithGivenSignatureMethod(SignatureMethod signatureMethod) throws Exception { + PrivateKey signingKey = TestUtils.getTestSigningKey(); + String consumerKey = "consumer-key"; + MockedConstruction.Context[] capturedContext = new MockedConstruction.Context[1]; + + try (MockedConstruction mocked = Mockito.mockConstruction( + OkHttpSigner.class, + (mock, context) -> capturedContext[0] = context)) { + new OkHttpOAuth1Interceptor(consumerKey, signingKey, signatureMethod); + + Assertions.assertEquals(1, mocked.constructed().size()); + MockedConstruction.Context context = capturedContext[0]; + Assertions.assertEquals(consumerKey, context.arguments().get(0)); + Assertions.assertEquals(signingKey, context.arguments().get(1)); + Assertions.assertEquals(signatureMethod, context.arguments().get(2)); + } + } + + @Test + void intercept_shouldSignRequestAndProceed() throws Exception { + Request request = new Request.Builder().url("https://api.mastercard.com/resource").build(); + Interceptor.Chain chain = Mockito.mock(Interceptor.Chain.class); + Response expectedResponse = Mockito.mock(Response.class); + PrivateKey signingKey = TestUtils.getTestSigningKey(); + String consumerKey = "consumer-key"; + + Mockito.when(chain.request()).thenReturn(request); + Mockito.when(chain.proceed(Mockito.any(Request.class))).thenReturn(expectedResponse); + + try (MockedConstruction mocked = Mockito.mockConstruction(OkHttpSigner.class)) { + OkHttpOAuth1Interceptor instanceUnderTest = new OkHttpOAuth1Interceptor(consumerKey, signingKey, SignatureMethod.RSA_SHA256); + OkHttpSigner signerMock = mocked.constructed().get(0); + + Response actualResponse = instanceUnderTest.intercept(chain); + + Mockito.verify(signerMock).sign(Mockito.any(Request.Builder.class)); + Mockito.verify(chain).proceed(Mockito.any(Request.class)); + Assertions.assertSame(expectedResponse, actualResponse); + } + } +} diff --git a/src/test/java/com/mastercard/developer/interceptors/OpenFeignOAuth1InterceptorTest.java b/src/test/java/com/mastercard/developer/interceptors/OpenFeignOAuth1InterceptorTest.java new file mode 100644 index 0000000..f11473e --- /dev/null +++ b/src/test/java/com/mastercard/developer/interceptors/OpenFeignOAuth1InterceptorTest.java @@ -0,0 +1,78 @@ +package com.mastercard.developer.interceptors; + +import com.mastercard.developer.oauth.OAuth; +import com.mastercard.developer.oauth.SignatureMethod; +import com.mastercard.developer.signers.OpenFeignSigner; +import com.mastercard.developer.test.TestUtils; +import feign.RequestTemplate; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.MockedConstruction; +import org.mockito.Mockito; + +import java.security.PrivateKey; + +class OpenFeignOAuth1InterceptorTest { + + @Test + void constructor_shouldInstantiateSignerWithDefaultSignatureMethod() throws Exception { + PrivateKey signingKey = TestUtils.getTestSigningKey(); + String consumerKey = "consumer-key"; + String baseUri = "https://api.mastercard.com"; + MockedConstruction.Context[] capturedContext = new MockedConstruction.Context[1]; + + try (MockedConstruction mocked = Mockito.mockConstruction( + OpenFeignSigner.class, + (mock, context) -> capturedContext[0] = context)) { + new OpenFeignOAuth1Interceptor(consumerKey, signingKey, baseUri); + + Assertions.assertEquals(1, mocked.constructed().size()); + MockedConstruction.Context context = capturedContext[0]; + Assertions.assertEquals(consumerKey, context.arguments().get(0)); + Assertions.assertEquals(signingKey, context.arguments().get(1)); + Assertions.assertEquals(baseUri, context.arguments().get(2)); + Assertions.assertEquals(OAuth.DEFAULT_SIGNATURE_METHOD, context.arguments().get(3)); + } + } + + @ParameterizedTest + @EnumSource(SignatureMethod.class) + void constructor_shouldInstantiateSignerWithGivenSignatureMethod(SignatureMethod signatureMethod) throws Exception { + PrivateKey signingKey = TestUtils.getTestSigningKey(); + String consumerKey = "consumer-key"; + String baseUri = "https://api.mastercard.com"; + MockedConstruction.Context[] capturedContext = new MockedConstruction.Context[1]; + + try (MockedConstruction mocked = Mockito.mockConstruction( + OpenFeignSigner.class, + (mock, context) -> capturedContext[0] = context)) { + new OpenFeignOAuth1Interceptor(consumerKey, signingKey, baseUri, signatureMethod); + + Assertions.assertEquals(1, mocked.constructed().size()); + MockedConstruction.Context context = capturedContext[0]; + Assertions.assertEquals(consumerKey, context.arguments().get(0)); + Assertions.assertEquals(signingKey, context.arguments().get(1)); + Assertions.assertEquals(baseUri, context.arguments().get(2)); + Assertions.assertEquals(signatureMethod, context.arguments().get(3)); + } + } + + @Test + void apply_shouldSignRequest() throws Exception { + RequestTemplate requestTemplate = Mockito.mock(RequestTemplate.class); + PrivateKey signingKey = TestUtils.getTestSigningKey(); + String consumerKey = "consumer-key"; + String baseUri = "https://api.mastercard.com"; + + try (MockedConstruction mocked = Mockito.mockConstruction(OpenFeignSigner.class)) { + OpenFeignOAuth1Interceptor instanceUnderTest = new OpenFeignOAuth1Interceptor(consumerKey, signingKey, baseUri, SignatureMethod.RSA_SHA256); + OpenFeignSigner signerMock = mocked.constructed().get(0); + + instanceUnderTest.apply(requestTemplate); + + Mockito.verify(signerMock).sign(requestTemplate); + } + } +} diff --git a/src/test/java/com/mastercard/developer/interceptors/SpringWebfluxOAuth1InterceptorTest.java b/src/test/java/com/mastercard/developer/interceptors/SpringWebfluxOAuth1InterceptorTest.java new file mode 100644 index 0000000..624225f --- /dev/null +++ b/src/test/java/com/mastercard/developer/interceptors/SpringWebfluxOAuth1InterceptorTest.java @@ -0,0 +1,86 @@ +package com.mastercard.developer.interceptors; + +import com.mastercard.developer.oauth.OAuth; +import com.mastercard.developer.oauth.SignatureMethod; +import com.mastercard.developer.signers.SpringWebfluxSigner; +import com.mastercard.developer.test.TestUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.MockedConstruction; +import org.mockito.Mockito; +import org.springframework.http.HttpMethod; +import org.springframework.web.reactive.function.client.ClientRequest; +import org.springframework.web.reactive.function.client.ClientResponse; +import org.springframework.web.reactive.function.client.ExchangeFunction; +import reactor.core.publisher.Mono; + +import java.net.URI; +import java.security.PrivateKey; + +class SpringWebfluxOAuth1InterceptorTest { + + @Test + void constructor_shouldInstantiateSignerWithDefaultSignatureMethod() throws Exception { + PrivateKey signingKey = TestUtils.getTestSigningKey(); + String consumerKey = "consumer-key"; + MockedConstruction.Context[] capturedContext = new MockedConstruction.Context[1]; + + try (MockedConstruction mocked = Mockito.mockConstruction( + SpringWebfluxSigner.class, + (mock, context) -> capturedContext[0] = context)) { + new SpringWebfluxOAuth1Interceptor(consumerKey, signingKey); + + Assertions.assertEquals(1, mocked.constructed().size()); + MockedConstruction.Context context = capturedContext[0]; + Assertions.assertEquals(consumerKey, context.arguments().get(0)); + Assertions.assertEquals(signingKey, context.arguments().get(1)); + Assertions.assertEquals(OAuth.DEFAULT_SIGNATURE_METHOD, context.arguments().get(2)); + } + } + + @ParameterizedTest + @EnumSource(SignatureMethod.class) + void constructor_shouldInstantiateSignerWithGivenSignatureMethod(SignatureMethod signatureMethod) throws Exception { + PrivateKey signingKey = TestUtils.getTestSigningKey(); + String consumerKey = "consumer-key"; + MockedConstruction.Context[] capturedContext = new MockedConstruction.Context[1]; + + try (MockedConstruction mocked = Mockito.mockConstruction( + SpringWebfluxSigner.class, + (mock, context) -> capturedContext[0] = context)) { + new SpringWebfluxOAuth1Interceptor(consumerKey, signingKey, signatureMethod); + + Assertions.assertEquals(1, mocked.constructed().size()); + MockedConstruction.Context context = capturedContext[0]; + Assertions.assertEquals(consumerKey, context.arguments().get(0)); + Assertions.assertEquals(signingKey, context.arguments().get(1)); + Assertions.assertEquals(signatureMethod, context.arguments().get(2)); + } + } + + @Test + void filter_shouldSignRequestAndProceed() throws Exception { + ClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create("https://api.mastercard.com/resource")).build(); + ClientRequest signedRequest = ClientRequest.create(HttpMethod.GET, URI.create("https://api.mastercard.com/resource")).build(); + ExchangeFunction next = Mockito.mock(ExchangeFunction.class); + ClientResponse expectedResponse = Mockito.mock(ClientResponse.class); + PrivateKey signingKey = TestUtils.getTestSigningKey(); + String consumerKey = "consumer-key"; + + Mockito.when(next.exchange(Mockito.any(ClientRequest.class))).thenReturn(Mono.just(expectedResponse)); + + try (MockedConstruction mocked = Mockito.mockConstruction(SpringWebfluxSigner.class)) { + SpringWebfluxOAuth1Interceptor instanceUnderTest = new SpringWebfluxOAuth1Interceptor(consumerKey, signingKey, SignatureMethod.RSA_SHA256); + SpringWebfluxSigner signerMock = mocked.constructed().get(0); + Mockito.when(signerMock.sign(request)).thenReturn(signedRequest); + + ClientResponse actualResponse = instanceUnderTest.filter(request, next).block(); + + Mockito.verify(signerMock).sign(request); + Mockito.verify(next).exchange(signedRequest); + Assertions.assertSame(expectedResponse, actualResponse); + } + } +} diff --git a/src/test/java/com/mastercard/developer/oauth/OAuthTest.java b/src/test/java/com/mastercard/developer/oauth/OAuthTest.java index 14d1f9d..caeea64 100644 --- a/src/test/java/com/mastercard/developer/oauth/OAuthTest.java +++ b/src/test/java/com/mastercard/developer/oauth/OAuthTest.java @@ -5,14 +5,22 @@ import org.junit.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.EnumSource; import java.net.URI; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.security.PrivateKey; +import java.security.Signature; +import java.security.spec.AlgorithmParameterSpec; import java.util.*; +import java.util.Base64; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import static org.mockito.Mockito.CALLS_REAL_METHODS; import static com.mastercard.developer.test.TestUtils.UTF8_CHARSET; import static org.assertj.core.api.Assertions.assertThat; @@ -290,14 +298,110 @@ public void testGetSignatureBaseString_Nominal() { } @Test - public void testSignSignatureBaseString() throws Exception { + public void testSignSignatureBaseString_ShouldReturnExpectedSignature_WhenUsingRsaSha256() throws Exception { String expectedSignatureString = "IJeNKYGfUhFtj5OAPRI92uwfjJJLCej3RCMLbp7R6OIYJhtwxnTkloHQ2bgV7fks4GT/A7rkqrgUGk0ewbwIC6nS3piJHyKVc7rvQXZuCQeeeQpFzLRiH3rsb+ZS+AULK+jzDje4Fb+BQR6XmxuuJmY6YrAKkj13Ln4K6bZJlSxOizbNvt+Htnx+hNd4VgaVBeJKcLhHfZbWQxK76nMnjY7nDcM/2R6LUIR2oLG1L9m55WP3bakAvmOr392ulv1+mWCwDAZZzQ4lakDD2BTu0ZaVsvBW+mcKFxYeTq7SyTQMM4lEwFPJ6RLc8jJJ+veJXHekLVzWg4qHRtzNBLz1mA=="; - assertEquals(expectedSignatureString, OAuth.signSignatureBaseString("baseString", TestUtils.getTestSigningKey(), StandardCharsets.UTF_8)); + assertEquals(expectedSignatureString, OAuth.signSignatureBaseString("baseString", TestUtils.getTestSigningKey(), StandardCharsets.UTF_8, SignatureMethod.RSA_SHA256)); + } + + @ParameterizedTest + @EnumSource(SignatureMethod.class) + public void testGetAuthorizationHeader_ShouldUseProvidedSignatureMethod(SignatureMethod signatureMethod) throws Exception { + URI uri = URI.create("https://example.com/resource?query=value"); + String method = "POST"; + String payload = "{\"foo\":\"bar\"}"; + Charset charset = StandardCharsets.UTF_8; + String consumerKey = "ckey"; + PrivateKey signingKey = TestUtils.getTestSigningKey(); + + try (MockedStatic oauthMock = Mockito.mockStatic(OAuth.class, CALLS_REAL_METHODS)) { + oauthMock.when(OAuth::getNonce).thenReturn("fixed-nonce"); + oauthMock.when(() -> OAuth.signSignatureBaseString(Mockito.anyString(), Mockito.eq(signingKey), Mockito.eq(charset), Mockito.eq(signatureMethod))) + .thenReturn("signed-" + signatureMethod.getOauthName()); + + String header = OAuth.getAuthorizationHeader(uri, method, payload, charset, consumerKey, signingKey, signatureMethod); + + oauthMock.verify(() -> OAuth.signSignatureBaseString(Mockito.anyString(), Mockito.eq(signingKey), Mockito.eq(charset), Mockito.eq(signatureMethod))); + + String[] headerParts = header.substring("OAuth ".length()).split(","); + + String[] expectedParts = new String[] { + "oauth_consumer_key=\"ckey\"", + "oauth_nonce=\"fixed-nonce\"", + "oauth_signature_method=\"" + signatureMethod.getOauthName() + "\"", + "oauth_version=\"1.0\"", + "oauth_body_hash=\"" + OAuth.getBodyHash(payload, charset, HASH_ALGORITHM) + "\"", + "oauth_signature=\"signed-" + signatureMethod.getOauthName() + "\"" + }; + + assertThat(headerParts).hasSize(expectedParts.length + 1); // +1 for timestamp + + assertThat(headerParts).contains(expectedParts); + + String timestampPart = Arrays.stream(headerParts) + .filter(p -> p.startsWith("oauth_timestamp=")).findFirst() + .orElseThrow(() -> new AssertionError("oauth_timestamp not found")); + assertThat(timestampPart).matches("oauth_timestamp=\"\\d{10}\""); + } + } + + @Test + public void testSignSignatureBaseString_ShouldVerify_WhenUsingRsaPssSha256() throws Exception { + String sbs = "baseString"; + + String signature = OAuth.signSignatureBaseString(sbs, TestUtils.getTestSigningKey(), StandardCharsets.UTF_8, SignatureMethod.RSA_PSS_SHA256); + + AlgorithmParameterSpec params = SignatureMethod.RSA_PSS_SHA256.getAlgorithmParams(); + Signature verifier = Signature.getInstance(SignatureMethod.RSA_PSS_SHA256.getJcaName()); + verifier.setParameter(params); + verifier.initVerify(TestUtils.getTestPublicKey()); + verifier.update(sbs.getBytes(StandardCharsets.UTF_8)); + + assertTrue(verifier.verify(Base64.getDecoder().decode(signature))); } @Test(expected = IllegalStateException.class) public void testSignSignatureBaseString_ShouldThrowIllegalStateException_WhenInvalidKey() { - OAuth.signSignatureBaseString("some string", null, StandardCharsets.UTF_8); + OAuth.signSignatureBaseString("some string", null, StandardCharsets.UTF_8, SignatureMethod.RSA_SHA256); + } + + @Test + public void testSignSignatureBaseString_ShouldDelegateToDefaultSignatureMethod() throws Exception { + String sbs = "baseString"; + PrivateKey signingKey = TestUtils.getTestSigningKey(); + + try (MockedStatic oauthMock = Mockito.mockStatic(OAuth.class)) { + oauthMock.when(() -> OAuth.signSignatureBaseString(sbs, signingKey, StandardCharsets.UTF_8, SignatureMethod.RSA_SHA256)) + .thenReturn("expected-signature"); + oauthMock.when(() -> OAuth.signSignatureBaseString(sbs, signingKey, StandardCharsets.UTF_8)) + .thenCallRealMethod(); + + String actual = OAuth.signSignatureBaseString(sbs, signingKey, StandardCharsets.UTF_8); + + assertEquals("expected-signature", actual); + oauthMock.verify(() -> OAuth.signSignatureBaseString(sbs, signingKey, StandardCharsets.UTF_8, SignatureMethod.RSA_SHA256)); + } + } + + @Test + public void testGetAuthorizationHeader_ShouldDelegateToDefaultSignatureMethod() throws Exception { + URI uri = URI.create("https://example.com/resource"); + String method = "GET"; + String payload = "payload"; + Charset charset = StandardCharsets.UTF_8; + String consumerKey = "ckey"; + PrivateKey signingKey = TestUtils.getTestSigningKey(); + + try (MockedStatic oauthMock = Mockito.mockStatic(OAuth.class)) { + oauthMock.when(() -> OAuth.getAuthorizationHeader(uri, method, payload, charset, consumerKey, signingKey, OAuth.DEFAULT_SIGNATURE_METHOD)) + .thenReturn("delegated"); + oauthMock.when(() -> OAuth.getAuthorizationHeader(uri, method, payload, charset, consumerKey, signingKey)) + .thenCallRealMethod(); + + String header = OAuth.getAuthorizationHeader(uri, method, payload, charset, consumerKey, signingKey); + + assertEquals("delegated", header); + oauthMock.verify(() -> OAuth.getAuthorizationHeader(uri, method, payload, charset, consumerKey, signingKey, OAuth.DEFAULT_SIGNATURE_METHOD)); + } } @Test @@ -358,4 +462,5 @@ public void run() { fail("Expected " + randomNonces + " but got " + dupes.size()); } } + } diff --git a/src/test/java/com/mastercard/developer/signers/ApacheHttpClient4SignerTest.java b/src/test/java/com/mastercard/developer/signers/ApacheHttpClient4SignerTest.java index 493dfa3..a425aab 100644 --- a/src/test/java/com/mastercard/developer/signers/ApacheHttpClient4SignerTest.java +++ b/src/test/java/com/mastercard/developer/signers/ApacheHttpClient4SignerTest.java @@ -1,5 +1,7 @@ package com.mastercard.developer.signers; +import com.mastercard.developer.oauth.OAuth; +import com.mastercard.developer.oauth.SignatureMethod; import com.mastercard.developer.test.TestUtils; import org.apache.http.Header; import org.apache.http.client.methods.HttpPost; @@ -7,9 +9,17 @@ import org.apache.http.entity.StringEntity; import org.junit.Assert; import org.junit.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import java.net.URI; +import java.nio.charset.Charset; import java.security.PrivateKey; +import static com.mastercard.developer.test.TestUtils.UTF8_CHARSET; + public class ApacheHttpClient4SignerTest { @Test @@ -30,4 +40,48 @@ public void testSign_ShouldAddOAuth1HeaderToPostRequest() throws Exception { String authorizationHeaderValue = authorizationHeaders[0].getValue(); Assert.assertNotNull(authorizationHeaderValue); } + + @ParameterizedTest + @EnumSource(SignatureMethod.class) + public void testSign_ShouldInvokeSigningAsExpected(SignatureMethod signatureMethod) throws Exception { + + // GIVEN + PrivateKey signingKey = TestUtils.getTestSigningKey(); + String consumerKey = "Some key"; + Charset charset = UTF8_CHARSET; + String payload = "{\"foo\":\"bar\"}"; + + HttpPost httpPost = new HttpPost("https://api.mastercard.com/service"); + httpPost.setEntity(new StringEntity(payload, ContentType.APPLICATION_JSON)); // default UTF-8 + + URI expectedUri = URI.create("https://api.mastercard.com/service"); + + try (MockedStatic oauthMock = Mockito.mockStatic(OAuth.class)) { + oauthMock.when(() -> OAuth.getAuthorizationHeader( + expectedUri, + "POST", + payload, + charset, + consumerKey, + signingKey, + signatureMethod + )).thenReturn("OAuth header"); + + ApacheHttpClient4Signer instanceUnderTest = new ApacheHttpClient4Signer(consumerKey, signingKey, signatureMethod); + + // WHEN + instanceUnderTest.sign(httpPost); + + // THEN + oauthMock.verify(() -> OAuth.getAuthorizationHeader( + expectedUri, + "POST", + payload, + charset, + consumerKey, + signingKey, + signatureMethod + )); + } + } } diff --git a/src/test/java/com/mastercard/developer/signers/GoogleApiClientSignerTest.java b/src/test/java/com/mastercard/developer/signers/GoogleApiClientSignerTest.java index e58648e..6d87a1f 100644 --- a/src/test/java/com/mastercard/developer/signers/GoogleApiClientSignerTest.java +++ b/src/test/java/com/mastercard/developer/signers/GoogleApiClientSignerTest.java @@ -2,16 +2,54 @@ import com.google.api.client.http.*; import com.google.api.client.http.javanet.NetHttpTransport; +import com.mastercard.developer.oauth.OAuth; +import com.mastercard.developer.oauth.SignatureMethod; import com.mastercard.developer.test.TestUtils; import org.junit.Assert; import org.junit.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import java.net.URI; +import java.nio.charset.Charset; import java.security.PrivateKey; import static com.mastercard.developer.test.TestUtils.UTF8_CHARSET; public class GoogleApiClientSignerTest { + @ParameterizedTest + @EnumSource(SignatureMethod.class) + public void testConstructor_WithSignatureMethod_ShouldUseDefaultCharsetAndProvidedSignatureMethod(SignatureMethod signatureMethod) throws Exception { + + PrivateKey signingKey = TestUtils.getTestSigningKey(); + String consumerKey = "Some key"; + + GoogleApiClientSigner instanceUnderTest = new GoogleApiClientSigner(consumerKey, signingKey, signatureMethod); + + Assert.assertEquals(consumerKey, instanceUnderTest.consumerKey); + Assert.assertEquals(signingKey, instanceUnderTest.signingKey); + Assert.assertEquals(Charset.defaultCharset(), instanceUnderTest.charset); + Assert.assertEquals(signatureMethod, instanceUnderTest.signatureMethod); + } + + @Test + public void testConstructor_WithCharset_ShouldUseProvidedCharsetAndDefaultSignatureMethod() throws Exception { + + PrivateKey signingKey = TestUtils.getTestSigningKey(); + String consumerKey = "Some key"; + Charset charset = UTF8_CHARSET; + + GoogleApiClientSigner instanceUnderTest = new GoogleApiClientSigner(charset, consumerKey, signingKey); + + Assert.assertEquals(consumerKey, instanceUnderTest.consumerKey); + Assert.assertEquals(signingKey, instanceUnderTest.signingKey); + Assert.assertEquals(charset, instanceUnderTest.charset); + Assert.assertEquals(OAuth.DEFAULT_SIGNATURE_METHOD, instanceUnderTest.signatureMethod); + } + @Test public void testSign_ShouldAddOAuth1HeaderToPostRequest() throws Exception { @@ -31,4 +69,50 @@ public void testSign_ShouldAddOAuth1HeaderToPostRequest() throws Exception { String authorizationHeaderValue = request.getHeaders().getAuthorization(); Assert.assertNotNull(authorizationHeaderValue); } + + @ParameterizedTest + @EnumSource(SignatureMethod.class) + public void testSign_ShouldInvokeSigningAsExpected(SignatureMethod signatureMethod) throws Exception { + + // GIVEN + PrivateKey signingKey = TestUtils.getTestSigningKey(); + String consumerKey = "Some key"; + Charset charset = UTF8_CHARSET; + String payload = "{\"foo\":\"bar\"}"; + + HttpRequestFactory requestFactory = new NetHttpTransport().createRequestFactory(); + HttpContent httpContent = new ByteArrayContent("application/json; charset=" + charset.name(), payload.getBytes(charset)); + HttpRequest request = requestFactory.buildPostRequest(new GenericUrl("https://api.mastercard.com/service"), httpContent); + request.setRequestMethod("POST"); + + URI expectedUri = URI.create("https://api.mastercard.com/service"); + + try (MockedStatic oauthMock = Mockito.mockStatic(OAuth.class)) { + oauthMock.when(() -> OAuth.getAuthorizationHeader( + expectedUri, + "POST", + payload, + charset, + consumerKey, + signingKey, + signatureMethod + )).thenReturn("OAuth header"); + + GoogleApiClientSigner instanceUnderTest = new GoogleApiClientSigner(charset, consumerKey, signingKey, signatureMethod); + + // WHEN + instanceUnderTest.sign(request); + + // THEN + oauthMock.verify(() -> OAuth.getAuthorizationHeader( + expectedUri, + "POST", + payload, + charset, + consumerKey, + signingKey, + signatureMethod + )); + } + } } diff --git a/src/test/java/com/mastercard/developer/signers/HttpsUrlConnectionSignerTest.java b/src/test/java/com/mastercard/developer/signers/HttpsUrlConnectionSignerTest.java index 8fe7ddf..7dc3e42 100644 --- a/src/test/java/com/mastercard/developer/signers/HttpsUrlConnectionSignerTest.java +++ b/src/test/java/com/mastercard/developer/signers/HttpsUrlConnectionSignerTest.java @@ -1,12 +1,20 @@ package com.mastercard.developer.signers; +import com.mastercard.developer.oauth.OAuth; +import com.mastercard.developer.oauth.SignatureMethod; import com.mastercard.developer.test.TestUtils; import org.junit.Assert; import org.junit.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.MockedStatic; +import org.mockito.Mockito; import javax.net.ssl.HttpsURLConnection; import java.net.URL; +import java.net.URI; import java.security.PrivateKey; +import java.nio.charset.Charset; import static com.mastercard.developer.test.TestUtils.UTF8_CHARSET; @@ -31,4 +39,50 @@ public void testSign_ShouldAddOAuth1HeaderToPostRequest() throws Exception { String authorizationHeaderValue = connection.getRequestProperty("Authorization"); Assert.assertNull(authorizationHeaderValue); // https://stackoverflow.com/questions/2864062/getrequestpropertyauthorization-always-returns-null } + + @ParameterizedTest + @EnumSource(SignatureMethod.class) + public void testSign_ShouldInvokeSigningAsExpected(SignatureMethod signatureMethod) throws Exception { + + // GIVEN + PrivateKey signingKey = TestUtils.getTestSigningKey(); + String consumerKey = "Some key"; + Charset charset = UTF8_CHARSET; + String payload = "{\"foo\":\"bar\"}"; + + URL url = new URL("https://api.mastercard.com/service"); + HttpsURLConnection connection = Mockito.mock(HttpsURLConnection.class); + Mockito.when(connection.getURL()).thenReturn(url); + Mockito.when(connection.getRequestMethod()).thenReturn("POST"); + + URI expectedUri = URI.create("https://api.mastercard.com/service"); + + try (MockedStatic oauthMock = Mockito.mockStatic(OAuth.class)) { + oauthMock.when(() -> OAuth.getAuthorizationHeader( + expectedUri, + "POST", + payload, + charset, + consumerKey, + signingKey, + signatureMethod + )).thenReturn("OAuth header"); + + HttpsUrlConnectionSigner instanceUnderTest = new HttpsUrlConnectionSigner(charset, consumerKey, signingKey, signatureMethod); + + // WHEN + instanceUnderTest.sign(connection, payload); + + // THEN + oauthMock.verify(() -> OAuth.getAuthorizationHeader( + expectedUri, + "POST", + payload, + charset, + consumerKey, + signingKey, + signatureMethod + )); + } + } } diff --git a/src/test/java/com/mastercard/developer/signers/OkHttp2SignerTest.java b/src/test/java/com/mastercard/developer/signers/OkHttp2SignerTest.java index 9616e90..3cc623f 100644 --- a/src/test/java/com/mastercard/developer/signers/OkHttp2SignerTest.java +++ b/src/test/java/com/mastercard/developer/signers/OkHttp2SignerTest.java @@ -1,10 +1,19 @@ package com.mastercard.developer.signers; +import com.mastercard.developer.oauth.OAuth; +import com.mastercard.developer.oauth.SignatureMethod; import com.squareup.okhttp.*; import com.squareup.okhttp.Request.Builder; import org.junit.Assert; import org.junit.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import java.net.URI; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.security.PrivateKey; import static com.mastercard.developer.test.TestUtils.UTF8_CHARSET; @@ -12,6 +21,36 @@ public class OkHttp2SignerTest { + @ParameterizedTest + @EnumSource(SignatureMethod.class) + public void testConstructor_WithSignatureMethod_ShouldUseUtf8CharsetAndProvidedSignatureMethod(SignatureMethod signatureMethod) throws Exception { + + PrivateKey signingKey = getTestSigningKey(); + String consumerKey = "Some key"; + + OkHttp2Signer instanceUnderTest = new OkHttp2Signer(consumerKey, signingKey, signatureMethod); + + Assert.assertEquals(consumerKey, instanceUnderTest.consumerKey); + Assert.assertEquals(signingKey, instanceUnderTest.signingKey); + Assert.assertEquals(StandardCharsets.UTF_8, instanceUnderTest.charset); + Assert.assertEquals(signatureMethod, instanceUnderTest.signatureMethod); + } + + @Test + public void testConstructor_WithCharset_ShouldUseProvidedCharsetAndDefaultSignatureMethod() throws Exception { + + PrivateKey signingKey = getTestSigningKey(); + String consumerKey = "Some key"; + Charset charset = UTF8_CHARSET; + + OkHttp2Signer instanceUnderTest = new OkHttp2Signer(charset, consumerKey, signingKey); + + Assert.assertEquals(consumerKey, instanceUnderTest.consumerKey); + Assert.assertEquals(signingKey, instanceUnderTest.signingKey); + Assert.assertEquals(charset, instanceUnderTest.charset); + Assert.assertEquals(OAuth.DEFAULT_SIGNATURE_METHOD, instanceUnderTest.signatureMethod); + } + @Test public void testSign_ShouldAddOAuth1HeaderToPostRequest() throws Exception { @@ -33,4 +72,50 @@ public void testSign_ShouldAddOAuth1HeaderToPostRequest() throws Exception { String authorizationHeaderValue = request.header("Authorization"); Assert.assertNotNull(authorizationHeaderValue); } + + @ParameterizedTest + @EnumSource(SignatureMethod.class) + public void testSign_ShouldInvokeSigningAsExpected(SignatureMethod signatureMethod) throws Exception { + + // GIVEN + PrivateKey signingKey = getTestSigningKey(); + String consumerKey = "Some key"; + Charset charset = UTF8_CHARSET; + String payload = "{\"foo\":\"bar\"}"; + MediaType jsonMediaType = MediaType.parse("application/json; charset=" + charset.name()); + RequestBody body = RequestBody.create(jsonMediaType, payload); + Builder requestBuilder = new Builder() + .url("https://api.mastercard.com/service") + .post(body); + + URI expectedUri = URI.create("https://api.mastercard.com/service"); + + try (MockedStatic oauthMock = Mockito.mockStatic(OAuth.class)) { + oauthMock.when(() -> OAuth.getAuthorizationHeader( + expectedUri, + "POST", + payload, + charset, + consumerKey, + signingKey, + signatureMethod + )).thenReturn("OAuth header"); + + OkHttp2Signer instanceUnderTest = new OkHttp2Signer(charset, consumerKey, signingKey, signatureMethod); + + // WHEN + instanceUnderTest.sign(requestBuilder); + + // THEN + oauthMock.verify(() -> OAuth.getAuthorizationHeader( + expectedUri, + "POST", + payload, + charset, + consumerKey, + signingKey, + signatureMethod + )); + } + } } diff --git a/src/test/java/com/mastercard/developer/signers/OkHttpSignerTest.java b/src/test/java/com/mastercard/developer/signers/OkHttpSignerTest.java index 2573842..cbbd6c3 100644 --- a/src/test/java/com/mastercard/developer/signers/OkHttpSignerTest.java +++ b/src/test/java/com/mastercard/developer/signers/OkHttpSignerTest.java @@ -1,11 +1,20 @@ package com.mastercard.developer.signers; +import com.mastercard.developer.oauth.OAuth; +import com.mastercard.developer.oauth.SignatureMethod; import okhttp3.MediaType; import okhttp3.Request; import okhttp3.RequestBody; import org.junit.Assert; import org.junit.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import java.net.URI; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.security.PrivateKey; import static com.mastercard.developer.test.TestUtils.UTF8_CHARSET; @@ -14,6 +23,36 @@ public class OkHttpSignerTest { + @ParameterizedTest + @EnumSource(SignatureMethod.class) + public void testConstructor_WithSignatureMethod_ShouldUseUtf8CharsetAndProvidedSignatureMethod(SignatureMethod signatureMethod) throws Exception { + + PrivateKey signingKey = getTestSigningKey(); + String consumerKey = "Some key"; + + OkHttpSigner instanceUnderTest = new OkHttpSigner(consumerKey, signingKey, signatureMethod); + + Assert.assertEquals(consumerKey, instanceUnderTest.consumerKey); + Assert.assertEquals(signingKey, instanceUnderTest.signingKey); + Assert.assertEquals(StandardCharsets.UTF_8, instanceUnderTest.charset); + Assert.assertEquals(signatureMethod, instanceUnderTest.signatureMethod); + } + + @Test + public void testConstructor_WithCharset_ShouldUseProvidedCharsetAndDefaultSignatureMethod() throws Exception { + + PrivateKey signingKey = getTestSigningKey(); + String consumerKey = "Some key"; + Charset charset = UTF8_CHARSET; + + OkHttpSigner instanceUnderTest = new OkHttpSigner(charset, consumerKey, signingKey); + + Assert.assertEquals(consumerKey, instanceUnderTest.consumerKey); + Assert.assertEquals(signingKey, instanceUnderTest.signingKey); + Assert.assertEquals(charset, instanceUnderTest.charset); + Assert.assertEquals(OAuth.DEFAULT_SIGNATURE_METHOD, instanceUnderTest.signatureMethod); + } + @Test public void testSign_ShouldAddOAuth1HeaderToPostRequest() throws Exception { @@ -35,4 +74,50 @@ public void testSign_ShouldAddOAuth1HeaderToPostRequest() throws Exception { String authorizationHeaderValue = request.header("Authorization"); Assert.assertNotNull(authorizationHeaderValue); } + + @ParameterizedTest + @EnumSource(SignatureMethod.class) + public void testSign_ShouldInvokeSigningAsExpected(SignatureMethod signatureMethod) throws Exception { + + // GIVEN + PrivateKey signingKey = getTestSigningKey(); + String consumerKey = "Some key"; + Charset charset = UTF8_CHARSET; + String payload = "{\"foo\":\"bar\"}"; + MediaType jsonMediaType = MediaType.parse("application/json; charset=" + charset.name()); + RequestBody body = RequestBody.create(jsonMediaType, payload); + Builder requestBuilder = new Builder() + .url("https://api.mastercard.com/service") + .post(body); + + URI expectedUri = URI.create("https://api.mastercard.com/service"); + + try (MockedStatic oauthMock = Mockito.mockStatic(OAuth.class)) { + oauthMock.when(() -> OAuth.getAuthorizationHeader( + expectedUri, + "POST", + payload, + charset, + consumerKey, + signingKey, + signatureMethod + )).thenReturn("OAuth header"); + + OkHttpSigner instanceUnderTest = new OkHttpSigner(charset, consumerKey, signingKey, signatureMethod); + + // WHEN + instanceUnderTest.sign(requestBuilder); + + // THEN + oauthMock.verify(() -> OAuth.getAuthorizationHeader( + expectedUri, + "POST", + payload, + charset, + consumerKey, + signingKey, + signatureMethod + )); + } + } } diff --git a/src/test/java/com/mastercard/developer/signers/OpenFeignSignerTest.java b/src/test/java/com/mastercard/developer/signers/OpenFeignSignerTest.java index 49cc195..05f19c7 100644 --- a/src/test/java/com/mastercard/developer/signers/OpenFeignSignerTest.java +++ b/src/test/java/com/mastercard/developer/signers/OpenFeignSignerTest.java @@ -1,9 +1,18 @@ package com.mastercard.developer.signers; +import com.mastercard.developer.oauth.OAuth; +import com.mastercard.developer.oauth.SignatureMethod; import feign.RequestTemplate; import org.junit.Assert; import org.junit.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import java.lang.reflect.Field; +import java.net.URI; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.PrivateKey; import java.util.Collection; @@ -12,6 +21,26 @@ public class OpenFeignSignerTest { + @ParameterizedTest + @EnumSource(SignatureMethod.class) + public void testConstructor_WithSignatureMethod_ShouldUseDefaultCharsetAndProvidedValues(SignatureMethod signatureMethod) throws Exception { + + PrivateKey signingKey = getTestSigningKey(); + String consumerKey = "Some key"; + String baseUri = "https://api.mastercard.com/"; + + OpenFeignSigner instanceUnderTest = new OpenFeignSigner(consumerKey, signingKey, baseUri, signatureMethod); + + Assert.assertEquals(consumerKey, instanceUnderTest.consumerKey); + Assert.assertEquals(signingKey, instanceUnderTest.signingKey); + Assert.assertEquals(Charset.defaultCharset(), instanceUnderTest.charset); + Assert.assertEquals(signatureMethod, instanceUnderTest.signatureMethod); + + Field baseUriField = OpenFeignSigner.class.getDeclaredField("baseUri"); + baseUriField.setAccessible(true); + Assert.assertEquals(baseUri, baseUriField.get(instanceUnderTest)); + } + @Test public void testSign_ShouldAddOAuth1HeaderToPostRequest() throws Exception { @@ -55,4 +84,54 @@ public void testSign_ShouldAddOAuth1HeaderToGetRequest() throws Exception { String authorizationHeaderValue = (String)authorizationHeaders.toArray()[0]; Assert.assertNotNull(authorizationHeaderValue); } + + @ParameterizedTest + @EnumSource(SignatureMethod.class) + public void testSign_ShouldInvokeSigningAsExpected(SignatureMethod signatureMethod) throws Exception { + + // GIVEN + PrivateKey signingKey = getTestSigningKey(); + String consumerKey = "Some key"; + Charset charset = StandardCharsets.UTF_8; + String baseUri = "https://api.mastercard.com/"; + RequestTemplate requestTemplate = new RequestTemplate(); + requestTemplate.method("POST"); + requestTemplate.append("/service"); + String payload = "{\"foo\":\"bar\"}"; + requestTemplate.body(payload); + + URI expectedUri = URI.create("https://api.mastercard.com/service"); + + try (MockedStatic oauthMock = Mockito.mockStatic(OAuth.class)) { + oauthMock.when(() -> OAuth.getAuthorizationHeader( + expectedUri, + "POST", + payload, + charset, + consumerKey, + signingKey, + signatureMethod + )).thenReturn("OAuth header"); + + OpenFeignSigner instanceUnderTest = new OpenFeignSigner(charset, + consumerKey, + signingKey, + baseUri, + signatureMethod); + + // WHEN + instanceUnderTest.sign(requestTemplate); + + // THEN + oauthMock.verify(() -> OAuth.getAuthorizationHeader( + expectedUri, + "POST", + payload, + charset, + consumerKey, + signingKey, + signatureMethod + )); + } + } } diff --git a/src/test/java/com/mastercard/developer/signers/SpringHttpRequestSignerTest.java b/src/test/java/com/mastercard/developer/signers/SpringHttpRequestSignerTest.java index 3cf58c7..ebe6653 100644 --- a/src/test/java/com/mastercard/developer/signers/SpringHttpRequestSignerTest.java +++ b/src/test/java/com/mastercard/developer/signers/SpringHttpRequestSignerTest.java @@ -1,13 +1,21 @@ package com.mastercard.developer.signers; +import com.mastercard.developer.oauth.OAuth; +import com.mastercard.developer.oauth.SignatureMethod; import com.mastercard.developer.test.TestUtils; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + import org.springframework.http.HttpRequest; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import java.net.URI; +import java.nio.charset.Charset; import org.junit.Assert; import org.junit.Before; @@ -16,6 +24,8 @@ import java.security.PrivateKey; import java.util.Map; +import static com.mastercard.developer.test.TestUtils.UTF8_CHARSET; + public class SpringHttpRequestSignerTest { private static final HttpMethod POST_METHOD = HttpMethod.POST; @@ -165,4 +175,69 @@ public HttpHeaders getHeaders(){ String authorizationHeaderValue = headers.getFirst(HttpHeaders.AUTHORIZATION); Assert.assertNotNull(authorizationHeaderValue); } + + @ParameterizedTest + @EnumSource(SignatureMethod.class) + public void testSignShouldInvokeSigningAsExpected(SignatureMethod signatureMethod) throws Exception { + + // GIVEN + PrivateKey signingKey = TestUtils.getTestSigningKey(); + String consumerKey = DEFAULT_CONSUMER_KEY; + Charset charset = UTF8_CHARSET; + String payload = DEFAULT_BODY; + + HttpHeaders localHeaders = new HttpHeaders(); + localHeaders.setContentType(new MediaType("application", "json", charset)); + URI expectedUri = new URI("https://api.mastercard.com/service"); + + HttpRequest localRequest = new HttpRequest() { + @Override + public HttpMethod getMethod() { + return POST_METHOD; + } + + @Override + public URI getURI() { + return expectedUri; + } + + @Override + public Map getAttributes() { + return Map.of(); + } + + @Override + public HttpHeaders getHeaders() { + return localHeaders; + } + }; + + try (MockedStatic oauthMock = Mockito.mockStatic(OAuth.class)) { + oauthMock.when(() -> OAuth.getAuthorizationHeader( + expectedUri, + "POST", + payload, + charset, + consumerKey, + signingKey, + signatureMethod + )).thenReturn("OAuth header"); + + SpringHttpRequestSigner instanceUnderTest = new SpringHttpRequestSigner(consumerKey, signingKey, signatureMethod); + + // WHEN + instanceUnderTest.sign(localRequest, payload.getBytes(charset)); + + // THEN + oauthMock.verify(() -> OAuth.getAuthorizationHeader( + expectedUri, + "POST", + payload, + charset, + consumerKey, + signingKey, + signatureMethod + )); + } + } } diff --git a/src/test/java/com/mastercard/developer/signers/SpringWebfluxSignerTest.java b/src/test/java/com/mastercard/developer/signers/SpringWebfluxSignerTest.java index 921f10c..11bfc4e 100644 --- a/src/test/java/com/mastercard/developer/signers/SpringWebfluxSignerTest.java +++ b/src/test/java/com/mastercard/developer/signers/SpringWebfluxSignerTest.java @@ -1,9 +1,16 @@ package com.mastercard.developer.signers; +import com.mastercard.developer.oauth.OAuth; +import com.mastercard.developer.oauth.SignatureMethod; +import com.fasterxml.jackson.databind.ObjectMapper; import com.squareup.okhttp.MediaType; import com.squareup.okhttp.RequestBody; import org.junit.Assert; import org.junit.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.MockedStatic; +import org.mockito.Mockito; import org.springframework.http.HttpMethod; import org.springframework.http.ReactiveHttpOutputMessage; import org.springframework.web.reactive.function.BodyInserter; @@ -11,7 +18,9 @@ import org.springframework.web.reactive.function.client.ClientRequest; import java.net.URI; +import java.nio.charset.Charset; import java.security.PrivateKey; +import java.util.Map; import static com.mastercard.developer.test.TestUtils.UTF8_CHARSET; import static com.mastercard.developer.test.TestUtils.getTestSigningKey; @@ -35,4 +44,48 @@ public void testSign_ShouldAddOAuth1HeaderToPostRequest() throws Exception { String authorizationHeaderValue = signedRequest.headers().getFirst("Authorization"); Assert.assertNotNull(authorizationHeaderValue); } + + @ParameterizedTest + @EnumSource(SignatureMethod.class) + public void testSign_ShouldInvokeSigningAsExpected(SignatureMethod signatureMethod) throws Exception { + + // GIVEN + PrivateKey signingKey = getTestSigningKey(); + String consumerKey = "Some key"; + Charset charset = UTF8_CHARSET; + Map payload = Map.of("foo", "bar"); + String serializedPayload = new ObjectMapper().writeValueAsString(payload); + + URI expectedUri = URI.create("https://api.mastercard.com/service"); + BodyInserterWrapper bodyWrapper = new BodyInserterWrapper(payload); + ClientRequest request = ClientRequest.create(HttpMethod.POST, expectedUri).body(bodyWrapper).build(); + + try (MockedStatic oauthMock = Mockito.mockStatic(OAuth.class)) { + oauthMock.when(() -> OAuth.getAuthorizationHeader( + expectedUri, + "POST", + serializedPayload, + charset, + consumerKey, + signingKey, + signatureMethod + )).thenReturn("OAuth header"); + + SpringWebfluxSigner instanceUnderTest = new SpringWebfluxSigner(consumerKey, signingKey, signatureMethod); + + // WHEN + ClientRequest signedRequest = instanceUnderTest.sign(request); + + // THEN + oauthMock.verify(() -> OAuth.getAuthorizationHeader( + expectedUri, + "POST", + serializedPayload, + charset, + consumerKey, + signingKey, + signatureMethod + )); + } + } } \ No newline at end of file diff --git a/src/test/java/com/mastercard/developer/test/TestUtils.java b/src/test/java/com/mastercard/developer/test/TestUtils.java index c773a5f..8c815bf 100644 --- a/src/test/java/com/mastercard/developer/test/TestUtils.java +++ b/src/test/java/com/mastercard/developer/test/TestUtils.java @@ -1,8 +1,17 @@ package com.mastercard.developer.test; +import java.io.FileInputStream; +import java.io.InputStream; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.security.KeyStore; import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.io.IOException; import static com.mastercard.developer.utils.AuthenticationUtils.loadSigningKey; @@ -12,8 +21,20 @@ private TestUtils() { } public static final Charset UTF8_CHARSET = StandardCharsets.UTF_8; + private static final String TEST_KEYSTORE_PATH = "./src/test/resources/test_key_container.p12"; + private static final String TEST_KEY_ALIAS = "mykeyalias"; + private static final String TEST_KEY_PASSWORD = "Password1"; public static PrivateKey getTestSigningKey() throws Exception { - return loadSigningKey("./src/test/resources/test_key_container.p12", "mykeyalias", "Password1"); + return loadSigningKey(TEST_KEYSTORE_PATH, TEST_KEY_ALIAS, TEST_KEY_PASSWORD); + } + + public static PublicKey getTestPublicKey() throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException { + KeyStore keyStore = KeyStore.getInstance("PKCS12"); + try (InputStream keyStream = new FileInputStream(TEST_KEYSTORE_PATH)) { + keyStore.load(keyStream, TEST_KEY_PASSWORD.toCharArray()); + } + Certificate certificate = keyStore.getCertificate(TEST_KEY_ALIAS); + return certificate.getPublicKey(); } }