Files
i2p.plugins.i2pcontrol/src/java/net/i2p/i2pcontrol/security/SecurityManager.java
2016-01-20 18:04:46 +00:00

217 lines
7.4 KiB
Java

package net.i2p.i2pcontrol.security;
/*
* Copyright 2011 hottuna (dev@robertfoss.se)
*
* 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.
*
*/
import net.i2p.I2PAppContext;
import net.i2p.crypto.SHA256Generator;
import net.i2p.data.Base64;
import net.i2p.i2pcontrol.security.jbcrypt.BCrypt;
import net.i2p.i2pcontrol.servlets.configuration.ConfigurationManager;
import net.i2p.util.Log;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.security.KeyStore;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.*;
/**
* Manage the password storing for I2PControl.
*/
public class SecurityManager {
private final static String SSL_PROVIDER = "SunJSSE";
private final static String DEFAULT_AUTH_BCRYPT_SALT = "$2a$11$5aOLx2x/8i4fNaitoCSSWu";
private final static String DEFAULT_AUTH_PASSWORD = "$2a$11$5aOLx2x/8i4fNaitoCSSWuut2wEl3Hupuca8DCT.NXzvH9fq1pBU.";
private HashMap<String, AuthToken> authTokens;
private Timer timer;
private String[] SSL_CIPHER_SUITES;
private KeyStore _ks;
private Log _log;
private static SecurityManager _securityManager;
public static SecurityManager getInstance() {
if (_securityManager == null) {
_securityManager = new SecurityManager();
}
return _securityManager;
}
private SecurityManager() {
_log = I2PAppContext.getGlobalContext().logManager().getLog(SecurityManager.class);
authTokens = new HashMap<String, AuthToken>();
timer = new Timer("SecurityManager Timer Sweeper ");
// Start running periodic task after 20 minutes, run periodically every 10th minute.
timer.scheduleAtFixedRate(new Sweeper(), 1000 * 60 * 20, 1000 * 60 * 10);
// Get supported SSL cipher suites.
SocketFactory SSLF = SSLSocketFactory.getDefault();
try {
SSL_CIPHER_SUITES = ((SSLSocket)SSLF.createSocket()).getSupportedCipherSuites();
} catch (Exception e) {
_log.log(Log.CRIT, "Unable to create SSLSocket used for fetching supported ssl cipher suites.", e);
}
_ks = KeyStoreProvider.getDefaultKeyStore();
}
public String[] getSupprtedSSLCipherSuites() {
return SSL_CIPHER_SUITES;
}
public String getSecurityProvider() {
return SSL_PROVIDER;
}
public void stopTimedEvents() {
timer.cancel();
}
/**
* Return the X509Certificate of the server as a Base64 encoded string.
* @return base64 encode of X509Certificate
*/
public String getBase64Cert() {
X509Certificate caCert = KeyStoreProvider.readCert(_ks,
KeyStoreProvider.DEFAULT_CERTIFICATE_ALIAS,
KeyStoreProvider.DEFAULT_KEYSTORE_PASSWORD);
return getBase64FromCert(caCert);
}
/**
* Return the X509Certificate as a base64 encoded string.
* @param cert
* @return base64 encode of X509Certificate
*/
private static String getBase64FromCert(X509Certificate cert) {
try {
return Base64.encode(cert.getEncoded());
} catch (CertificateEncodingException e) {
e.printStackTrace();
}
return null;
}
/**
* Hash pwd with using BCrypt with the default salt.
* @param pwd
* @return BCrypt hash of salt and input string
*/
public String getPasswdHash(String pwd) {
return BCrypt.hashpw(pwd, ConfigurationManager.getInstance().getConf("auth.salt", DEFAULT_AUTH_BCRYPT_SALT));
}
/**
* Hash input one time with SHA-256, return Base64 encdoded string.
* @param string
* @return
*/
public String getHash(String string) {
SHA256Generator hashGen = new SHA256Generator(I2PAppContext.getGlobalContext());
byte[] bytes = string.getBytes();
bytes = hashGen.calculateHash(bytes).toByteArray();
return Base64.encode(bytes);
}
/**
* Add a Authentication Token if the provided password is valid.
* The token will be valid for one day.
* @return Returns AuthToken if password is valid. If password is invalid null will be returned.
*/
public AuthToken validatePasswd(String pwd) {
String storedPass = ConfigurationManager.getInstance().getConf("auth.password", DEFAULT_AUTH_PASSWORD);
if (getPasswdHash(pwd).equals(storedPass)) {
AuthToken token = new AuthToken(pwd);
synchronized (authTokens) {
authTokens.put(token.getId(), token);
}
return token;
} else {
return null;
}
}
/**
* Set new password. Old tokens will NOT remain valid, to encourage the new password being tested.
* @param newPasswd
* @return Returns true if a new password was set.
*/
public boolean setPasswd(String newPasswd) {
String newHash = getPasswdHash(newPasswd);
String oldHash = ConfigurationManager.getInstance().getConf("auth.password", DEFAULT_AUTH_PASSWORD);
if (!newHash.equals(oldHash)) {
ConfigurationManager.getInstance().setConf("auth.password", newHash);
synchronized (authTokens) {
authTokens.clear();
}
return true;
}
return false;
}
/**
* Checks whether the AuthToken with the given ID exists and if it does whether is has expired.
* @param tokenID - The token to validate
* @throws InvalidAuthTokenException
* @throws ExpiredAuthTokenException
*/
public void verifyToken(String tokenID) throws InvalidAuthTokenException, ExpiredAuthTokenException {
AuthToken token = authTokens.get(tokenID);
if (token == null) {
throw new InvalidAuthTokenException("AuthToken with ID: " + tokenID + " couldn't be found.");
} else if (!token.isValid()) {
synchronized (authTokens) {
authTokens.remove(token.getId());
}
throw new ExpiredAuthTokenException("AuthToken with ID: " + tokenID + " expired " + token.getExpiryTime(), token.getExpiryTime());
} else {
return; // Everything is fine. :)
}
}
/**
* Clean up old authorization tokens to keep the token store slim and fit.
* @author hottuna
*
*/
private class Sweeper extends TimerTask {
@Override
public void run() {
_log.debug("Starting cleanup job..");
ArrayList<String> arr = new ArrayList<String>();
for (Map.Entry<String, AuthToken> e : authTokens.entrySet()) {
AuthToken token = e.getValue();
if (!token.isValid()) {
arr.add(e.getKey());
}
}
synchronized (authTokens) {
for (String s : arr) {
authTokens.remove(s);
}
}
_log.debug("Cleanup job done.");
}
}
}