Skip to content

Commit 5e5d226

Browse files
committed
Introduce PemSslStore as an alternative to PemSslStoreDetails.
Add a `PemSslStore` interface that can be used as an alternative to `PemSslStoreDetails` when PEM content has already been loaded and parsed. Closes gh-38175
1 parent 2b39ec6 commit 5e5d226

File tree

6 files changed

+453
-69
lines changed

6 files changed

+453
-69
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright 2012-2023 the original author or authors.
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 org.springframework.boot.ssl.pem;
18+
19+
import java.io.IOException;
20+
import java.security.PrivateKey;
21+
import java.security.cert.X509Certificate;
22+
import java.util.List;
23+
24+
import org.springframework.util.Assert;
25+
import org.springframework.util.CollectionUtils;
26+
27+
/**
28+
* {@link PemSslStore} loaded from {@link PemSslStoreDetails}.
29+
*
30+
* @author Phillip Webb
31+
* @see PemSslStore#load(PemSslStoreDetails)
32+
*/
33+
final class LoadedPemSslStore implements PemSslStore {
34+
35+
private final PemSslStoreDetails details;
36+
37+
private final List<X509Certificate> certificates;
38+
39+
private final PrivateKey privateKey;
40+
41+
LoadedPemSslStore(PemSslStoreDetails details) throws IOException {
42+
Assert.notNull(details, "Details must not be null");
43+
this.details = details;
44+
this.certificates = loadCertificates(details);
45+
this.privateKey = loadPrivateKey(details);
46+
}
47+
48+
private static List<X509Certificate> loadCertificates(PemSslStoreDetails details) throws IOException {
49+
PemContent pemContent = PemContent.load(details.certificates());
50+
if (pemContent == null) {
51+
return null;
52+
}
53+
List<X509Certificate> certificates = pemContent.getCertificates();
54+
Assert.state(!CollectionUtils.isEmpty(certificates), "Loaded certificates are empty");
55+
return certificates;
56+
}
57+
58+
private static PrivateKey loadPrivateKey(PemSslStoreDetails details) throws IOException {
59+
PemContent pemContent = PemContent.load(details.privateKey());
60+
return (pemContent != null) ? pemContent.getPrivateKey(details.privateKeyPassword()) : null;
61+
}
62+
63+
@Override
64+
public String type() {
65+
return this.details.type();
66+
}
67+
68+
@Override
69+
public String alias() {
70+
return this.details.alias();
71+
}
72+
73+
@Override
74+
public String password() {
75+
return this.details.password();
76+
}
77+
78+
@Override
79+
public List<X509Certificate> certificates() {
80+
return this.certificates;
81+
}
82+
83+
@Override
84+
public PrivateKey privateKey() {
85+
return this.privateKey;
86+
}
87+
88+
}
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
/*
2+
* Copyright 2012-2023 the original author or authors.
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 org.springframework.boot.ssl.pem;
18+
19+
import java.io.IOException;
20+
import java.security.KeyStore;
21+
import java.security.PrivateKey;
22+
import java.security.cert.X509Certificate;
23+
import java.util.List;
24+
25+
import org.springframework.util.Assert;
26+
27+
/**
28+
* An individual trust or key store that has been loaded from PEM content.
29+
*
30+
* @author Phillip Webb
31+
* @since 3.2.0
32+
* @see PemSslStoreDetails
33+
* @see PemContent
34+
*/
35+
public interface PemSslStore {
36+
37+
/**
38+
* The key store type, for example {@code JKS} or {@code PKCS11}. A {@code null} value
39+
* will use {@link KeyStore#getDefaultType()}).
40+
* @return the key store type
41+
*/
42+
String type();
43+
44+
/**
45+
* The alias used when setting entries in the {@link KeyStore}.
46+
* @return the alias
47+
*/
48+
String alias();
49+
50+
/**
51+
* the password used
52+
* {@link KeyStore#setKeyEntry(String, java.security.Key, char[], java.security.cert.Certificate[])
53+
* setting key entries} in the {@link KeyStore}.
54+
* @return the password
55+
*/
56+
String password();
57+
58+
/**
59+
* The certificates for this store. When a {@link #privateKey() private key} is
60+
* present the returned value is treated as a certificate chain, otherwise it is
61+
* treated a list of certificates that should all be registered.
62+
* @return the X509 certificates
63+
*/
64+
List<X509Certificate> certificates();
65+
66+
/**
67+
* The private key for this store or {@code null}.
68+
* @return the private key
69+
*/
70+
PrivateKey privateKey();
71+
72+
/**
73+
* Return a new {@link PemSslStore} instance with a new alias.
74+
* @param alias the new alias
75+
* @return a new {@link PemSslStore} instance
76+
*/
77+
default PemSslStore withAlias(String alias) {
78+
return of(type(), alias, password(), certificates(), privateKey());
79+
}
80+
81+
/**
82+
* Return a new {@link PemSslStore} instance with a new password.
83+
* @param password the new password
84+
* @return a new {@link PemSslStore} instance
85+
*/
86+
default PemSslStore withPassword(String password) {
87+
return of(type(), alias(), password, certificates(), privateKey());
88+
}
89+
90+
/**
91+
* Return a {@link PemSslStore} instance loaded using the given
92+
* {@link PemSslStoreDetails}.
93+
* @param details the PEM store details
94+
* @return a loaded {@link PemSslStore} or {@code null}.
95+
* @throws IOException on IO error
96+
*/
97+
static PemSslStore load(PemSslStoreDetails details) throws IOException {
98+
if (details == null || details.isEmpty()) {
99+
return null;
100+
}
101+
return new LoadedPemSslStore(details);
102+
}
103+
104+
/**
105+
* Factory method that can be used to create a new {@link PemSslStore} with the given
106+
* values.
107+
* @param type the key store type
108+
* @param certificates the certificates for this store
109+
* @param privateKey the private key
110+
* @return a new {@link PemSslStore} instance
111+
*/
112+
static PemSslStore of(String type, List<X509Certificate> certificates, PrivateKey privateKey) {
113+
return of(type, null, null, certificates, privateKey);
114+
}
115+
116+
/**
117+
* Factory method that can be used to create a new {@link PemSslStore} with the given
118+
* values.
119+
* @param certificates the certificates for this store
120+
* @param privateKey the private key
121+
* @return a new {@link PemSslStore} instance
122+
*/
123+
static PemSslStore of(List<X509Certificate> certificates, PrivateKey privateKey) {
124+
return of(null, null, null, certificates, privateKey);
125+
}
126+
127+
/**
128+
* Factory method that can be used to create a new {@link PemSslStore} with the given
129+
* values.
130+
* @param type the key store type
131+
* @param alias the alias used when setting entries in the {@link KeyStore}
132+
* @param password the password used
133+
* {@link KeyStore#setKeyEntry(String, java.security.Key, char[], java.security.cert.Certificate[])
134+
* setting key entries} in the {@link KeyStore}
135+
* @param certificates the certificates for this store
136+
* @param privateKey the private key
137+
* @return a new {@link PemSslStore} instance
138+
*/
139+
static PemSslStore of(String type, String alias, String password, List<X509Certificate> certificates,
140+
PrivateKey privateKey) {
141+
Assert.notEmpty(certificates, "Certificates must not be empty");
142+
return new PemSslStore() {
143+
144+
@Override
145+
public String type() {
146+
return type;
147+
}
148+
149+
@Override
150+
public String alias() {
151+
return alias;
152+
}
153+
154+
@Override
155+
public String password() {
156+
return password;
157+
}
158+
159+
@Override
160+
public List<X509Certificate> certificates() {
161+
return certificates;
162+
}
163+
164+
@Override
165+
public PrivateKey privateKey() {
166+
return privateKey;
167+
}
168+
169+
};
170+
}
171+
172+
}

0 commit comments

Comments
 (0)