-
Notifications
You must be signed in to change notification settings - Fork 682
/
KeyStoreCredentialProvider.java
170 lines (147 loc) · 6.06 KB
/
KeyStoreCredentialProvider.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
package org.pac4j.saml.crypto;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import org.opensaml.core.criterion.EntityIdCriterion;
import org.opensaml.security.credential.Credential;
import org.opensaml.security.credential.CredentialResolver;
import org.opensaml.security.credential.impl.KeyStoreCredentialResolver;
import org.opensaml.security.x509.X509Credential;
import org.opensaml.xmlsec.config.DefaultSecurityConfigurationBootstrap;
import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;
import org.opensaml.xmlsec.keyinfo.KeyInfoGenerator;
import org.opensaml.xmlsec.keyinfo.NamedKeyInfoGeneratorManager;
import org.opensaml.xmlsec.signature.KeyInfo;
import org.pac4j.core.exception.TechnicalException;
import org.pac4j.core.io.Resource;
import org.pac4j.core.util.CommonHelper;
import org.pac4j.saml.client.SAML2ClientConfiguration;
import org.pac4j.saml.exceptions.SAMLException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
import net.shibboleth.utilities.java.support.resolver.ResolverException;
/**
* Class responsible for loading a private key from a JKS keystore and returning
* the corresponding {@link Credential} opensaml object.
*
* @author Misagh Moayyed
* @since 1.8.0
*/
public class KeyStoreCredentialProvider implements CredentialProvider {
private static final String DEFAULT_KEYSTORE_TYPE = "JKS";
private final Logger logger = LoggerFactory.getLogger(KeyStoreCredentialProvider.class);
private final CredentialResolver credentialResolver;
private final String privateKey;
public KeyStoreCredentialProvider(final String name, final String storePasswd, final String privateKeyPasswd) {
this(null, null, DEFAULT_KEYSTORE_TYPE, null, name, storePasswd, privateKeyPasswd);
}
public KeyStoreCredentialProvider(final KeyStore keyStore, final String keyStoreAlias, String keyStoreType,
final Resource keyStoreResource, final String keyStorePath, final String storePasswd,
final String privateKeyPasswd) {
CommonHelper.assertTrue(keyStore != null || keyStoreResource != null || CommonHelper.isNotBlank(keyStorePath),
"Either keyStore, keyStoreResource or keyStorePath must be provided");
final KeyStore keyStoreToUse;
if (keyStore != null) {
keyStoreToUse = keyStore;
} else {
final InputStream inputStream;
if (keyStoreResource != null) {
try {
inputStream = keyStoreResource.getInputStream();
} catch (IOException e) {
throw new TechnicalException(e);
}
} else {
inputStream = CommonHelper.getInputStreamFromName(keyStorePath);
}
keyStoreToUse = loadKeyStore(inputStream, storePasswd, keyStoreType);
}
this.privateKey = getPrivateKeyAlias(keyStoreToUse, keyStoreAlias);
final Map<String, String> passwords = new HashMap<String, String>();
passwords.put(this.privateKey, privateKeyPasswd);
this.credentialResolver = new KeyStoreCredentialResolver(keyStoreToUse, passwords);
}
public KeyStoreCredentialProvider(SAML2ClientConfiguration configuration) {
this(configuration.getKeyStore(), configuration.getKeyStoreAlias(), (configuration.getKeyStoreType() == null ? DEFAULT_KEYSTORE_TYPE : configuration.getKeyStoreType()),
configuration.getKeystoreResource(), configuration.getKeystorePath(),
configuration.getKeystorePassword(), configuration.getPrivateKeyPassword());
}
@Override
public KeyInfo getKeyInfo() {
final Credential serverCredential = getCredential();
final KeyInfo keyInfo = generateKeyInfoForCredential(serverCredential);
return keyInfo;
}
@Override
public final CredentialResolver getCredentialResolver() {
return credentialResolver;
}
@Override
public KeyInfoCredentialResolver getKeyInfoCredentialResolver() {
return DefaultSecurityConfigurationBootstrap.buildBasicInlineKeyInfoCredentialResolver();
}
@Override
public final KeyInfoGenerator getKeyInfoGenerator() {
final NamedKeyInfoGeneratorManager mgmr = DefaultSecurityConfigurationBootstrap
.buildBasicKeyInfoGeneratorManager();
final Credential credential = getCredential();
return mgmr.getDefaultManager().getFactory(credential).newInstance();
}
@Override
public final Credential getCredential() {
try {
final CriteriaSet cs = new CriteriaSet();
final EntityIdCriterion criteria = new EntityIdCriterion(this.privateKey);
cs.add(criteria);
final X509Credential creds = (X509Credential) this.credentialResolver.resolveSingle(cs);
return creds;
} catch (final ResolverException e) {
throw new SAMLException("Can't obtain SP private key", e);
}
}
protected final KeyInfo generateKeyInfoForCredential(final Credential credential) {
try {
return getKeyInfoGenerator().generate(credential);
} catch (final org.opensaml.security.SecurityException e) {
throw new SAMLException("Unable to generate keyInfo from given credential", e);
}
}
private KeyStore loadKeyStore(final InputStream inputStream, final String storePasswd, final String keyStoreType) {
try {
final KeyStore ks = KeyStore.getInstance(keyStoreType);
ks.load(inputStream, storePasswd == null ? null : storePasswd.toCharArray());
return ks;
} catch (final Exception e) {
throw new SAMLException("Error loading keystore", e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (final IOException e) {
this.logger.debug("Error closing input stream of keystore", e);
}
}
}
}
private String getPrivateKeyAlias(final KeyStore keyStore, final String keyStoreAlias) {
try {
final Enumeration<String> aliases = keyStore.aliases();
while (aliases.hasMoreElements()) {
String currentAlias = aliases.nextElement();
if (keyStoreAlias == null) {
return currentAlias;
} else if (currentAlias.equals(keyStoreAlias)) {
return currentAlias;
}
}
throw new SAMLException("Keystore has no private keys");
} catch (final KeyStoreException e) {
throw new SAMLException("Unable to get aliases from keyStore", e);
}
}
}