package com.limegroup.gnutella.security;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.security.SignatureException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import org.limewire.io.IpPort;
import org.limewire.logging.Log;
import org.limewire.logging.LogFactory;
/**
* Reads valid certificates from file, http and stores them to the same file.
*/
public class CertificateProviderImpl implements CertificateProvider {
private static final Log LOG = LogFactory.getLog(CertificateProviderImpl.class);
private final FileCertificateReader fileCertificateReader;
private final HttpCertificateReader httpCertificateReader;
private final CertificateVerifier certificateVerifier;
private AtomicReference<Certificate> validCertificate = new AtomicReference<Certificate>(null);
private final File file;
private final URI uri;
private final AtomicBoolean httpDone = new AtomicBoolean(false);
/**
* @param fileCertificateReader the file certificate reader used for reading
* certificates from disk and for storing them to disk.
* @param httpCertificateReader the http certificate reader used for
* retrieving certificates from a trusted http server
* @param certificateVerifier verifier to verify all read and set certificates
* @param file the file to read certificates from and write them to
* @param uri the uri certificates are downloaded from over http
*/
public CertificateProviderImpl(FileCertificateReader fileCertificateReader,
HttpCertificateReader httpCertificateReader,
CertificateVerifier certificateVerifier,
File file, URI uri) {
this.fileCertificateReader = fileCertificateReader;
this.httpCertificateReader = httpCertificateReader;
this.certificateVerifier = certificateVerifier;
this.file = file;
this.uri = uri;
}
private Certificate getFromFile() {
try {
return certificateVerifier.verify(fileCertificateReader.read(file));
} catch (IOException e) {
LOG.debugf(e, "certificate from invalid file: {0}", file);
} catch (SignatureException e) {
LOG.debugf(e, "certificate from file {0} invalid", file);
}
return null;
}
@Override
public void set(Certificate certificate) {
LOG.debugf("setting certificate: {0}", certificate);
try {
Certificate localCopy = validCertificate.get();
if (localCopy == null || certificate.getKeyVersion() > localCopy.getKeyVersion()) {
validCertificate.set(certificateVerifier.verify(certificate));
fileCertificateReader.write(certificate, file);
} else {
LOG.debugf("certificate version not greater than local one: {0}", certificate);
}
} catch (SignatureException se) {
LOG.debugf(se, "certificate invalid {0} ", certificate);
}
}
/**
* Potentially blocking call, accessing the disk and making network connections.
* <p>
* If a valid certificate is loaded, it will return the valid certificate.
* Otherwise it will try to read a certificate from disk. If this fails it
* will resort to http.
*
* @returns {@link NullCertificate} if no valid certificate could be retrieved
* from any of the sources
*/
@Override
public Certificate get() {
Certificate copy = validCertificate.get();
if (copy != null) {
return copy;
}
validCertificate.compareAndSet(null, getFromFile());
copy = validCertificate.get();
if (copy != null) {
return copy;
}
return new NullCertificate();
}
@Override
public Certificate get(int keyVersion, IpPort messageSource) {
Certificate copy = validCertificate.get();
if (copy == null) {
validCertificate.compareAndSet(null, getFromFile());
}
copy = validCertificate.get();
if (copy != null && copy.getKeyVersion() >= keyVersion) {
return copy;
}
return getFromHttp(messageSource);
}
Certificate getFromHttp(IpPort messageSource) {
if (httpDone.compareAndSet(false, true)) {
try {
LOG.debug("getting certifcate from http");
return certificateVerifier.verify(httpCertificateReader.read(uri, messageSource));
} catch (IOException ie) {
LOG.debugf(ie, "certificate from invalid url: {0}", uri);
} catch (SignatureException e) {
LOG.debugf(e, "certificate from http invalid: {0}", uri);
}
return new NullCertificate();
} else {
Certificate copy = validCertificate.get();
if (copy != null) {
return copy;
} else {
return new NullCertificate();
}
}
}
}