mirror of
https://github.com/moparisthebest/keepass2android
synced 2024-11-25 02:32:26 -05:00
Improved Certificate Handling (fixed problem with Certificate validation)
This commit is contained in:
parent
ee8348e688
commit
b320477a64
@ -20,7 +20,7 @@
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>False</Optimize>
|
||||
<OutputPath>bin\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;EXCLUDE_TWOFISH;EXCLUDE_KEYBOARD;EXCLUDE_KEYTRANSFORM;INCLUDE_FILECHOOSER;INCLUDE_JAVAFILESTORAGE</DefineConstants>
|
||||
<DefineConstants>DEBUG;EXCLUDE_TWOFISH;EXCLUDE_KEYBOARD;EXCLUDE_KEYTRANSFORM;EXCLUDE_FILECHOOSER;EXCLUDE_JAVAFILESTORAGE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>False</ConsolePause>
|
||||
|
@ -317,7 +317,7 @@ namespace KeePassLib.Serialization
|
||||
if ((ex.Response is HttpWebResponse) && (((HttpWebResponse)ex.Response).StatusCode == HttpStatusCode.Unauthorized))
|
||||
return CreateWebClient(ioc, true).OpenRead(new Uri(ioc.Path));
|
||||
else
|
||||
throw ex;
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
@ -369,7 +369,7 @@ namespace KeePassLib.Serialization
|
||||
if ((ex.Response is HttpWebResponse) && (((HttpWebResponse) ex.Response).StatusCode == HttpStatusCode.Unauthorized))
|
||||
uploadData(IOConnection.CreateWebClient(ioc, true));
|
||||
else
|
||||
throw ex;
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -78,5 +78,11 @@ namespace keepass2android
|
||||
IFileStorage GetFileStorage(IOConnectionInfo iocInfo);
|
||||
|
||||
void TriggerReload(Context context);
|
||||
|
||||
/// <summary>
|
||||
/// Handles a failed certificate validation. Returns true if the users wants to continue, false otherwise.
|
||||
/// see http://msdn.microsoft.com/en-us/library/system.net.icertificatepolicy(v=vs.110).aspx
|
||||
/// </summary>
|
||||
bool OnServerCertificateError(int certificateProblem);
|
||||
}
|
||||
}
|
@ -3,8 +3,10 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Security;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Java.Security.Cert;
|
||||
using KeePassLib.Serialization;
|
||||
using KeePassLib.Utility;
|
||||
|
||||
@ -12,6 +14,56 @@ namespace keepass2android.Io
|
||||
{
|
||||
public class BuiltInFileStorage: IFileStorage
|
||||
{
|
||||
public enum CertificateProblem :long
|
||||
{
|
||||
CertEXPIRED = 0x800B0101,
|
||||
CertVALIDITYPERIODNESTING = 0x800B0102,
|
||||
CertROLE = 0x800B0103,
|
||||
CertPATHLENCONST = 0x800B0104,
|
||||
CertCRITICAL = 0x800B0105,
|
||||
CertPURPOSE = 0x800B0106,
|
||||
CertISSUERCHAINING = 0x800B0107,
|
||||
CertMALFORMED = 0x800B0108,
|
||||
CertUNTRUSTEDROOT = 0x800B0109,
|
||||
CertCHAINING = 0x800B010A,
|
||||
CertREVOKED = 0x800B010C,
|
||||
CertUNTRUSTEDTESTROOT = 0x800B010D,
|
||||
CertREVOCATION_FAILURE = 0x800B010E,
|
||||
CertCN_NO_MATCH = 0x800B010F,
|
||||
CertWRONG_USAGE = 0x800B0110,
|
||||
CertUNTRUSTEDCA = 0x800B0112
|
||||
}
|
||||
|
||||
|
||||
private readonly IKp2aApp _app;
|
||||
|
||||
class CertificatePolicity: ICertificatePolicy
|
||||
{
|
||||
private readonly IKp2aApp _app;
|
||||
|
||||
public CertificatePolicity(IKp2aApp app)
|
||||
{
|
||||
_app = app;
|
||||
}
|
||||
|
||||
public bool CheckValidationResult(ServicePoint srvPoint, System.Security.Cryptography.X509Certificates.X509Certificate certificate, WebRequest request,
|
||||
int certificateProblem)
|
||||
{
|
||||
if (certificateProblem == 0) //ok
|
||||
return true;
|
||||
return _app.OnServerCertificateError(certificateProblem);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public BuiltInFileStorage(IKp2aApp app)
|
||||
{
|
||||
_app = app;
|
||||
//use the obsolute CertificatePolicy because the ServerCertificateValidationCallback isn't called in Mono for Android (?)
|
||||
ServicePointManager.CertificatePolicy = new CertificatePolicity(app);
|
||||
|
||||
}
|
||||
|
||||
public IEnumerable<string> SupportedProtocols
|
||||
{
|
||||
get
|
||||
@ -68,26 +120,51 @@ namespace keepass2android.Io
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
if ((ex.Response is HttpWebResponse) && (((HttpWebResponse)ex.Response).StatusCode == HttpStatusCode.NotFound))
|
||||
{
|
||||
throw new FileNotFoundException(ex.Message, ioc.Path, ex);
|
||||
}
|
||||
ConvertException(ioc, ex);
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void ConvertException(IOConnectionInfo ioc, WebException ex)
|
||||
{
|
||||
if ((ex.Response is HttpWebResponse) && (((HttpWebResponse) ex.Response).StatusCode == HttpStatusCode.NotFound))
|
||||
{
|
||||
throw new FileNotFoundException(ex.Message, ioc.Path, ex);
|
||||
}
|
||||
if (ex.Status == WebExceptionStatus.TrustFailure)
|
||||
{
|
||||
throw new Exception(_app.GetResourceString(UiStringKey.CertificateFailure), ex);
|
||||
}
|
||||
var inner1 = ex.InnerException as IOException;
|
||||
if (inner1 != null)
|
||||
{
|
||||
var inner2 = inner1.InnerException;
|
||||
if (inner2 != null)
|
||||
{
|
||||
if (inner2.Message.Contains("Invalid certificate received from server."))
|
||||
{
|
||||
throw new Exception(_app.GetResourceString(UiStringKey.CertificateFailure), ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction)
|
||||
{
|
||||
return new BuiltInFileTransaction(ioc, useFileTransaction);
|
||||
return new BuiltInFileTransaction(ioc, useFileTransaction, this);
|
||||
}
|
||||
|
||||
public class BuiltInFileTransaction : IWriteTransaction
|
||||
{
|
||||
private readonly IOConnectionInfo _ioc;
|
||||
private readonly BuiltInFileStorage _fileStorage;
|
||||
private readonly FileTransactionEx _transaction;
|
||||
|
||||
public BuiltInFileTransaction(IOConnectionInfo ioc, bool useFileTransaction)
|
||||
public BuiltInFileTransaction(IOConnectionInfo ioc, bool useFileTransaction, BuiltInFileStorage fileStorage)
|
||||
{
|
||||
_ioc = ioc;
|
||||
_fileStorage = fileStorage;
|
||||
_transaction = new FileTransactionEx(ioc, useFileTransaction);
|
||||
}
|
||||
|
||||
@ -98,12 +175,30 @@ namespace keepass2android.Io
|
||||
|
||||
public Stream OpenFile()
|
||||
{
|
||||
return _transaction.OpenWrite();
|
||||
try
|
||||
{
|
||||
return _transaction.OpenWrite();
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
_fileStorage.ConvertException(_ioc, ex);
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void CommitWrite()
|
||||
{
|
||||
_transaction.CommitWrite();
|
||||
try
|
||||
{
|
||||
_transaction.CommitWrite();
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
_fileStorage.ConvertException(_ioc, ex);
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>TRACE;DEBUG;EXCLUDE_TWOFISH;EXCLUDE_KEYBOARD;EXCLUDE_KEYTRANSFORM;INCLUDE_FILECHOOSER;INCLUDE_JAVAFILESTORAGE</DefineConstants>
|
||||
<DefineConstants>TRACE;DEBUG;EXCLUDE_TWOFISH;EXCLUDE_KEYBOARD;EXCLUDE_KEYTRANSFORM;EXCLUDE_FILECHOOSER;EXCLUDE_JAVAFILESTORAGE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
@ -44,6 +44,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Mono.Android" />
|
||||
<Reference Include="Mono.Security" />
|
||||
<Reference Include="mscorlib" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
|
@ -46,6 +46,7 @@ namespace keepass2android
|
||||
CannotMoveGroupHere,
|
||||
ErrorOcurred,
|
||||
SynchronizingOtpAuxFile,
|
||||
SavingOtpAuxFile
|
||||
SavingOtpAuxFile,
|
||||
CertificateFailure
|
||||
}
|
||||
}
|
@ -18,9 +18,9 @@ namespace Kp2aUnitTests
|
||||
{
|
||||
TestRunner runner = new TestRunner();
|
||||
// Run all tests from this assembly
|
||||
runner.AddTests(Assembly.GetExecutingAssembly());
|
||||
//runner.AddTests(new List<Type> { typeof(TestSynchronizeCachedDatabase) });
|
||||
//runner.AddTests(typeof(TestSaveDbCached).GetMethod("TestLoadEditSaveWhenModified"));
|
||||
//runner.AddTests(Assembly.GetExecutingAssembly());
|
||||
//runner.AddTests(new List<Type> { typeof(TestSynchronizeCachedDatabase)});
|
||||
runner.AddTests(typeof(TestLoadDb).GetMethod("LoadErrorWithCertificateTrustFailure"));
|
||||
|
||||
//runner.AddTests(new List<Type> { typeof(TestSaveDb) });
|
||||
//runner.AddTests(new List<Type> { typeof(TestCachingFileStorage) });
|
||||
|
2
src/Kp2aUnitTests/Resources/Resource.Designer.cs
generated
2
src/Kp2aUnitTests/Resources/Resource.Designer.cs
generated
@ -2,7 +2,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// Dieser Code wurde von einem Tool generiert.
|
||||
// Laufzeitversion:4.0.30319.18051
|
||||
// Laufzeitversion:4.0.30319.34003
|
||||
//
|
||||
// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
|
||||
// der Code erneut generiert wird.
|
||||
|
@ -66,7 +66,7 @@ namespace Kp2aUnitTests
|
||||
var app = CreateTestKp2aApp();
|
||||
app.CreateNewDatabase();
|
||||
bool loadSuccesful = false;
|
||||
LoadDb task = new LoadDb(app, new IOConnectionInfo() { Path = filename }, null, password, keyfile, new ActionOnFinish((success, message) =>
|
||||
LoadDb task = new LoadDb(app, new IOConnectionInfo() { Path = filename }, null, CreateKey(password, keyfile), keyfile, new ActionOnFinish((success, message) =>
|
||||
{
|
||||
if (!success)
|
||||
Kp2aLog.Log(message);
|
||||
@ -80,7 +80,20 @@ namespace Kp2aUnitTests
|
||||
Assert.IsTrue(loadSuccesful);
|
||||
return app;
|
||||
}
|
||||
|
||||
protected static CompositeKey CreateKey(string password, string keyfile)
|
||||
{
|
||||
CompositeKey key = new CompositeKey();
|
||||
key.AddUserKey(new KcpPassword(password));
|
||||
if (!String.IsNullOrEmpty(keyfile))
|
||||
key.AddUserKey(new KcpKeyFile(keyfile));
|
||||
return key;
|
||||
}
|
||||
protected static CompositeKey CreateKey(string password)
|
||||
{
|
||||
CompositeKey key = new CompositeKey();
|
||||
key.AddUserKey(new KcpPassword(password));
|
||||
return key;
|
||||
}
|
||||
protected virtual TestKp2aApp CreateTestKp2aApp()
|
||||
{
|
||||
TestKp2aApp app = new TestKp2aApp();
|
||||
|
@ -14,6 +14,12 @@ namespace Kp2aUnitTests
|
||||
[TestClass]
|
||||
class TestCachingFileStorage: TestBase
|
||||
{
|
||||
[TestInitialize]
|
||||
public void InitTests()
|
||||
{
|
||||
TestFileStorage.Offline = false;
|
||||
}
|
||||
|
||||
private TestFileStorage _testFileStorage;
|
||||
private CachingFileStorage _fileStorage;
|
||||
private static readonly string CachingTestFile = DefaultDirectory + "cachingTestFile.txt";
|
||||
@ -37,7 +43,7 @@ namespace Kp2aUnitTests
|
||||
Assert.AreEqual(MemoryStreamToString(fileContents), _defaultCacheFileContents);
|
||||
|
||||
//let the base file storage go offline:
|
||||
_testFileStorage.Offline = true;
|
||||
TestFileStorage.Offline = true;
|
||||
|
||||
//now try to read the file again:
|
||||
MemoryStream fileContents2 = ReadToMemoryStream(_fileStorage, CachingTestFile);
|
||||
@ -98,7 +104,7 @@ namespace Kp2aUnitTests
|
||||
_testCacheSupervisor.AssertSingleCall(TestCacheSupervisor.UpdatedCachedFileOnLoadId);
|
||||
|
||||
//let the base file storage go offline:
|
||||
_testFileStorage.Offline = true;
|
||||
TestFileStorage.Offline = true;
|
||||
|
||||
//write something to the cache:
|
||||
string newContent = "new content";
|
||||
@ -116,7 +122,7 @@ namespace Kp2aUnitTests
|
||||
Assert.AreEqual(MemoryStreamToString(fileContents2), newContent);
|
||||
|
||||
//now go online and read again. This should trigger a sync and the modified data must be returned
|
||||
_testFileStorage.Offline = false;
|
||||
TestFileStorage.Offline = false;
|
||||
MemoryStream fileContents3 = ReadToMemoryStream(_fileStorage, CachingTestFile);
|
||||
Assert.AreEqual(MemoryStreamToString(fileContents3), newContent);
|
||||
|
||||
@ -141,7 +147,7 @@ namespace Kp2aUnitTests
|
||||
_testCacheSupervisor.AssertSingleCall(TestCacheSupervisor.UpdatedCachedFileOnLoadId);
|
||||
|
||||
//let the base file storage go offline:
|
||||
_testFileStorage.Offline = true;
|
||||
TestFileStorage.Offline = true;
|
||||
|
||||
//write something to the cache:
|
||||
string newLocalContent = "new local content";
|
||||
@ -153,7 +159,7 @@ namespace Kp2aUnitTests
|
||||
File.WriteAllText(CachingTestFile, "new remote content");
|
||||
|
||||
//go online again:
|
||||
_testFileStorage.Offline = false;
|
||||
TestFileStorage.Offline = false;
|
||||
|
||||
//now try to read the file again:
|
||||
MemoryStream fileContents2 = ReadToMemoryStream(_fileStorage, CachingTestFile);
|
||||
@ -231,7 +237,7 @@ namespace Kp2aUnitTests
|
||||
|
||||
private void SetupFileStorage()
|
||||
{
|
||||
_testFileStorage = new TestFileStorage();
|
||||
_testFileStorage = new TestFileStorage(new TestKp2aApp());
|
||||
_testCacheSupervisor = new TestCacheSupervisor();
|
||||
//_fileStorage = new CachingFileStorage(_testFileStorage, Application.Context.CacheDir.Path, _testCacheSupervisor);
|
||||
_fileStorage = new CachingFileStorage(_testFileStorage, "/mnt/sdcard/kp2atest_cache", _testCacheSupervisor);
|
||||
|
@ -1,16 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using KeePassLib.Serialization;
|
||||
using keepass2android;
|
||||
using keepass2android.Io;
|
||||
|
||||
namespace Kp2aUnitTests
|
||||
{
|
||||
internal class TestFileStorage: IFileStorage
|
||||
{
|
||||
private BuiltInFileStorage _builtIn = new BuiltInFileStorage();
|
||||
public TestFileStorage(IKp2aApp app)
|
||||
{
|
||||
_builtIn = new BuiltInFileStorage(app);
|
||||
}
|
||||
private BuiltInFileStorage _builtIn;
|
||||
|
||||
public bool Offline { get; set; }
|
||||
public static bool Offline { get; set; }
|
||||
|
||||
public IEnumerable<string> SupportedProtocols { get { yield return "test"; } }
|
||||
|
||||
@ -96,6 +103,11 @@ namespace Kp2aUnitTests
|
||||
return _builtIn.RequiresCredentials(ioc);
|
||||
}
|
||||
|
||||
public void CreateDirectory(IOConnectionInfo ioc, string newDirName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void CreateDirectory(IOConnectionInfo ioc)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
@ -110,5 +122,66 @@ namespace Kp2aUnitTests
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool RequiresSetup(IOConnectionInfo ioConnection)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public string IocToPath(IOConnectionInfo ioc)
|
||||
{
|
||||
return ioc.Path;
|
||||
}
|
||||
|
||||
public void StartSelectFile(IFileStorageSetupInitiatorActivity activity, bool isForSave, int requestCode, string protocolId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void PrepareFileUsage(IFileStorageSetupInitiatorActivity activity, IOConnectionInfo ioc, int requestCode,
|
||||
bool alwaysReturnSuccess)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void OnCreate(IFileStorageSetupActivity activity, Bundle savedInstanceState)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void OnResume(IFileStorageSetupActivity activity)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void OnStart(IFileStorageSetupActivity activity)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void OnActivityResult(IFileStorageSetupActivity activity, int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public string GetDisplayName(IOConnectionInfo ioc)
|
||||
{
|
||||
return ioc.Path;
|
||||
}
|
||||
|
||||
public string CreateFilePath(string parent, string newFilename)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IOConnectionInfo GetParentPath(IOConnectionInfo ioc)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IOConnectionInfo GetFilePath(IOConnectionInfo folderPath, string filename)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net.Security;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using KeePassLib.Keys;
|
||||
using KeePassLib.Serialization;
|
||||
using keepass2android;
|
||||
using keepass2android.Io;
|
||||
@ -30,18 +32,27 @@ namespace Kp2aUnitTests
|
||||
|
||||
}
|
||||
|
||||
public virtual TestFileStorage TestFileStorage
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_testFileStorage != null)
|
||||
return _testFileStorage;
|
||||
return (TestFileStorage) FileStorage;
|
||||
}
|
||||
set { _testFileStorage = value; }
|
||||
}
|
||||
|
||||
public void LockDatabase(bool allowQuickUnlock = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, string password, string keyFile,
|
||||
public void LoadDatabase(IOConnectionInfo ioConnectionInfo, MemoryStream memoryStream, CompositeKey compKey,
|
||||
ProgressDialogStatusLogger statusLogger)
|
||||
{
|
||||
_db.LoadData(this, ioConnectionInfo, memoryStream, password, statusLogger);
|
||||
|
||||
_db.LoadData(this, ioConnectionInfo, memoryStream, compKey, statusLogger);
|
||||
}
|
||||
|
||||
public Database GetDb()
|
||||
{
|
||||
return _db;
|
||||
@ -128,10 +139,11 @@ namespace Kp2aUnitTests
|
||||
|
||||
|
||||
public bool TriggerReloadCalled;
|
||||
private TestFileStorage _testFileStorage;
|
||||
|
||||
public TestKp2aApp()
|
||||
{
|
||||
FileStorage = new BuiltInFileStorage();
|
||||
FileStorage = new BuiltInFileStorage(this);
|
||||
}
|
||||
|
||||
public void TriggerReload(Context ctx)
|
||||
@ -139,6 +151,16 @@ namespace Kp2aUnitTests
|
||||
TriggerReloadCalled = true;
|
||||
}
|
||||
|
||||
public bool OnServerCertificateError(int sslPolicyErrors)
|
||||
{
|
||||
ServerCertificateErrorCalled = true;
|
||||
return ServerCertificateErrorResponse;
|
||||
}
|
||||
|
||||
public bool ServerCertificateErrorResponse { get; set; }
|
||||
|
||||
protected bool ServerCertificateErrorCalled { get; set; }
|
||||
|
||||
public void SetYesNoCancelResult(YesNoCancelResult yesNoCancelResult)
|
||||
{
|
||||
_yesNoCancelResult = yesNoCancelResult;
|
||||
|
@ -1,9 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Android.App;
|
||||
using Android.OS;
|
||||
using KeePassLib.Serialization;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using keepass2android;
|
||||
@ -21,8 +19,10 @@ namespace Kp2aUnitTests
|
||||
IKp2aApp app = new TestKp2aApp();
|
||||
app.CreateNewDatabase();
|
||||
bool loadSuccesful = false;
|
||||
LoadDb task = new LoadDb(app, new IOConnectionInfo() { Path = TestDbDirectory+filenameWithoutDir }, null,
|
||||
password, keyfile, new ActionOnFinish((success, message) =>
|
||||
var key = CreateKey(password, keyfile);
|
||||
|
||||
LoadDb task = new LoadDb(app, new IOConnectionInfo { Path = TestDbDirectory+filenameWithoutDir }, null,
|
||||
key, keyfile, new ActionOnFinish((success, message) =>
|
||||
{
|
||||
if (!success)
|
||||
Android.Util.Log.Debug("KP2ATest", "error loading db: " + message);
|
||||
@ -40,6 +40,8 @@ namespace Kp2aUnitTests
|
||||
Assert.AreEqual(2,app.GetDb().KpDatabase.RootGroup.Entries.Count());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void TestLoadWithPasswordOnly()
|
||||
@ -74,11 +76,12 @@ namespace Kp2aUnitTests
|
||||
public void LoadFromRemoteWithDomain()
|
||||
{
|
||||
var ioc = RemoteDomainIoc; //note: this property is defined in "TestLoadDbCredentials.cs" which is deliberately excluded from Git because the credentials are not public!
|
||||
IKp2aApp app = new TestKp2aApp();
|
||||
var app = new TestKp2aApp();
|
||||
app.ServerCertificateErrorResponse = true; //accept invalid cert
|
||||
app.CreateNewDatabase();
|
||||
|
||||
bool loadSuccesful = false;
|
||||
LoadDb task = new LoadDb(app, ioc, null, "a", null, new ActionOnFinish((success, message) =>
|
||||
LoadDb task = new LoadDb(app, ioc, null, CreateKey("a"), null, new ActionOnFinish((success, message) =>
|
||||
{
|
||||
if (!success)
|
||||
Android.Util.Log.Debug("KP2ATest", "error loading db: " + message);
|
||||
@ -95,14 +98,69 @@ namespace Kp2aUnitTests
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void LoadFromRemote1and1()
|
||||
public void LoadErrorWithCertificateTrustFailure()
|
||||
{
|
||||
var ioc = RemoteCertFailureIoc; //note: this property is defined in "TestLoadDbCredentials.cs" which is deliberately excluded from Git because the credentials are not public!
|
||||
var app = new TestKp2aApp();
|
||||
app.ServerCertificateErrorResponse = false;
|
||||
app.CreateNewDatabase();
|
||||
|
||||
bool loadSuccesful = false;
|
||||
string theMessage = "";
|
||||
LoadDb task = new LoadDb(app, ioc, null, CreateKey("test"), null, new ActionOnFinish((success, message) =>
|
||||
{
|
||||
if (!success)
|
||||
Android.Util.Log.Debug("KP2ATest", "error loading db: " + message);
|
||||
loadSuccesful = success;
|
||||
theMessage = message;
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
ProgressTask pt = new ProgressTask(app, Application.Context, task);
|
||||
Android.Util.Log.Debug("KP2ATest", "Running ProgressTask");
|
||||
pt.Run();
|
||||
pt.JoinWorkerThread();
|
||||
Android.Util.Log.Debug("KP2ATest", "PT.run finished");
|
||||
Assert.IsFalse(loadSuccesful, "database should not be loaded because invalid certificates are not accepted");
|
||||
Assert.AreEqual(theMessage, UiStringKey.ErrorOcurred +" "+UiStringKey.CertificateFailure);
|
||||
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void LoadWithAcceptedCertificateTrustFailure()
|
||||
{
|
||||
var ioc = RemoteCertFailureIoc; //note: this property is defined in "TestLoadDbCredentials.cs" which is deliberately excluded from Git because the credentials are not public!
|
||||
var app = new TestKp2aApp();
|
||||
app.ServerCertificateErrorResponse = true;
|
||||
app.CreateNewDatabase();
|
||||
|
||||
bool loadSuccesful = false;
|
||||
LoadDb task = new LoadDb(app, ioc, null, CreateKey("test"), null, new ActionOnFinish((success, message) =>
|
||||
{
|
||||
if (!success)
|
||||
Android.Util.Log.Debug("KP2ATest", "error loading db: " + message);
|
||||
loadSuccesful = success;
|
||||
})
|
||||
);
|
||||
ProgressTask pt = new ProgressTask(app, Application.Context, task);
|
||||
Android.Util.Log.Debug("KP2ATest", "Running ProgressTask");
|
||||
pt.Run();
|
||||
pt.JoinWorkerThread();
|
||||
Android.Util.Log.Debug("KP2ATest", "PT.run finished");
|
||||
Assert.IsTrue(loadSuccesful, "database should be loaded because invalid certificates are accepted");
|
||||
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void LoadFromRemote1And1()
|
||||
{
|
||||
var ioc = RemoteIoc1and1; //note: this property is defined in "TestLoadDbCredentials.cs" which is deliberately excluded from Git because the credentials are not public!
|
||||
IKp2aApp app = new TestKp2aApp();
|
||||
app.CreateNewDatabase();
|
||||
|
||||
bool loadSuccesful = false;
|
||||
LoadDb task = new LoadDb(app, ioc, null, "test", null, new ActionOnFinish((success, message) =>
|
||||
LoadDb task = new LoadDb(app, ioc, null, CreateKey("test"), null, new ActionOnFinish((success, message) =>
|
||||
{
|
||||
if (!success)
|
||||
Android.Util.Log.Debug("KP2ATest", "error loading db: " + message);
|
||||
@ -120,7 +178,7 @@ namespace Kp2aUnitTests
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void LoadFromRemote1and1NonExisting()
|
||||
public void LoadFromRemote1And1NonExisting()
|
||||
{
|
||||
var ioc = RemoteIoc1and1NonExisting; //note: this property is defined in "TestLoadDbCredentials.cs" which is deliberately excluded from Git because the credentials are not public!
|
||||
IKp2aApp app = new TestKp2aApp();
|
||||
@ -128,7 +186,7 @@ namespace Kp2aUnitTests
|
||||
|
||||
bool loadSuccesful = false;
|
||||
bool gotError = false;
|
||||
LoadDb task = new LoadDb(app, ioc, null, "test", null, new ActionOnFinish((success, message) =>
|
||||
LoadDb task = new LoadDb(app, ioc, null, CreateKey("test"), null, new ActionOnFinish((success, message) =>
|
||||
{
|
||||
if (!success)
|
||||
{
|
||||
@ -148,7 +206,7 @@ namespace Kp2aUnitTests
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void LoadFromRemote1and1WrongCredentials()
|
||||
public void LoadFromRemote1And1WrongCredentials()
|
||||
{
|
||||
var ioc = RemoteIoc1and1WrongCredentials; //note: this property is defined in "TestLoadDbCredentials.cs" which is deliberately excluded from Git because the credentials are not public!
|
||||
IKp2aApp app = new TestKp2aApp();
|
||||
@ -156,7 +214,7 @@ namespace Kp2aUnitTests
|
||||
|
||||
bool loadSuccesful = false;
|
||||
bool gotError = false;
|
||||
LoadDb task = new LoadDb(app, ioc, null, "test", null, new ActionOnFinish((success, message) =>
|
||||
LoadDb task = new LoadDb(app, ioc, null, CreateKey("test"), null, new ActionOnFinish((success, message) =>
|
||||
{
|
||||
if (!success)
|
||||
{
|
||||
@ -179,7 +237,7 @@ namespace Kp2aUnitTests
|
||||
[TestMethod]
|
||||
public void FileNotFoundExceptionWithWebDav()
|
||||
{
|
||||
var fileStorage = new BuiltInFileStorage();
|
||||
var fileStorage = new BuiltInFileStorage(new TestKp2aApp());
|
||||
|
||||
//should work:
|
||||
using (var stream = fileStorage.OpenFileForRead(RemoteIoc1and1))
|
||||
|
@ -22,12 +22,12 @@ namespace Kp2aUnitTests
|
||||
class TestSaveDbCached: TestBase
|
||||
{
|
||||
private TestCacheSupervisor _testCacheSupervisor = new TestCacheSupervisor();
|
||||
private TestFileStorage _testFileStorage = new TestFileStorage();
|
||||
|
||||
|
||||
protected override TestKp2aApp CreateTestKp2aApp()
|
||||
{
|
||||
TestKp2aApp app = base.CreateTestKp2aApp();
|
||||
app.FileStorage = new CachingFileStorage(_testFileStorage, "/mnt/sdcard/kp2atest/cache/", _testCacheSupervisor);
|
||||
app.FileStorage = new CachingFileStorage(new TestFileStorage(app), "/mnt/sdcard/kp2atest/cache/", _testCacheSupervisor);
|
||||
return app;
|
||||
}
|
||||
|
||||
|
@ -19,13 +19,20 @@ namespace Kp2aUnitTests
|
||||
[TestClass]
|
||||
internal class TestSynchronizeCachedDatabase : TestBase
|
||||
{
|
||||
[TestInitialize]
|
||||
public void InitTests()
|
||||
{
|
||||
TestFileStorage.Offline = false;
|
||||
}
|
||||
|
||||
private TestCacheSupervisor _testCacheSupervisor = new TestCacheSupervisor();
|
||||
private TestFileStorage _testFileStorage = new TestFileStorage();
|
||||
|
||||
protected override TestKp2aApp CreateTestKp2aApp()
|
||||
{
|
||||
TestKp2aApp app = base.CreateTestKp2aApp();
|
||||
app.FileStorage = new CachingFileStorage(_testFileStorage, "/mnt/sdcard/kp2atest/cache/", _testCacheSupervisor);
|
||||
app.TestFileStorage = new TestFileStorage(app);
|
||||
|
||||
app.FileStorage = new CachingFileStorage(app.TestFileStorage, "/mnt/sdcard/kp2atest/cache/", _testCacheSupervisor);
|
||||
return app;
|
||||
}
|
||||
|
||||
@ -58,7 +65,7 @@ namespace Kp2aUnitTests
|
||||
Assert.AreEqual(resultMessage, app.GetResourceString(UiStringKey.FilesInSync));
|
||||
|
||||
//go offline:
|
||||
_testFileStorage.Offline = true;
|
||||
TestFileStorage.Offline = true;
|
||||
|
||||
//sync when offline (->error)
|
||||
Synchronize(app, out wasSuccessful, out resultMessage);
|
||||
@ -73,7 +80,7 @@ namespace Kp2aUnitTests
|
||||
_testCacheSupervisor.AssertSingleCall(TestCacheSupervisor.CouldntSaveToRemoteId);
|
||||
|
||||
//go online again:
|
||||
_testFileStorage.Offline = false;
|
||||
TestFileStorage.Offline = false;
|
||||
|
||||
//sync with local changes only (-> upload):
|
||||
Synchronize(app, out wasSuccessful, out resultMessage);
|
||||
@ -81,10 +88,10 @@ namespace Kp2aUnitTests
|
||||
Assert.AreEqual(resultMessage, app.GetResourceString(UiStringKey.SynchronizedDatabaseSuccessfully));
|
||||
|
||||
//ensure both files are identical and up to date now:
|
||||
_testFileStorage.Offline = true;
|
||||
TestFileStorage.Offline = true;
|
||||
var appOfflineLoaded = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile);
|
||||
_testCacheSupervisor.AssertSingleCall(TestCacheSupervisor.CouldntOpenFromRemoteId);
|
||||
_testFileStorage.Offline = false;
|
||||
TestFileStorage.Offline = false;
|
||||
var appRemoteLoaded = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile);
|
||||
_testCacheSupervisor.AssertSingleCall(TestCacheSupervisor.LoadedFromRemoteInSyncId);
|
||||
|
||||
@ -92,6 +99,8 @@ namespace Kp2aUnitTests
|
||||
AssertDatabasesAreEqual(app.GetDb().KpDatabase, appRemoteLoaded.GetDb().KpDatabase);
|
||||
}
|
||||
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void TestSyncWhenRemoteDeleted()
|
||||
{
|
||||
@ -133,11 +142,11 @@ namespace Kp2aUnitTests
|
||||
_testCacheSupervisor.AssertSingleCall(TestCacheSupervisor.LoadedFromRemoteInSyncId);
|
||||
|
||||
var app2 = LoadDatabase(DefaultFilename, DefaultPassword, DefaultKeyfile);
|
||||
app2.FileStorage = _testFileStorage; //give app2 direct access to the remote file
|
||||
app2.FileStorage = app.TestFileStorage; //give app2 direct access to the remote file
|
||||
_testCacheSupervisor.AssertSingleCall(TestCacheSupervisor.LoadedFromRemoteInSyncId);
|
||||
|
||||
//go offline:
|
||||
_testFileStorage.Offline = true;
|
||||
TestFileStorage.Offline = true;
|
||||
|
||||
|
||||
string resultMessage;
|
||||
@ -153,7 +162,7 @@ namespace Kp2aUnitTests
|
||||
_testCacheSupervisor.AssertSingleCall(TestCacheSupervisor.CouldntSaveToRemoteId);
|
||||
|
||||
//go online again:
|
||||
_testFileStorage.Offline = false;
|
||||
TestFileStorage.Offline = false;
|
||||
|
||||
//...and remote only for "app2":
|
||||
SaveDatabase(app2);
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="30" android:versionName="0.9.2" package="keepass2android.keepass2android" android:installLocation="auto">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="31" android:versionName="0.9.2-r2" package="keepass2android.keepass2android" android:installLocation="auto">
|
||||
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="14" />
|
||||
<permission android:description="@string/permission_desc" android:icon="@drawable/ic_launcher" android:label="KP2A internal file browsing" android:name="keepass2android.keepass2android.permission.KP2aInternalFileBrowsing" android:protectionLevel="signature" />
|
||||
<application android:label="keepass2android" android:icon="@drawable/ic_launcher">
|
||||
|
1634
src/keepass2android/Resources/Resource.designer.cs
generated
1634
src/keepass2android/Resources/Resource.designer.cs
generated
File diff suppressed because it is too large
Load Diff
@ -89,6 +89,7 @@
|
||||
<string name="UseFileTransactions_key">UseFileTransactions</string>
|
||||
<string name="LockWhenScreenOff_key">LockWhenScreenOff</string>
|
||||
<string name="UseOfflineCache_key">UseOfflineCache</string>
|
||||
<string name="AcceptAllServerCertificates_key">AcceptAllServerCertificates</string>
|
||||
<string name="CheckForFileChangesOnSave_key">CheckForFileChangesOnSave</string>
|
||||
|
||||
<string name="MarketURL">market://details?id=</string>
|
||||
@ -122,6 +123,13 @@
|
||||
<item>28</item>
|
||||
</string-array>
|
||||
|
||||
<string name="AcceptAllServerCertificates_default">WARN</string>
|
||||
<string-array name="AcceptAllServerCertificates_values">
|
||||
<item>IGNORE</item>
|
||||
<item>WARN</item>
|
||||
<item>ERROR</item>
|
||||
</string-array>
|
||||
|
||||
<string name="ShowUnlockedNotification_key">ShowUnlockedNotification</string>
|
||||
<bool name="ShowUnlockedNotification_default">true</bool>
|
||||
|
||||
|
@ -239,6 +239,11 @@
|
||||
<string name="LockWhenScreenOff_summary">Lock the database when screen is switched off.</string>
|
||||
<string name="UseOfflineCache_title">Database caching</string>
|
||||
<string name="UseOfflineCache_summary">Keep a copy of remote database files in the application cache directory. This allows to use remote databases even when offline.</string>
|
||||
|
||||
<string name="AcceptAllServerCertificates_title">SSL certificates</string>
|
||||
<string name="AcceptAllServerCertificates_summary">Define the behavior when certificate validation fails. Note: you can install certificates on your device if validation fails!</string>
|
||||
|
||||
|
||||
<string name="ClearOfflineCache_title">Clear cache?</string>
|
||||
<string name="ClearOfflineCache_question">This will delete all cached database files. Any changes you made while being offline which have not yet been synchronized will be lost! Continue?</string>
|
||||
<string name="CheckForFileChangesOnSave_title">Check for modifications</string>
|
||||
@ -372,16 +377,20 @@
|
||||
<string name="SavingOtpAuxFile">Saving auxiliary OTP file…</string>
|
||||
|
||||
<string name="loading">Loading…</string>
|
||||
|
||||
<string name="CertificateWarning">Warning: Server certificate validation failed: %1$s. Install appropriate root certificate on your device or see settings!</string>
|
||||
<string name="CertificateFailure">Error: Server certificate validation failed! Install appropriate root certificate on your device or see settings!</string>
|
||||
|
||||
<string name="ChangeLog_title">Change log</string>
|
||||
|
||||
<string name="ChangeLog_0_9_2">
|
||||
<b>Version 0.9.2b (preview)</b>\n
|
||||
<b>Version 0.9.2</b>\n
|
||||
* Added OTP support (compatible with OtpKeyProv plugin)\n
|
||||
* Integrated NFC support for OTPs from YubiKey NEO \n
|
||||
* Several UI improvements\n
|
||||
* Integrated Keepass 2.24 library\n
|
||||
* Added option to kill the app process (see settings)\n
|
||||
* Improved SSL certificate validation\n
|
||||
* Bug fixes\n
|
||||
</string>
|
||||
|
||||
@ -501,4 +510,10 @@ Initial public release
|
||||
<item>Password + OTP</item>
|
||||
<item>Password + OTP secret (recovery mode)</item>
|
||||
</string-array>
|
||||
<string-array name="AcceptAllServerCertificates_options">
|
||||
<item>Ignore certificate validation failures</item>
|
||||
<item>Warn when validation fails</item>
|
||||
<item>Do not accept invalid certificates</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
|
@ -204,6 +204,16 @@
|
||||
android:defaultValue="true"
|
||||
android:title="@string/UseOfflineCache_title"
|
||||
android:key="@string/UseOfflineCache_key" />
|
||||
<ListPreference
|
||||
android:key="@string/AcceptAllServerCertificates_key"
|
||||
android:title="@string/AcceptAllServerCertificates_title"
|
||||
android:summary="@string/AcceptAllServerCertificates_summary"
|
||||
android:entries="@array/AcceptAllServerCertificates_options"
|
||||
android:entryValues="@array/AcceptAllServerCertificates_values"
|
||||
android:dialogTitle="@string/AcceptAllServerCertificates_title"
|
||||
android:defaultValue="@string/AcceptAllServerCertificates_default"/>
|
||||
|
||||
|
||||
<CheckBoxPreference
|
||||
android:enabled="true"
|
||||
android:persistent="true"
|
||||
|
@ -353,7 +353,7 @@ namespace keepass2android
|
||||
public IFileStorage GetFileStorage(IOConnectionInfo iocInfo)
|
||||
{
|
||||
if (iocInfo.IsLocalFile())
|
||||
return new BuiltInFileStorage();
|
||||
return new BuiltInFileStorage(this);
|
||||
else
|
||||
{
|
||||
IFileStorage innerFileStorage = GetCloudFileStorage(iocInfo);
|
||||
@ -399,7 +399,7 @@ namespace keepass2android
|
||||
new GoogleDriveFileStorage(Application.Context, this),
|
||||
new SkyDriveFileStorage(Application.Context, this),
|
||||
#endif
|
||||
new BuiltInFileStorage()
|
||||
new BuiltInFileStorage(this)
|
||||
};
|
||||
}
|
||||
return _fileStorages;
|
||||
@ -415,6 +415,61 @@ namespace keepass2android
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
private String GetProblemMessage(BuiltInFileStorage.CertificateProblem problem)
|
||||
{
|
||||
String problemMessage;
|
||||
const BuiltInFileStorage.CertificateProblem problemList = new BuiltInFileStorage.CertificateProblem();
|
||||
string problemCodeName = Enum.GetName(typeof(BuiltInFileStorage.CertificateProblem), problem);
|
||||
|
||||
if (problemCodeName != null)
|
||||
problemMessage = problemCodeName;
|
||||
else
|
||||
problemMessage = "Unknown Certificate Problem";
|
||||
return problemMessage;
|
||||
}
|
||||
|
||||
enum ValidationMode
|
||||
{
|
||||
Ignore, Warn, Error
|
||||
}
|
||||
|
||||
public bool OnServerCertificateError(int certificateProblem)
|
||||
{
|
||||
var prefs = PreferenceManager.GetDefaultSharedPreferences(Application.Context);
|
||||
|
||||
ValidationMode validationMode = ValidationMode.Warn;
|
||||
|
||||
string strValMode = prefs.GetString(Application.Context.Resources.GetString(Resource.String.AcceptAllServerCertificates_key),
|
||||
Application.Context.Resources.GetString(Resource.String.AcceptAllServerCertificates_default));
|
||||
|
||||
if (strValMode == "IGNORE")
|
||||
validationMode = ValidationMode.Ignore;
|
||||
else if (strValMode == "ERROR")
|
||||
validationMode = ValidationMode.Error;
|
||||
;
|
||||
|
||||
switch (validationMode)
|
||||
{
|
||||
case ValidationMode.Ignore:
|
||||
return true;
|
||||
case ValidationMode.Warn:
|
||||
ShowToast(Application.Context.GetString(Resource.String.CertificateWarning,
|
||||
new Java.Lang.Object[]
|
||||
{
|
||||
GetProblemMessage(
|
||||
(BuiltInFileStorage.CertificateProblem)
|
||||
(System.UInt32) certificateProblem)
|
||||
}));
|
||||
return true;
|
||||
case ValidationMode.Error:
|
||||
return false;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal void OnTerminate()
|
||||
{
|
||||
@ -492,7 +547,7 @@ namespace keepass2android
|
||||
|
||||
public void ClearOfflineCache()
|
||||
{
|
||||
new CachingFileStorage(new BuiltInFileStorage(), Application.Context.CacheDir.Path, this).ClearCache();
|
||||
new CachingFileStorage(new BuiltInFileStorage(this), Application.Context.CacheDir.Path, this).ClearCache();
|
||||
}
|
||||
|
||||
public IFileStorage GetFileStorage(string protocolId)
|
||||
@ -508,7 +563,7 @@ namespace keepass2android
|
||||
{
|
||||
|
||||
if (iocInfo.IsLocalFile())
|
||||
return new BuiltInFileStorage();
|
||||
return new BuiltInFileStorage(this);
|
||||
else
|
||||
{
|
||||
IFileStorage innerFileStorage = GetCloudFileStorage(iocInfo);
|
||||
|
@ -29,7 +29,7 @@
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>False</Optimize>
|
||||
<OutputPath>bin\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;EXCLUDE_TWOFISH;INCLUDE_KEYBOARD;EXCLUDE_KEYTRANSFORM;INCLUDE_FILECHOOSER;INCLUDE_JAVAFILESTORAGE</DefineConstants>
|
||||
<DefineConstants>DEBUG;EXCLUDE_TWOFISH;EXCLUDE_KEYBOARD;EXCLUDE_KEYTRANSFORM;EXCLUDE_FILECHOOSER;EXCLUDE_JAVAFILESTORAGE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>False</ConsolePause>
|
||||
|
Loading…
Reference in New Issue
Block a user