fuel-scm/ext/qtkeychain/keychain_android.cpp
2021-05-30 14:01:05 +02:00

183 lines
6.3 KiB
C++

/******************************************************************************
* Copyright (C) 2016 Mathias Hasselmann <mathias.hasselmann@kdab.com> *
* *
* 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. For licensing and distribution *
* details, check the accompanying file 'COPYING'. *
*****************************************************************************/
#include "keychain_p.h"
#include "androidkeystore_p.h"
#include "plaintextstore_p.h"
#include <QtAndroid>
using namespace QKeychain;
using android::content::Context;
using android::security::KeyPairGeneratorSpec;
using java::io::ByteArrayInputStream;
using java::io::ByteArrayOutputStream;
using java::security::interfaces::RSAPrivateKey;
using java::security::interfaces::RSAPublicKey;
using java::security::KeyPair;
using java::security::KeyPairGenerator;
using java::security::KeyStore;
using java::util::Calendar;
using javax::crypto::Cipher;
using javax::crypto::CipherInputStream;
using javax::crypto::CipherOutputStream;
using javax::security::auth::x500::X500Principal;
namespace {
inline QString makeAlias(const QString &service, const QString &key)
{
return service + QLatin1Char('/') + key;
}
}
void ReadPasswordJobPrivate::scheduledStart()
{
PlainTextStore plainTextStore(q->service(), q->settings());
if (!plainTextStore.contains(q->key())) {
q->emitFinishedWithError(Error::EntryNotFound, tr("Entry not found"));
return;
}
const QByteArray &encryptedData = plainTextStore.readData(q->key());
const KeyStore keyStore = KeyStore::getInstance(QStringLiteral("AndroidKeyStore"));
if (!keyStore || !keyStore.load()) {
q->emitFinishedWithError(Error::AccessDenied, tr("Could not open keystore"));
return;
}
const auto &alias = makeAlias(q->service(), q->key());
const KeyStore::PrivateKeyEntry entry = keyStore.getEntry(alias);
if (!entry) {
q->emitFinishedWithError(Error::AccessDenied, tr("Could not retrieve private key from keystore"));
return;
}
const Cipher cipher = Cipher::getInstance(QStringLiteral("RSA/ECB/PKCS1Padding"));
if (!cipher || !cipher.init(Cipher::DECRYPT_MODE, entry.getPrivateKey())) {
q->emitFinishedWithError(Error::OtherError, tr("Could not create decryption cipher"));
return;
}
QByteArray plainData;
const CipherInputStream inputStream(ByteArrayInputStream(encryptedData), cipher);
for (int nextByte; (nextByte = inputStream.read()) != -1; )
plainData.append(nextByte);
mode = plainTextStore.readMode(q->key());
data = plainData;
q->emitFinished();
}
void WritePasswordJobPrivate::scheduledStart()
{
const KeyStore keyStore = KeyStore::getInstance(QStringLiteral("AndroidKeyStore"));
if (!keyStore || !keyStore.load()) {
q->emitFinishedWithError(Error::AccessDenied, tr("Could not open keystore"));
return;
}
const auto &alias = makeAlias(q->service(), q->key());
if (!keyStore.containsAlias(alias)) {
const Calendar start = Calendar::getInstance();
const Calendar end = Calendar::getInstance();
end.add(Calendar::YEAR, 99);
const KeyPairGeneratorSpec spec =
KeyPairGeneratorSpec::Builder(Context(QtAndroid::androidActivity())).
setAlias(alias).
setSubject(X500Principal(QStringLiteral("CN=QtKeychain, O=Android Authority"))).
setSerialNumber(java::math::BigInteger::ONE).
setStartDate(start.getTime()).
setEndDate(end.getTime()).
build();
const KeyPairGenerator generator = KeyPairGenerator::getInstance(QStringLiteral("RSA"),
QStringLiteral("AndroidKeyStore"));
if (!generator) {
q->emitFinishedWithError(Error::OtherError, tr("Could not create private key generator"));
return;
}
generator.initialize(spec);
if (!generator.generateKeyPair()) {
q->emitFinishedWithError(Error::OtherError, tr("Could not generate new private key"));
return;
}
}
const KeyStore::PrivateKeyEntry entry = keyStore.getEntry(alias);
if (!entry) {
q->emitFinishedWithError(Error::AccessDenied, tr("Could not retrieve private key from keystore"));
return;
}
const RSAPublicKey publicKey = entry.getCertificate().getPublicKey();
const Cipher cipher = Cipher::getInstance(QStringLiteral("RSA/ECB/PKCS1Padding"));
if (!cipher || !cipher.init(Cipher::ENCRYPT_MODE, publicKey)) {
q->emitFinishedWithError(Error::OtherError, tr("Could not create encryption cipher"));
return;
}
ByteArrayOutputStream outputStream;
CipherOutputStream cipherOutputStream(outputStream, cipher);
if (!cipherOutputStream.write(data) || !cipherOutputStream.close()) {
q->emitFinishedWithError(Error::OtherError, tr("Could not encrypt data"));
return;
}
PlainTextStore plainTextStore(q->service(), q->settings());
plainTextStore.write(q->key(), outputStream.toByteArray(), mode);
if (plainTextStore.error() != NoError)
q->emitFinishedWithError(plainTextStore.error(), plainTextStore.errorString());
else
q->emitFinished();
}
void DeletePasswordJobPrivate::scheduledStart()
{
const KeyStore keyStore = KeyStore::getInstance(QStringLiteral("AndroidKeyStore"));
if (!keyStore || !keyStore.load()) {
q->emitFinishedWithError(Error::AccessDenied, tr("Could not open keystore"));
return;
}
const auto &alias = makeAlias(q->service(), q->key());
if (!keyStore.deleteEntry(alias)) {
q->emitFinishedWithError(Error::OtherError, tr("Could not remove private key from keystore"));
return;
}
PlainTextStore plainTextStore(q->service(), q->settings());
plainTextStore.remove(q->key());
if (plainTextStore.error() != NoError)
q->emitFinishedWithError(plainTextStore.error(), plainTextStore.errorString());
else
q->emitFinished();
}