org.simplejavamail.utils.mail.smime.SmimeKeyStore Maven / Gradle / Ivy
Show all versions of utils-mail-smime Show documentation
/*
* Copyright © 2021 Benny Bottema ([email protected])
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.simplejavamail.utils.mail.smime;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
/**
* A wrapper around a {@link KeyStore} that can be initialized with a PKCS12
* keystore and is used to obtain {@link SmimeKey SmimeKeys}.
*
* @author Allen Petersen (akp at sourceforge dot net)
* @author Torsten Krause (tk at markenwerk dot net)
* @since 1.0.0
*/
public class SmimeKeyStore {
private final KeyStore keyStore;
/**
* Creates a new {@code SmimeKeyStore} by loading a PKCS12 keystore from
* the given input stream.
*
*
* The character array holding the password is overwritten with {@code 0s}
* after it has been used.
*
* @param stream
* The {@link InputStream} to read the PKCS12 keystore from.
* @param password
* The password to unlock the PKCS12 keystore with.
*/
public SmimeKeyStore(InputStream stream, char[] password) {
this(stream, password, true);
}
/**
* Creates a new {@code SmimeKeyStore} by loading a PKCS12 keystore from
* the given input stream.
*
*
* If {@code discardPassword} is set to {@code true}, the character array
* holding the password is overwritten with {@code 0s} after it has been
* used.
*
* @param stream
* The {@link InputStream} to read the PKCS12 keystore from.
* @param password
* The password to unlock the PKCS12 keystore with.
* @param discardPassword
* Whether to overwrite the {@code char[]} holding the password
* after it has been used.
*/
public SmimeKeyStore(InputStream stream, char[] password, boolean discardPassword) {
try {
keyStore = KeyStore.getInstance("PKCS12", "BC");
keyStore.load(stream, password);
} catch (Exception e) {
throw new SmimeException("Couldn't initialize SmimeKeyStore", e);
} finally {
if (discardPassword) {
overwrite(password);
}
}
}
private void overwrite(char[] password) {
if (null != password) {
for (int i = 0, n = password.length; i < n; i++) {
password[i] = 0;
}
}
}
/**
* Returns the number of entries in the underlying PKCS12 keystore.
*
* @return The number of entries in the underlying {@link KeyStore}.
*
*/
public int size() {
try {
return keyStore.size();
} catch (KeyStoreException e) {
throw new SmimeException("Couldn't retrieve the number of entries from SmimeKeyStore", e);
}
}
/**
* Returns the S/MIME key associated with the given alias, using the given
* password to recover it.
*
*
* The character array holding the password is overwritten with {@code 0s}
* after it has been used.
*
* @param alias
* The alias.
* @param password
* The password to unlock the {@link PrivateKey} keystore with.
*
* @return The requested {@link SmimeKey}, or null if the given alias does
* not exist or does not identify a private key entry.
*/
public SmimeKey getPrivateKey(String alias, char[] password) {
return getPrivateKey(alias, password, true);
}
/**
* Returns the S/MIME key associated with the given alias, using the given
* password to recover it.
*
*
* If {@code discardPassword} is set to {@code true}, the character array
* holding the password is overwritten with {@code 0s} after it has been
* used.
*
* @param alias
* The alias.
* @param password
* The password to unlock the {@link PrivateKey} keystore with.
* @param discardPassword
* Whether to overwrite the {@code char[]} holding the password
* after it has been used.
*
* @return The requested {@link SmimeKey}, or null if the given alias does
* not exist or does not identify a private key entry.
*/
public SmimeKey getPrivateKey(String alias, char[] password, boolean discardPassword) {
try {
if (containsPrivateKeyAlias(alias)) {
PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, password);
Certificate[] certificateChain = keyStore.getCertificateChain(alias);
return new SmimeKey(privateKey, copy(certificateChain));
}
return null;
} catch (Exception e) {
throw new SmimeException("Couldn't recover SmimeKey from SmimeKeyStore", e);
} finally {
if (discardPassword) {
overwrite(password);
}
}
}
private X509Certificate[] copy(Certificate[] certificateChain) {
X509Certificate[] x509certificateChain = new X509Certificate[certificateChain.length];
for (int i = 0, n = certificateChain.length; i < n; i++) {
x509certificateChain[i] = (X509Certificate) certificateChain[i];
}
return x509certificateChain;
}
/**
* Returns a set containing all aliases listed in the PKCS12 keystore.
*
* @return A {@link Collections#unmodifiableSet(Set) unmodifiable set} of
* aliases.
*/
public Set getPrivateKeyAliases() {
try {
Enumeration aliases = keyStore.aliases();
Set aliasSet = new HashSet<>();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
if (keyStore.isKeyEntry(alias))
aliasSet.add(alias);
}
return Collections.unmodifiableSet(aliasSet);
} catch (Exception e) {
throw new SmimeException("Couldn't recover aliases from SmimeKeyStore", e);
}
}
/**
* Checks if the given alias exists in the PKCS12 keystore.
*
* @param alias
* The alias to look for.
*
* @return {@code true} if the alias exists, {@code false} otherwise.
*/
public boolean containsPrivateKeyAlias(String alias) {
try {
return keyStore.isKeyEntry(alias);
} catch (Exception e) {
throw new SmimeException("Couldn't recover aliases from SmimeKeyStore", e);
}
}
}