183 lines
6.3 KiB
C++
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();
|
|
}
|