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();
 | 
						|
}
 |