open-keychain/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/WebKeyDirectoryClient.java
2020-05-30 15:47:09 +02:00

136 lines
5.1 KiB
Java

/*
* Copyright (C) 2017 Schürmann & Breitmoser GbR
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.keyimport;
import androidx.annotation.Nullable;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.sufficientlysecure.keychain.network.OkHttpClientFactory;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.util.ParcelableProxy;
import org.sufficientlysecure.keychain.util.WebKeyDirectoryUtil;
import timber.log.Timber;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.List;
/**
* Searches for keys using Web Key Directory protocol.
*
* @see <a href="https://tools.ietf.org/html/draft-koch-openpgp-webkey-service-05#section-3.1">Key Discovery</a>
*/
public class WebKeyDirectoryClient implements KeyserverClient {
public static WebKeyDirectoryClient getInstance() {
return new WebKeyDirectoryClient();
}
private WebKeyDirectoryClient() {
}
@Override
public List<ImportKeysListEntry> search(String name, ParcelableProxy proxy)
throws QueryFailedException {
URL webKeyDirectoryURL = WebKeyDirectoryUtil.toWebKeyDirectoryURL(name, true);
if (webKeyDirectoryURL == null) {
Timber.d("Name not supported by Web Key Directory Client: " + name);
return Collections.emptyList();
}
Timber.d("Web Key Directory import: " + name + " using Proxy: " + proxy.getProxy());
Timber.d("Query Web Key Directory Advanced method for: " + name);
byte[] data = query(webKeyDirectoryURL, proxy.getProxy());
if (data == null) {
// Retry with direct mode
URL webKeyDirectoryURLDirect = WebKeyDirectoryUtil.toWebKeyDirectoryURL(name, false);
Timber.d("Query Web Key Directory fallback Direct method for: " + name);
byte[] dataDirect = query(webKeyDirectoryURLDirect, proxy.getProxy());
if (dataDirect == null) {
Timber.d("No Web Key Directory endpoint for: " + name);
return Collections.emptyList();
} else {
data = dataDirect;
}
}
// if we're here that means key retrieval succeeded,
// would have thrown an exception otherwise
try {
UncachedKeyRing ring = UncachedKeyRing.decodeFromData(data);
return Collections.singletonList(new ImportKeysListEntry(null, ring));
} catch (PgpGeneralException | IOException e) {
Timber.e(e, "Failed parsing key from Web Key Directory during search");
throw new QueryFailedException("No valid key found on Web Key Directory");
}
}
@Override
public String get(String name, ParcelableProxy proxy) {
throw new UnsupportedOperationException("Returning armored key from Web Key Directory not supported");
}
@Nullable
private byte[] query(URL url, Proxy proxy) throws QueryFailedException {
try {
Timber.d("fetching from Web Key Directory with: %s proxy: %s", url, proxy);
Request request = new Request.Builder().url(url).build();
OkHttpClient client = OkHttpClientFactory.getClientPinnedIfAvailableWithRedirects(url, proxy);
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
return response.body().bytes();
} else if (response.code() == HttpURLConnection.HTTP_NOT_FOUND) {
return null;
} else {
throw new QueryFailedException("Error while fetching key from Web Key Directory. " +
"Response:" + response);
}
} catch (UnknownHostException e) {
Timber.e(e, "Unknown host at Web Key Directory key download");
return null;
} catch (IOException e) {
Timber.e(e, "IOException at Web Key Directory key download");
throw new QueryFailedException("Cannot connect to Web Key Directory. "
+ "Check your Internet connection!"
+ (proxy == Proxy.NO_PROXY ? "" : " Using proxy " + proxy));
}
}
@Override
public void add(String armoredKey, ParcelableProxy proxy) {
throw new UnsupportedOperationException("Uploading keys to Web Key Directory is not supported");
}
}