Skip to content

Commit 898f9c3

Browse files
authored
Add AssumeRoleWithCertificate credential provider. (#1239)
Signed-off-by: Bala.FA <bala@minio.io>
1 parent ca1e8b0 commit 898f9c3

File tree

3 files changed

+234
-0
lines changed

3 files changed

+234
-0
lines changed

api/src/main/java/io/minio/credentials/AssumeRoleBaseProvider.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@
1919
import io.minio.Xml;
2020
import io.minio.errors.XmlParserException;
2121
import java.io.IOException;
22+
import java.security.GeneralSecurityException;
2223
import java.security.ProviderException;
2324
import java.util.Arrays;
2425
import java.util.concurrent.TimeUnit;
26+
import javax.net.ssl.SSLSocketFactory;
27+
import javax.net.ssl.X509TrustManager;
2528
import okhttp3.HttpUrl;
2629
import okhttp3.OkHttpClient;
2730
import okhttp3.Protocol;
@@ -43,6 +46,35 @@ public AssumeRoleBaseProvider(OkHttpClient customHttpClient) {
4346
: new OkHttpClient().newBuilder().protocols(Arrays.asList(Protocol.HTTP_1_1)).build();
4447
}
4548

49+
public AssumeRoleBaseProvider(
50+
OkHttpClient customHttpClient,
51+
SSLSocketFactory sslSocketFactory,
52+
X509TrustManager trustManager)
53+
throws GeneralSecurityException, IOException {
54+
if (customHttpClient != null) {
55+
if (sslSocketFactory != null || trustManager != null) {
56+
throw new IllegalArgumentException(
57+
"Either sslSocketFactory/trustManager or custom HTTP client must be provided");
58+
}
59+
60+
this.httpClient = customHttpClient;
61+
} else {
62+
if (sslSocketFactory == null || trustManager == null) {
63+
throw new IllegalArgumentException(
64+
"Both sslSocketFactory and trustManager must be provided");
65+
}
66+
67+
// HTTP/1.1 is only supported in default client because of HTTP/2 in OkHttpClient cause 5
68+
// minutes timeout on program exit.
69+
this.httpClient =
70+
new OkHttpClient()
71+
.newBuilder()
72+
.protocols(Arrays.asList(Protocol.HTTP_1_1))
73+
.sslSocketFactory(sslSocketFactory, trustManager)
74+
.build();
75+
}
76+
}
77+
4678
@Override
4779
public synchronized Credentials fetch() {
4880
if (credentials != null && !credentials.isExpired()) {
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2021 MinIO, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.minio.credentials;
18+
19+
import java.io.IOException;
20+
import java.security.GeneralSecurityException;
21+
import java.util.Objects;
22+
import javax.annotation.Nonnull;
23+
import javax.annotation.Nullable;
24+
import javax.net.ssl.SSLSocketFactory;
25+
import javax.net.ssl.X509TrustManager;
26+
import okhttp3.HttpUrl;
27+
import okhttp3.MediaType;
28+
import okhttp3.OkHttpClient;
29+
import okhttp3.Request;
30+
import okhttp3.RequestBody;
31+
import org.simpleframework.xml.Element;
32+
import org.simpleframework.xml.Namespace;
33+
import org.simpleframework.xml.Path;
34+
import org.simpleframework.xml.Root;
35+
36+
/**
37+
* Credential provider using <a
38+
* href="https://github.com/minio/minio/blob/master/docs/sts/tls.md">AssumeRoleWithCertificate
39+
* API</a>.
40+
*/
41+
public class CertificateIdentityProvider extends AssumeRoleBaseProvider {
42+
private static final RequestBody EMPTY_BODY =
43+
RequestBody.create(new byte[] {}, MediaType.parse("application/octet-stream"));
44+
private final Request request;
45+
46+
public CertificateIdentityProvider(
47+
@Nonnull String stsEndpoint,
48+
@Nullable SSLSocketFactory sslSocketFactory,
49+
@Nullable X509TrustManager trustManager,
50+
@Nullable Integer durationSeconds,
51+
@Nullable OkHttpClient customHttpClient)
52+
throws GeneralSecurityException, IOException {
53+
super(customHttpClient, sslSocketFactory, trustManager);
54+
stsEndpoint = Objects.requireNonNull(stsEndpoint, "STS endpoint cannot be empty");
55+
HttpUrl url = Objects.requireNonNull(HttpUrl.parse(stsEndpoint), "Invalid STS endpoint");
56+
if (!url.isHttps()) {
57+
throw new IllegalArgumentException("STS endpoint scheme must be HTTPS");
58+
}
59+
60+
HttpUrl.Builder urlBuilder =
61+
newUrlBuilder(
62+
url,
63+
"AssumeRoleWithCertificate",
64+
getValidDurationSeconds(durationSeconds),
65+
null,
66+
null,
67+
null);
68+
url = urlBuilder.build();
69+
this.request = new Request.Builder().url(url).method("POST", EMPTY_BODY).build();
70+
}
71+
72+
@Override
73+
protected Request getRequest() {
74+
return this.request;
75+
}
76+
77+
@Override
78+
protected Class<? extends AssumeRoleBaseProvider.Response> getResponseClass() {
79+
return CertificateIdentityResponse.class;
80+
}
81+
82+
/** Object representation of response XML of AssumeRoleWithCertificate API. */
83+
@Root(name = "AssumeRoleWithCertificateResponse", strict = false)
84+
@Namespace(reference = "https://sts.amazonaws.com/doc/2011-06-15/")
85+
public static class CertificateIdentityResponse implements AssumeRoleBaseProvider.Response {
86+
@Path(value = "AssumeRoleWithCertificateResult")
87+
@Element(name = "Credentials")
88+
private Credentials credentials;
89+
90+
public Credentials getCredentials() {
91+
return credentials;
92+
}
93+
}
94+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2021 MinIO, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import io.minio.MinioClient;
18+
import io.minio.StatObjectArgs;
19+
import io.minio.StatObjectResponse;
20+
import io.minio.credentials.CertificateIdentityProvider;
21+
import io.minio.credentials.Provider;
22+
import javax.net.ssl.SSLSocketFactory;
23+
import javax.net.ssl.X509TrustManager;
24+
25+
public class MinioClientWithCertificateIdentityProvider {
26+
public static void main(String[] args) throws Exception {
27+
// STS endpoint usually point to MinIO server.
28+
String stsEndpoint = "https://STS-HOST:STS-PORT/";
29+
30+
// SSL socket factory.
31+
SSLSocketFactory sslSocketFactory = null;
32+
33+
// Trust manager.
34+
X509TrustManager trustManager = null;
35+
36+
// Below is a sample code to construct sslSocketFactory and trustManager for self-signed
37+
// certificates (server and client) used in a MinIO server setup.
38+
//
39+
// CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
40+
//
41+
// Certificate serverCertificate = null;
42+
// try (FileInputStream fis = new FileInputStream("/home/bala/.minio/certs/public.crt")) {
43+
// serverCertificate = certificateFactory.generateCertificate(fis);
44+
// }
45+
//
46+
// KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
47+
// trustStore.load(null, "secret".toCharArray());
48+
//
49+
// trustStore.setCertificateEntry("server-certificate", serverCertificate);
50+
//
51+
// String privateKeyString =
52+
// new String(
53+
// Files.readAllBytes(Paths.get("/home/bala/.minio/certs/CAs/client1.key")),
54+
// Charset.defaultCharset())
55+
// .replace("-----BEGIN PRIVATE KEY-----", "")
56+
// .replaceAll(System.lineSeparator(), "")
57+
// .replace("-----END PRIVATE KEY-----", "");
58+
//
59+
// byte[] privateKey = Base64.getDecoder().decode(privateKeyString);
60+
// KeyFactory keyFactory = KeyFactory.getInstance("RSA");
61+
// PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
62+
//
63+
// Certificate certificateChain = null;
64+
// try (FileInputStream fis = new FileInputStream("/home/bala/.minio/certs/CAs/client1.crt")) {
65+
// certificateChain = certificateFactory.generateCertificate(fis);
66+
// }
67+
//
68+
// KeyStore identityStore = KeyStore.getInstance(KeyStore.getDefaultType());
69+
// identityStore.load(null, "secret".toCharArray());
70+
// identityStore.setKeyEntry(
71+
// "client",
72+
// keyFactory.generatePrivate(keySpec),
73+
// "secret".toCharArray(),
74+
// new Certificate[] {certificateChain});
75+
//
76+
// TrustManagerFactory trustManagerFactory =
77+
// TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
78+
// trustManagerFactory.init(trustStore);
79+
// TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
80+
//
81+
// KeyManagerFactory keyManagerFactory =
82+
// KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
83+
// keyManagerFactory.init(identityStore, "secret".toCharArray());
84+
// KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
85+
//
86+
// SSLContext sslContext = SSLContext.getInstance("TLS");
87+
// sslContext.init(keyManagers, trustManagers, null);
88+
//
89+
// SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
90+
// X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
91+
//
92+
93+
Provider provider =
94+
new CertificateIdentityProvider(stsEndpoint, sslSocketFactory, trustManager, null, null);
95+
96+
MinioClient minioClient =
97+
MinioClient.builder()
98+
.endpoint("https://MINIO-HOST:MINIO-PORT")
99+
.credentialsProvider(provider)
100+
.build();
101+
102+
// Get information of an object.
103+
StatObjectResponse stat =
104+
minioClient.statObject(
105+
StatObjectArgs.builder().bucket("my-bucketname").object("my-objectname").build());
106+
System.out.println(stat);
107+
}
108+
}

0 commit comments

Comments
 (0)