Compare commits
156 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
135d7ebda5 | ||
![]() |
337297e3e0 | ||
![]() |
26def9805f | ||
![]() |
5429876cde | ||
![]() |
d13b2d236b | ||
![]() |
9dce637d22 | ||
![]() |
dbb291fb86 | ||
![]() |
bc2dc1b2e9 | ||
![]() |
2d9429d688 | ||
![]() |
62f3ff1902 | ||
![]() |
36735cdfbe | ||
![]() |
f57db90d7b | ||
![]() |
97aec23384 | ||
![]() |
eb3bcf04ea | ||
![]() |
40626ba708 | ||
![]() |
882597365b | ||
![]() |
6b456c2f57 | ||
![]() |
515ae57e88 | ||
![]() |
9cc9d6ab66 | ||
![]() |
40b4fd210a | ||
![]() |
2ce2d0efab | ||
![]() |
97a863293d | ||
![]() |
d45426a4c1 | ||
![]() |
d5a49db782 | ||
![]() |
4b393f412c | ||
![]() |
8cc77fbe1d | ||
![]() |
673632bd92 | ||
![]() |
d2b9a5d1a9 | ||
![]() |
2bc46b0a0e | ||
![]() |
74200daf90 | ||
![]() |
b89c127df4 | ||
![]() |
67bbbb2097 | ||
![]() |
c6ddf0de87 | ||
![]() |
0ef73952b1 | ||
![]() |
0e02a3aeee | ||
![]() |
1558a7c386 | ||
![]() |
b72becd328 | ||
![]() |
df2ac8e203 | ||
![]() |
c6c230002c | ||
![]() |
885503b5fa | ||
![]() |
40fd91257e | ||
![]() |
02ece3cf41 | ||
![]() |
901c879647 | ||
![]() |
990a73f4a4 | ||
![]() |
73169d339d | ||
![]() |
265b86c031 | ||
![]() |
7c29c23015 | ||
![]() |
538e579477 | ||
![]() |
73db5f55e2 | ||
![]() |
31a9980343 | ||
![]() |
02c50f510f | ||
![]() |
3541ee0512 | ||
![]() |
e72821ba1e | ||
![]() |
3af00871a5 | ||
![]() |
22fe4ca790 | ||
![]() |
72d0570c15 | ||
![]() |
62e0cc0867 | ||
![]() |
8987480491 | ||
![]() |
70bb4c2478 | ||
![]() |
cf079a6be4 | ||
![]() |
f153cdcba1 | ||
![]() |
4e048240c5 | ||
![]() |
c098bb1ca0 | ||
![]() |
e02ded734c | ||
![]() |
e721f29f5a | ||
![]() |
f763d5f936 | ||
![]() |
a3368eb557 | ||
![]() |
b5758347c2 | ||
![]() |
775a6d92aa | ||
![]() |
2ab5995177 | ||
![]() |
f6e847d43f | ||
![]() |
cac2ca3d18 | ||
![]() |
d1e870cee0 | ||
![]() |
ca0f65767b | ||
![]() |
7968bfc262 | ||
![]() |
ed4bbe9814 | ||
![]() |
6e83eb6da2 | ||
![]() |
00949b6135 | ||
![]() |
f3a857f26f | ||
![]() |
4bd03372aa | ||
![]() |
dcc589c57a | ||
![]() |
3018ec8cf0 | ||
![]() |
c452a66a4f | ||
![]() |
7a860f8564 | ||
![]() |
94f6f4bdff | ||
![]() |
a627dac4b7 | ||
![]() |
848b6862be | ||
![]() |
d181575e93 | ||
![]() |
778775055f | ||
![]() |
22ccda8d34 | ||
![]() |
c5fc6fd72e | ||
![]() |
c8a4978b5f | ||
![]() |
9e88f8c884 | ||
![]() |
2e543bf4d3 | ||
![]() |
caf42d423f | ||
![]() |
dc39f874ac | ||
![]() |
d1c7a124cf | ||
![]() |
8ec17ce9a6 | ||
![]() |
012af35e87 | ||
![]() |
78bd1f4a5d | ||
![]() |
4477158182 | ||
![]() |
6e96021047 | ||
![]() |
48f57eea66 | ||
![]() |
4b6cfefaf3 | ||
![]() |
0e34cc7f26 | ||
![]() |
27fb2870ab | ||
![]() |
ee6b7c4fe0 | ||
![]() |
0464cecde5 | ||
![]() |
b237259599 | ||
![]() |
8c379739f0 | ||
![]() |
4fb8db982c | ||
![]() |
921b50b642 | ||
![]() |
031332e8ab | ||
![]() |
f03ccced8d | ||
![]() |
51973d225c | ||
![]() |
a59666e752 | ||
![]() |
8259191e50 | ||
![]() |
3eb84cc955 | ||
![]() |
b1b7bff09d | ||
![]() |
82f28bfc8c | ||
![]() |
dfed92ac61 | ||
![]() |
0d6c9b468e | ||
![]() |
2bc12c510b | ||
![]() |
f3022a19c2 | ||
![]() |
800afae1c9 | ||
![]() |
3c19f8e76f | ||
![]() |
3ca462f46f | ||
![]() |
fadd21ebd0 | ||
![]() |
a10c474ce5 | ||
![]() |
513ea5a198 | ||
![]() |
4bab1c32d7 | ||
![]() |
39064eb2c6 | ||
![]() |
28a60f5243 | ||
![]() |
56b9b878f8 | ||
![]() |
e7ad6e32e3 | ||
![]() |
a1d6347db3 | ||
![]() |
9e80013e28 | ||
![]() |
e66a8a0b21 | ||
![]() |
77593969b2 | ||
![]() |
186fa35f70 | ||
![]() |
c39d0048a8 | ||
![]() |
5fc22b9530 | ||
![]() |
51735c3f6d | ||
![]() |
f14aad0c50 | ||
![]() |
cdbb492f2c | ||
![]() |
de18aefd7b | ||
![]() |
5f8807d62c | ||
![]() |
e42d8b8eeb | ||
![]() |
d67b8b8298 | ||
![]() |
10c8d157f5 | ||
![]() |
1f10558f1f | ||
![]() |
409228285e | ||
![]() |
0ac7758c04 | ||
![]() |
802e3d04b2 | ||
![]() |
39ef4a4711 | ||
![]() |
b2215e1db6 |
6
crowdin.yml
Normal file
6
crowdin.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
files:
|
||||
- source: src/keepass2android/Resources/values/strings.xml
|
||||
translation: >-
|
||||
/src/keepass2android/Resources/values-%two_letters_code%/%original_file_name%
|
||||
translate_attributes: '0'
|
||||
content_segmentation: '0'
|
@@ -7,11 +7,12 @@ If you think something is missing in the documentation, please create an issue a
|
||||
If you store important information using Keepass2Android, you should know a little bit about what's going on:
|
||||
* Keepass2Android stores your password in an encrypted file. It is *your responsibility* to backup this file regularly and safely.
|
||||
* There is no way for anyone, including the app's author, to access the information stored in your password database without
|
||||
** having the database file
|
||||
** knowing the master password (and additional second factor if you chose one)
|
||||
This means that **if you forget the master password, your database is lost**! So make sure you remember the password. You might also want to think about:
|
||||
** What happens if I have an accident? Should any trusted person be able to access my database?
|
||||
** What happens if my phone gets lost or stolen? Do I know how to recover my database from a backup or the cloud?
|
||||
* having the database file
|
||||
* knowing the master password (and additional second factor if you chose one)
|
||||
This means that **if you forget the master password, your database is lost**! So make sure you remember the password and retain any second factor method (if one is used).
|
||||
* You might also want to think about:
|
||||
* What happens if I have an accident? Should any trusted person be able to access my database?
|
||||
* What happens if my phone gets lost or stolen? Do I know how to recover my database from a backup or the cloud?
|
||||
|
||||
|
||||
# Getting started
|
||||
|
@@ -4,28 +4,28 @@
|
||||
on how to set up a Keepass 2 database with Yubikey/OTP protection.<br>
|
||||
<br>
|
||||
After successful setup you should have the database file, e.g. yubi.kdbx, and the OTP auxiliary file, e.g. yubi.otp.xml, both in the same folder.<br>
|
||||
<a href="http://download-codeplex.sec.s-msft.com/Download?ProjectName=keepass2android&DownloadId=767825"><img title="OTPAuxFile" src="http://download-codeplex.sec.s-msft.com/Download?ProjectName=keepass2android&DownloadId=767826" alt="OTPAuxFile" width="513" height="40" border="0" style="padding-top:0px; padding-left:0px; display:inline; padding-right:0px; border:0px"></a></p>
|
||||
<a href="How to use Keepass2Android with YubiKey NEO_OTPAuxFile_2.png"><img title="OTPAuxFile" src="How to use Keepass2Android with YubiKey NEO_OTPAuxFile_thumb.png" alt="OTPAuxFile" width="513" height="40" border="0" style="padding-top:0px; padding-left:0px; display:inline; padding-right:0px; border:0px"></a></p>
|
||||
<p>Make sure you make <strong>both files</strong> available to Keepass2Android, e.g. by placing them both in your Dropbox.</p>
|
||||
<p>Now you should check your NDEF setup of the Yubikey NEO. Therefore, go to the Tools menu in the Yubico Personalization Utility. Select the same slot as used for OTPs with Keepass 2. The default setting for NDEF type and payload should work. If you experience
|
||||
problems, you may use the configuration as shown in this screenshot or simply press the “Reset” button:</p>
|
||||
<p><a href="http://download-codeplex.sec.s-msft.com/Download?ProjectName=keepass2android&DownloadId=767911"><img title="image" src="http://download-codeplex.sec.s-msft.com/Download?ProjectName=keepass2android&DownloadId=767912" alt="image" width="760" height="622" border="0" style="padding-top:0px; padding-left:0px; display:inline; padding-right:0px; border:0px"></a></p>
|
||||
<p><a href="How to use Keepass2Android with YubiKey NEO_image_2.png"><img title="image" src="How to use Keepass2Android with YubiKey NEO_image_thumb.png" alt="image" width="760" height="622" border="0" style="padding-top:0px; padding-left:0px; display:inline; padding-right:0px; border:0px"></a></p>
|
||||
<p><br>
|
||||
<br>
|
||||
In Keepass2Android, select "Open file" and locate your database file, e.g. yubi.kdbx.<br>
|
||||
<br>
|
||||
In the password screen under "Select master key type" select "Password + OTP".</p>
|
||||
<p><a href="http://download-codeplex.sec.s-msft.com/Download?ProjectName=keepass2android&DownloadId=767913"><img title="Screenshot_2013-12-13-06-38-50" src="http://download-codeplex.sec.s-msft.com/Download?ProjectName=keepass2android&DownloadId=767914" alt="Screenshot_2013-12-13-06-38-50" width="204" height="360" border="0" style="padding-top:0px; padding-left:0px; display:inline; padding-right:0px; border:0px"></a></p>
|
||||
<p><a href="How to use Keepass2Android with YubiKey NEO_Screenshot_2013-12-13-06-38-50_2.png"><img title="Screenshot_2013-12-13-06-38-50" src="How to use Keepass2Android with YubiKey NEO_Screenshot_2013-12-13-06-38-50_thumb.png" alt="Screenshot_2013-12-13-06-38-50" width="204" height="360" border="0" style="padding-top:0px; padding-left:0px; display:inline; padding-right:0px; border:0px"></a></p>
|
||||
<p>Click "Load auxiliary OTP file". This is required to load the information how many OTPs must be entered. As loading the file might require user action in some cases, this is not performed automatically.<br>
|
||||
<a href="http://download-codeplex.sec.s-msft.com/Download?ProjectName=keepass2android&DownloadId=767915"><img title="Screenshot_2013-12-13-06-38-12" src="http://download-codeplex.sec.s-msft.com/Download?ProjectName=keepass2android&DownloadId=767916" alt="Screenshot_2013-12-13-06-38-12" width="204" height="360" border="0" style="padding-top:0px; padding-left:0px; display:inline; padding-right:0px; border:0px"></a><br>
|
||||
<a href="How to use Keepass2Android with YubiKey NEO_Screenshot_2013-12-13-06-38-12_2.png"><img title="Screenshot_2013-12-13-06-38-12" src="How to use Keepass2Android with YubiKey NEO_Screenshot_2013-12-13-06-38-12_thumb.png" alt="Screenshot_2013-12-13-06-38-12" width="204" height="360" border="0" style="padding-top:0px; padding-left:0px; display:inline; padding-right:0px; border:0px"></a><br>
|
||||
After loading the OTP auxiliary file, you should see a few text fields for entering the OTPs. Now swipe your YubiKey NEO at the back of your Android device. If you have multiple apps which can handle NFC actions, you might be prompted to select which app to
|
||||
use. Select Keepass2Android in this case. Swipe your YubiKey again until all OTP fields are filled. Note: You don't need to select the next text field, this is done automatically!<br>
|
||||
<a href="http://download-codeplex.sec.s-msft.com/Download?ProjectName=keepass2android&DownloadId=767917"><img title="Screenshot_2013-12-13-06-38-36" src="http://download-codeplex.sec.s-msft.com/Download?ProjectName=keepass2android&DownloadId=767918" alt="Screenshot_2013-12-13-06-38-36" width="204" height="360" border="0" style="padding-top:0px; padding-left:0px; display:inline; padding-right:0px; border:0px"></a><br>
|
||||
<a href="How to use Keepass2Android with YubiKey NEO_Screenshot_2013-12-13-06-38-36_2.png"><img title="Screenshot_2013-12-13-06-38-36" src="How to use Keepass2Android with YubiKey NEO_Screenshot_2013-12-13-06-38-36_thumb.png" alt="Screenshot_2013-12-13-06-38-36" width="204" height="360" border="0" style="padding-top:0px; padding-left:0px; display:inline; padding-right:0px; border:0px"></a><br>
|
||||
Don't forget to also enter your password and click OK. You will see the “Saving auxiliary OTP file…” dialog. Note that there is some encryption envolved which is probably fast on your PC but might take some time on your mobile device. You
|
||||
can reduce the look-ahead window length to speed this up.<br>
|
||||
<a href="http://download-codeplex.sec.s-msft.com/Download?ProjectName=keepass2android&DownloadId=767919"><img title="Screenshot_2013-12-13-06-39-47" src="http://download-codeplex.sec.s-msft.com/Download?ProjectName=keepass2android&DownloadId=767920" alt="Screenshot_2013-12-13-06-39-47" width="204" height="360" border="0" style="padding-top:0px; padding-left:0px; display:inline; padding-right:0px; border:0px"></a></p>
|
||||
<a href="How to use Keepass2Android with YubiKey NEO_Screenshot_2013-12-13-06-39-47_2.png"><img title="Screenshot_2013-12-13-06-39-47" src="How to use Keepass2Android with YubiKey NEO_Screenshot_2013-12-13-06-39-47_thumb.png" alt="Screenshot_2013-12-13-06-39-47" width="204" height="360" border="0" style="padding-top:0px; padding-left:0px; display:inline; padding-right:0px; border:0px"></a></p>
|
||||
<h2> </h2>
|
||||
<h2>A note about offline access</h2>
|
||||
<p>If your database is stored in the cloud or on the web, you can still access it if you have enabled file caching (which is on by default). With OTPs, this becomes a little bit more complicated: If you repeatedly open your datbase while being offline, the
|
||||
OTP counter stored on the Yubikey will be increased. Don’t forget to synchronize the database (which will also synchronize the OTP auxiliary file) as soon as possible to avoid problems with accessing your database on other devices! If you often need
|
||||
to open the database while you’re offline, consider increasing the look-ahead window length!</p>
|
||||
</div><div class="ClearBoth"></div>
|
||||
</div><div class="ClearBoth"></div>
|
||||
|
@@ -1,7 +1,6 @@
|
||||
|
||||
# What is Keepass2Android?
|
||||
Keepass2Android is a password manager app. It allows to store and retrieve passwords and other sensitive information in a file called "database". This database is secured with a so-called master password. The master password typically is a strong password and can be complemented with a second factor for additional security.
|
||||
The password database file can be synchronized across different devices. This works best using one of the built-in cloud storage options, but can also be performed with third-party apps. Keepass2Android is compatible with Keepass 1 and Keepass 2 on Windows and KepassX on Linux.
|
||||
The password database file can be synchronized across different devices. This works best using one of the built-in cloud storage options, but can also be performed with third-party apps. Keepass2Android is compatible with Keepass 1 and Keepass 2 on Windows and KeepassX on Linux.
|
||||
|
||||
# Where to get it?
|
||||
Regular stable releases of Keepass2Android are available on [Google Play](https://play.google.com/store/apps/details?id=keepass2android.keepass2android).
|
||||
|
@@ -26,6 +26,7 @@
|
||||
android:hint="@string/http_auth_dialog_password"
|
||||
android:inputType="textPassword"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="20dp" />
|
||||
android:paddingBottom="20dp"
|
||||
android:importantForAccessibility="no" />
|
||||
|
||||
</LinearLayout>
|
BIN
src/JavaFileStorageBindings/Jars/adal-1.14.0.aar
Normal file
BIN
src/JavaFileStorageBindings/Jars/adal-1.14.0.aar
Normal file
Binary file not shown.
@@ -60,6 +60,7 @@
|
||||
</LibraryProjectZip>
|
||||
<None Include="Jars\AboutJars.txt" />
|
||||
<None Include="Additions\AboutAdditions.txt" />
|
||||
<LibraryProjectZip Include="Jars\adal-1.14.0.aar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<TransformFile Include="Transforms\Metadata.xml" />
|
||||
@@ -92,9 +93,6 @@
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\onedrive-sdk-android-1.2.2\classes-onedrive-sdk.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\adal-1.1.19\classes-adal.jar" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedReferenceJar Include="Jars\gdrive\commons-logging-1.1.1.jar" />
|
||||
</ItemGroup>
|
||||
|
@@ -55,7 +55,10 @@ namespace KeePassLib.Cryptography.KeyDerivation
|
||||
get { return "AES-KDF"; }
|
||||
}
|
||||
|
||||
public AesKdf()
|
||||
public override byte[] GetSeed(KdfParameters p)
|
||||
{ return p.GetByteArray(ParamSeed); }
|
||||
|
||||
public AesKdf()
|
||||
{
|
||||
}
|
||||
|
||||
|
@@ -68,7 +68,10 @@ namespace KeePassLib.Cryptography.KeyDerivation
|
||||
get { return "Argon2"; }
|
||||
}
|
||||
|
||||
public Argon2Kdf()
|
||||
public override byte[] GetSeed(KdfParameters p)
|
||||
{ return p.GetByteArray(ParamSalt); }
|
||||
|
||||
public Argon2Kdf()
|
||||
{
|
||||
}
|
||||
|
||||
|
@@ -36,7 +36,9 @@ namespace KeePassLib.Cryptography.KeyDerivation
|
||||
get;
|
||||
}
|
||||
|
||||
public virtual KdfParameters GetDefaultParameters()
|
||||
public abstract byte[] GetSeed(KdfParameters p);
|
||||
|
||||
public virtual KdfParameters GetDefaultParameters()
|
||||
{
|
||||
return new KdfParameters(this.Uuid);
|
||||
}
|
||||
|
@@ -20,11 +20,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using KeePassLib.Cryptography;
|
||||
using KeePassLib.Cryptography.KeyDerivation;
|
||||
using KeePassLib.Native;
|
||||
using KeePassLib.Resources;
|
||||
using KeePassLib.Security;
|
||||
using KeePassLib.Utility;
|
||||
@@ -168,7 +165,7 @@ namespace KeePassLib.Keys
|
||||
/// Creates the composite key from the supplied user key sources (password,
|
||||
/// key file, user account, computer ID, etc.).
|
||||
/// </summary>
|
||||
private byte[] CreateRawCompositeKey32(byte[] mPbMasterSeed)
|
||||
private byte[] CreateRawCompositeKey32(byte[] mPbMasterSeed, byte[] mPbKdfSeed)
|
||||
{
|
||||
ValidateUserKeys();
|
||||
|
||||
@@ -178,7 +175,7 @@ namespace KeePassLib.Keys
|
||||
foreach(IUserKey pKey in m_vUserKeys)
|
||||
{
|
||||
if (pKey is ISeedBasedUserKey)
|
||||
((ISeedBasedUserKey)pKey).SetParams(mPbMasterSeed);
|
||||
((ISeedBasedUserKey)pKey).SetParams(mPbMasterSeed, mPbKdfSeed);
|
||||
ProtectedBinary b = pKey.KeyData;
|
||||
if(b != null)
|
||||
{
|
||||
@@ -211,15 +208,17 @@ namespace KeePassLib.Keys
|
||||
{
|
||||
if(p == null) { Debug.Assert(false); throw new ArgumentNullException("p"); }
|
||||
|
||||
byte[] pbRaw32 = CreateRawCompositeKey32(mPbMasterSeed);
|
||||
|
||||
KdfEngine kdf = KdfPool.Get(p.KdfUuid);
|
||||
if (kdf == null) // CryptographicExceptions are translated to "file corrupted"
|
||||
throw new Exception(KLRes.UnknownKdf + MessageService.NewParagraph +
|
||||
KLRes.FileNewVerOrPlgReq + MessageService.NewParagraph +
|
||||
"UUID: " + p.KdfUuid.ToHexString() + ".");
|
||||
|
||||
byte[] pbRaw32 = CreateRawCompositeKey32(mPbMasterSeed, kdf.GetSeed(p));
|
||||
if((pbRaw32 == null) || (pbRaw32.Length != 32))
|
||||
{ Debug.Assert(false); return null; }
|
||||
|
||||
KdfEngine kdf = KdfPool.Get(p.KdfUuid);
|
||||
if(kdf == null) // CryptographicExceptions are translated to "file corrupted"
|
||||
throw new Exception(KLRes.UnknownKdf + MessageService.NewParagraph +
|
||||
KLRes.FileNewVerOrPlgReq + MessageService.NewParagraph +
|
||||
"UUID: " + p.KdfUuid.ToHexString() + ".");
|
||||
|
||||
byte[] pbTrf32 = kdf.Transform(pbRaw32, p);
|
||||
if(pbTrf32 == null) { Debug.Assert(false); return null; }
|
||||
@@ -256,7 +255,7 @@ namespace KeePassLib.Keys
|
||||
|
||||
public interface ISeedBasedUserKey
|
||||
{
|
||||
void SetParams(byte[] masterSeed);
|
||||
void SetParams(byte[] masterSeed, byte[] mPbKdfSeed);
|
||||
}
|
||||
|
||||
public sealed class InvalidCompositeKeyException : Exception
|
||||
|
@@ -52,7 +52,7 @@ namespace keepass2android
|
||||
/// <summary>
|
||||
/// Tell the app that the file from ioc was opened with keyfile.
|
||||
/// </summary>
|
||||
void StoreOpenedFileAsRecent(IOConnectionInfo ioc, string keyfile);
|
||||
void StoreOpenedFileAsRecent(IOConnectionInfo ioc, string keyfile, string displayName = "");
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new database and returns it
|
||||
|
@@ -2,12 +2,16 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using Android;
|
||||
using Android.Content;
|
||||
using Android.Database;
|
||||
using Android.OS;
|
||||
using Android.Provider;
|
||||
using Java.IO;
|
||||
using KeePassLib.Serialization;
|
||||
using KeePassLib.Utility;
|
||||
using Console = System.Console;
|
||||
|
||||
namespace keepass2android.Io
|
||||
{
|
||||
@@ -51,7 +55,20 @@ namespace keepass2android.Io
|
||||
|
||||
public Stream OpenFileForRead(IOConnectionInfo ioc)
|
||||
{
|
||||
return _ctx.ContentResolver.OpenInputStream(Android.Net.Uri.Parse(ioc.Path));
|
||||
try
|
||||
{
|
||||
return _ctx.ContentResolver.OpenInputStream(Android.Net.Uri.Parse(ioc.Path));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e.Message.Contains("requires that you obtain access using ACTION_OPEN_DOCUMENT"))
|
||||
{
|
||||
//looks like permission was revoked.
|
||||
throw new DocumentAccessRevokedException();
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction)
|
||||
@@ -61,8 +78,9 @@ namespace keepass2android.Io
|
||||
|
||||
public string GetFilenameWithoutPathAndExt(IOConnectionInfo ioc)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return UrlUtil.StripExtension(
|
||||
UrlUtil.GetFileName(ioc.Path));
|
||||
}
|
||||
|
||||
public bool RequiresCredentials(IOConnectionInfo ioc)
|
||||
{
|
||||
@@ -267,7 +285,26 @@ namespace keepass2android.Io
|
||||
|
||||
}
|
||||
|
||||
class AndroidContentWriteTransaction : IWriteTransaction
|
||||
public class DocumentAccessRevokedException : Exception
|
||||
{
|
||||
public DocumentAccessRevokedException()
|
||||
{
|
||||
}
|
||||
|
||||
public DocumentAccessRevokedException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public DocumentAccessRevokedException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
protected DocumentAccessRevokedException(SerializationInfo info, StreamingContext context) : base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class AndroidContentWriteTransaction : IWriteTransaction
|
||||
{
|
||||
private readonly string _path;
|
||||
private readonly Context _ctx;
|
||||
|
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
|
||||
using System.Security;
|
||||
@@ -10,10 +11,14 @@ using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Content.PM;
|
||||
using Android.OS;
|
||||
using Android.Preferences;
|
||||
using Android.Support.V13.App;
|
||||
using Android.Support.V4.App;
|
||||
using Java.IO;
|
||||
using Java.Util;
|
||||
using KeePassLib.Serialization;
|
||||
using KeePassLib.Utility;
|
||||
|
||||
using ActivityCompat = Android.Support.V13.App.ActivityCompat;
|
||||
using File = System.IO.File;
|
||||
using FileNotFoundException = System.IO.FileNotFoundException;
|
||||
using IOException = System.IO.IOException;
|
||||
@@ -258,10 +263,14 @@ namespace keepass2android.Io
|
||||
|
||||
if (requiresPermission && (Build.VERSION.SdkInt >= BuildVersionCodes.M))
|
||||
{
|
||||
if (activity.Activity.CheckSelfPermission(Manifest.Permission.WriteExternalStorage) ==
|
||||
if ((activity.Activity.CheckSelfPermission(Manifest.Permission.WriteExternalStorage) ==
|
||||
Permission.Denied)
|
||||
{
|
||||
activity.StartFileUsageProcess(ioc, requestCode, alwaysReturnSuccess);
|
||||
||
|
||||
(activity.Activity.CheckSelfPermission(Manifest.Permission.ReadExternalStorage) ==
|
||||
Permission.Denied))
|
||||
|
||||
{
|
||||
activity.StartFileUsageProcess(ioc, requestCode, alwaysReturnSuccess);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -279,7 +288,7 @@ namespace keepass2android.Io
|
||||
|
||||
public void OnCreate(IFileStorageSetupActivity fileStorageSetupActivity, Bundle savedInstanceState)
|
||||
{
|
||||
((Activity)fileStorageSetupActivity).RequestPermissions(new[] { Manifest.Permission.WriteExternalStorage }, 0);
|
||||
Android.Support.V4.App.ActivityCompat.RequestPermissions(((Activity)fileStorageSetupActivity), new[] { Manifest.Permission.WriteExternalStorage, Manifest.Permission.ReadExternalStorage }, 0);
|
||||
}
|
||||
|
||||
public void OnResume(IFileStorageSetupActivity activity)
|
||||
@@ -372,7 +381,14 @@ namespace keepass2android.Io
|
||||
{
|
||||
if (ioc.IsLocalFile())
|
||||
{
|
||||
if (IsLocalFileFlaggedReadOnly(ioc))
|
||||
if (IsLocalBackup(ioc))
|
||||
{
|
||||
if (reason != null)
|
||||
reason.Result = UiStringKey.ReadOnlyReason_LocalBackup;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IsLocalFileFlaggedReadOnly(ioc))
|
||||
{
|
||||
if (reason != null)
|
||||
reason.Result = UiStringKey.ReadOnlyReason_ReadOnlyFlag;
|
||||
@@ -393,7 +409,22 @@ namespace keepass2android.Io
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsLocalFileFlaggedReadOnly(IOConnectionInfo ioc)
|
||||
private readonly Dictionary<string, bool> _isLocalBackupCache = new Dictionary<string, bool>();
|
||||
public bool IsLocalBackup(IOConnectionInfo ioc)
|
||||
{
|
||||
if (!ioc.IsLocalFile())
|
||||
return false;
|
||||
bool result;
|
||||
if (_isLocalBackupCache.TryGetValue(ioc.Path, out result))
|
||||
return result;
|
||||
|
||||
result = (PreferenceManager.GetDefaultSharedPreferences(Application.Context)
|
||||
.GetBoolean(IoUtil.GetIocPrefKey(ioc, "is_local_backup"), false));
|
||||
_isLocalBackupCache[ioc.Path] = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
private bool IsLocalFileFlaggedReadOnly(IOConnectionInfo ioc)
|
||||
{
|
||||
//see http://stackoverflow.com/a/33292700/292233
|
||||
try
|
||||
@@ -417,7 +448,7 @@ namespace keepass2android.Io
|
||||
public void OnRequestPermissionsResult(IFileStorageSetupActivity fileStorageSetupActivity, int requestCode,
|
||||
string[] permissions, Permission[] grantResults)
|
||||
{
|
||||
fileStorageSetupActivity.State.PutBoolean(PermissionGrantedKey, grantResults[0] == Permission.Granted);
|
||||
fileStorageSetupActivity.State.PutBoolean(PermissionGrantedKey, grantResults.All(res => res == Permission.Granted));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -345,57 +345,7 @@ namespace keepass2android.Io
|
||||
|
||||
private class CachedWriteTransaction: IWriteTransaction
|
||||
{
|
||||
private class CachedWriteMemoryStream : MemoryStream
|
||||
{
|
||||
private readonly IOConnectionInfo ioc;
|
||||
private readonly CachingFileStorage _cachingFileStorage;
|
||||
private readonly bool _useFileTransaction;
|
||||
private bool _closed;
|
||||
|
||||
public CachedWriteMemoryStream(IOConnectionInfo ioc, CachingFileStorage cachingFileStorage, bool useFileTransaction)
|
||||
{
|
||||
this.ioc = ioc;
|
||||
_cachingFileStorage = cachingFileStorage;
|
||||
_useFileTransaction = useFileTransaction;
|
||||
}
|
||||
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
if (_closed) return;
|
||||
|
||||
//write file to cache:
|
||||
//(note: this might overwrite local changes. It's assumed that a sync operation or check was performed before
|
||||
string hash;
|
||||
using (var hashingStream = new HashingStreamEx(File.Create(_cachingFileStorage.CachedFilePath(ioc)), true, new SHA256Managed()))
|
||||
{
|
||||
Position = 0;
|
||||
CopyTo(hashingStream);
|
||||
|
||||
hashingStream.Close();
|
||||
hash = MemUtil.ByteArrayToHexString(hashingStream.Hash);
|
||||
}
|
||||
|
||||
File.WriteAllText(_cachingFileStorage.VersionFilePath(ioc), hash);
|
||||
//update file on remote. This might overwrite changes there as well, see above.
|
||||
Position = 0;
|
||||
if (_cachingFileStorage.IsCached(ioc))
|
||||
{
|
||||
//if the file already is in the cache, it's ok if writing to remote fails.
|
||||
_cachingFileStorage.TryUpdateRemoteFile(this, ioc, _useFileTransaction, hash);
|
||||
}
|
||||
else
|
||||
{
|
||||
//if not, we don't accept a failure (e.g. invalid credentials would always remain a problem)
|
||||
_cachingFileStorage.UpdateRemoteFile(this, ioc, _useFileTransaction, hash);
|
||||
}
|
||||
|
||||
base.Close();
|
||||
|
||||
_closed = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private readonly IOConnectionInfo _ioc;
|
||||
private readonly bool _useFileTransaction;
|
||||
@@ -429,17 +379,48 @@ namespace keepass2android.Io
|
||||
|
||||
public Stream OpenFile()
|
||||
{
|
||||
_memoryStream = new CachedWriteMemoryStream(_ioc, _cachingFileStorage, _useFileTransaction);
|
||||
_memoryStream = new MemoryStream();
|
||||
return _memoryStream;
|
||||
}
|
||||
|
||||
public void CommitWrite()
|
||||
{
|
||||
//the transaction is committed in the stream's Close
|
||||
_committed = true;
|
||||
_memoryStream.Close();
|
||||
|
||||
//write file to cache:
|
||||
//(note: this might overwrite local changes. It's assumed that a sync operation or check was performed before
|
||||
|
||||
byte[] output = _memoryStream.ToArray();
|
||||
|
||||
string hash;
|
||||
using (var hashingStream = new HashingStreamEx(File.Create(_cachingFileStorage.CachedFilePath(_ioc)), true, new SHA256Managed()))
|
||||
{
|
||||
hashingStream.Write(output, 0, output.Length);
|
||||
|
||||
hashingStream.Close();
|
||||
hash = MemUtil.ByteArrayToHexString(hashingStream.Hash);
|
||||
}
|
||||
|
||||
File.WriteAllText(_cachingFileStorage.VersionFilePath(_ioc), hash);
|
||||
//create another memory stream which is open for reading again
|
||||
MemoryStream openMemStream = new MemoryStream(output);
|
||||
//update file on remote. This might overwrite changes there as well, see above.
|
||||
if (_cachingFileStorage.IsCached(_ioc))
|
||||
{
|
||||
//if the file already is in the cache, it's ok if writing to remote fails.
|
||||
_cachingFileStorage.TryUpdateRemoteFile(openMemStream, _ioc, _useFileTransaction, hash);
|
||||
}
|
||||
else
|
||||
{
|
||||
//if not, we don't accept a failure (e.g. invalid credentials would always remain a problem)
|
||||
_cachingFileStorage.UpdateRemoteFile(openMemStream, _ioc, _useFileTransaction, hash);
|
||||
}
|
||||
|
||||
openMemStream.Dispose();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public IWriteTransaction OpenWriteTransaction(IOConnectionInfo ioc, bool useFileTransaction)
|
||||
|
@@ -1,10 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Java.IO;
|
||||
using KeePassLib.Serialization;
|
||||
using KeePassLib.Utility;
|
||||
|
||||
namespace keepass2android.Io
|
||||
{
|
||||
@@ -125,5 +127,53 @@ namespace keepass2android.Io
|
||||
return ctx.FilesDir;
|
||||
}
|
||||
|
||||
}
|
||||
//creates a local ioc where the sourceIoc can be stored to
|
||||
public static IOConnectionInfo GetInternalIoc(IOConnectionInfo sourceIoc, Context ctx)
|
||||
{
|
||||
Java.IO.File internalDirectory = IoUtil.GetInternalDirectory(ctx);
|
||||
string targetPath = UrlUtil.GetFileName(sourceIoc.Path);
|
||||
targetPath = targetPath.Trim("|\\?*<\":>+[]/'".ToCharArray());
|
||||
if (targetPath == "")
|
||||
targetPath = "internal";
|
||||
if (new File(internalDirectory, targetPath).Exists())
|
||||
{
|
||||
int c = 1;
|
||||
var ext = UrlUtil.GetExtension(targetPath);
|
||||
var filenameWithoutExt = UrlUtil.StripExtension(targetPath);
|
||||
do
|
||||
{
|
||||
c++;
|
||||
targetPath = filenameWithoutExt + c;
|
||||
if (!String.IsNullOrEmpty(ext))
|
||||
targetPath += "." + ext;
|
||||
} while (new File(internalDirectory, targetPath).Exists());
|
||||
}
|
||||
return IOConnectionInfo.FromPath(new File(internalDirectory, targetPath).CanonicalPath);
|
||||
}
|
||||
|
||||
public static IOConnectionInfo ImportFileToInternalDirectory(IOConnectionInfo sourceIoc, Context ctx, IKp2aApp app)
|
||||
{
|
||||
var targetIoc = GetInternalIoc(sourceIoc, ctx);
|
||||
|
||||
|
||||
IoUtil.Copy(targetIoc, sourceIoc, app);
|
||||
return targetIoc;
|
||||
}
|
||||
|
||||
public static string GetIocPrefKey(IOConnectionInfo ioc, string suffix)
|
||||
{
|
||||
var iocAsHexString = IocAsHexString(ioc);
|
||||
|
||||
return "kp2a_ioc_key_" + iocAsHexString + suffix;
|
||||
}
|
||||
|
||||
|
||||
public static string IocAsHexString(IOConnectionInfo ioc)
|
||||
{
|
||||
SHA256Managed sha256 = new SHA256Managed();
|
||||
string iocAsHexString =
|
||||
MemUtil.ByteArrayToHexString(sha256.ComputeHash(Encoding.Unicode.GetBytes(ioc.Path.ToCharArray())));
|
||||
return iocAsHexString;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -231,7 +231,7 @@ namespace keepass2android.Io
|
||||
|
||||
|
||||
|
||||
internal Uri IocToUri(IOConnectionInfo ioc)
|
||||
public static Uri IocToUri(IOConnectionInfo ioc)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(ioc.UserName))
|
||||
{
|
||||
@@ -575,7 +575,7 @@ namespace keepass2android.Io
|
||||
{
|
||||
|
||||
_client = _fileStorage.GetClient(_ioc, false);
|
||||
_stream = _client.OpenWrite(_fileStorage.IocToUri(_iocTemp).PathAndQuery);
|
||||
_stream = _client.OpenWrite(NetFtpFileStorage.IocToUri(_iocTemp).PathAndQuery);
|
||||
return _stream;
|
||||
}
|
||||
catch (FtpCommandException ex)
|
||||
@@ -595,8 +595,8 @@ namespace keepass2android.Io
|
||||
//make sure target file does not exist:
|
||||
//try
|
||||
{
|
||||
if (_client.FileExists(_fileStorage.IocToUri(_ioc).PathAndQuery))
|
||||
_client.DeleteFile(_fileStorage.IocToUri(_ioc).PathAndQuery);
|
||||
if (_client.FileExists(NetFtpFileStorage.IocToUri(_ioc).PathAndQuery))
|
||||
_client.DeleteFile(NetFtpFileStorage.IocToUri(_ioc).PathAndQuery);
|
||||
|
||||
}
|
||||
//catch (FtpCommandException)
|
||||
@@ -604,8 +604,8 @@ namespace keepass2android.Io
|
||||
//TODO get a new clien? might be stale
|
||||
}
|
||||
|
||||
_client.Rename(_fileStorage.IocToUri(_iocTemp).PathAndQuery,
|
||||
_fileStorage.IocToUri(_ioc).PathAndQuery);
|
||||
_client.Rename(NetFtpFileStorage.IocToUri(_iocTemp).PathAndQuery,
|
||||
NetFtpFileStorage.IocToUri(_ioc).PathAndQuery);
|
||||
|
||||
}
|
||||
catch (FtpCommandException ex)
|
||||
|
@@ -14,7 +14,8 @@
|
||||
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
||||
<TargetFrameworkVersion>v8.1</TargetFrameworkVersion>
|
||||
<AndroidUseLatestPlatformSdk>true</AndroidUseLatestPlatformSdk>
|
||||
<NuGetPackageImportStamp>06ffb71c</NuGetPackageImportStamp>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
@@ -55,6 +56,36 @@
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Xamarin.Android.Arch.Core.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.Android.Arch.Core.Common.1.0.0\lib\MonoAndroid80\Xamarin.Android.Arch.Core.Common.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Arch.Lifecycle.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.Android.Arch.Lifecycle.Common.1.0.1\lib\MonoAndroid80\Xamarin.Android.Arch.Lifecycle.Common.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Arch.Lifecycle.Runtime, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.Android.Arch.Lifecycle.Runtime.1.0.0\lib\MonoAndroid80\Xamarin.Android.Arch.Lifecycle.Runtime.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Annotations, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.Android.Support.Annotations.26.1.0.1\lib\MonoAndroid80\Xamarin.Android.Support.Annotations.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Compat, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.Android.Support.Compat.26.1.0.1\lib\MonoAndroid80\Xamarin.Android.Support.Compat.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Core.UI, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.Android.Support.Core.UI.26.1.0.1\lib\MonoAndroid80\Xamarin.Android.Support.Core.UI.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Core.Utils, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.Android.Support.Core.Utils.26.1.0.1\lib\MonoAndroid80\Xamarin.Android.Support.Core.Utils.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Fragment, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.Android.Support.Fragment.26.1.0.1\lib\MonoAndroid80\Xamarin.Android.Support.Fragment.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.Media.Compat, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.Android.Support.Media.Compat.26.1.0.1\lib\MonoAndroid80\Xamarin.Android.Support.Media.Compat.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Xamarin.Android.Support.v13, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Xamarin.Android.Support.v13.26.1.0.1\lib\MonoAndroid80\Xamarin.Android.Support.v13.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="database\CheckDatabaseForChanges.cs" />
|
||||
@@ -148,7 +179,35 @@
|
||||
<ItemGroup>
|
||||
<Folder Include="Resources\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||
<Import Project="..\packages\Xamarin.Android.Support.Annotations.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Annotations.targets" Condition="Exists('..\packages\Xamarin.Android.Support.Annotations.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Annotations.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}".</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\Xamarin.Android.Support.Annotations.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Annotations.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Android.Support.Annotations.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Annotations.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.Android.Arch.Core.Common.1.0.0\build\MonoAndroid80\Xamarin.Android.Arch.Core.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Android.Arch.Core.Common.1.0.0\build\MonoAndroid80\Xamarin.Android.Arch.Core.Common.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.Android.Arch.Lifecycle.Common.1.0.1\build\MonoAndroid80\Xamarin.Android.Arch.Lifecycle.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Android.Arch.Lifecycle.Common.1.0.1\build\MonoAndroid80\Xamarin.Android.Arch.Lifecycle.Common.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.Android.Arch.Lifecycle.Runtime.1.0.0\build\MonoAndroid80\Xamarin.Android.Arch.Lifecycle.Runtime.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Android.Arch.Lifecycle.Runtime.1.0.0\build\MonoAndroid80\Xamarin.Android.Arch.Lifecycle.Runtime.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.Android.Support.Compat.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Compat.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Android.Support.Compat.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Compat.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.Android.Support.Core.UI.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Core.UI.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Android.Support.Core.UI.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Core.UI.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.Android.Support.Core.Utils.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Core.Utils.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Android.Support.Core.Utils.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Core.Utils.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.Android.Support.Fragment.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Fragment.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Android.Support.Fragment.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Fragment.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.Android.Support.Media.Compat.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Media.Compat.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Android.Support.Media.Compat.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Media.Compat.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Xamarin.Android.Support.v13.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.v13.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Xamarin.Android.Support.v13.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.v13.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\packages\Xamarin.Android.Arch.Core.Common.1.0.0\build\MonoAndroid80\Xamarin.Android.Arch.Core.Common.targets" Condition="Exists('..\packages\Xamarin.Android.Arch.Core.Common.1.0.0\build\MonoAndroid80\Xamarin.Android.Arch.Core.Common.targets')" />
|
||||
<Import Project="..\packages\Xamarin.Android.Arch.Lifecycle.Common.1.0.1\build\MonoAndroid80\Xamarin.Android.Arch.Lifecycle.Common.targets" Condition="Exists('..\packages\Xamarin.Android.Arch.Lifecycle.Common.1.0.1\build\MonoAndroid80\Xamarin.Android.Arch.Lifecycle.Common.targets')" />
|
||||
<Import Project="..\packages\Xamarin.Android.Arch.Lifecycle.Runtime.1.0.0\build\MonoAndroid80\Xamarin.Android.Arch.Lifecycle.Runtime.targets" Condition="Exists('..\packages\Xamarin.Android.Arch.Lifecycle.Runtime.1.0.0\build\MonoAndroid80\Xamarin.Android.Arch.Lifecycle.Runtime.targets')" />
|
||||
<Import Project="..\packages\Xamarin.Android.Support.Compat.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Compat.targets" Condition="Exists('..\packages\Xamarin.Android.Support.Compat.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Compat.targets')" />
|
||||
<Import Project="..\packages\Xamarin.Android.Support.Core.UI.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Core.UI.targets" Condition="Exists('..\packages\Xamarin.Android.Support.Core.UI.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Core.UI.targets')" />
|
||||
<Import Project="..\packages\Xamarin.Android.Support.Core.Utils.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Core.Utils.targets" Condition="Exists('..\packages\Xamarin.Android.Support.Core.Utils.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Core.Utils.targets')" />
|
||||
<Import Project="..\packages\Xamarin.Android.Support.Fragment.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Fragment.targets" Condition="Exists('..\packages\Xamarin.Android.Support.Fragment.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Fragment.targets')" />
|
||||
<Import Project="..\packages\Xamarin.Android.Support.Media.Compat.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Media.Compat.targets" Condition="Exists('..\packages\Xamarin.Android.Support.Media.Compat.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.Media.Compat.targets')" />
|
||||
<Import Project="..\packages\Xamarin.Android.Support.v13.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.v13.targets" Condition="Exists('..\packages\Xamarin.Android.Support.v13.26.1.0.1\build\MonoAndroid80\Xamarin.Android.Support.v13.targets')" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
|
@@ -30,8 +30,12 @@ namespace keepass2android
|
||||
readonly IKp2aApp _app;
|
||||
private readonly Handler _handler;
|
||||
private string _message = "";
|
||||
private string _submessage;
|
||||
|
||||
public ProgressDialogStatusLogger() {
|
||||
public String SubMessage => _submessage;
|
||||
public String Message => _message;
|
||||
|
||||
public ProgressDialogStatusLogger() {
|
||||
|
||||
}
|
||||
|
||||
@@ -56,6 +60,7 @@ namespace keepass2android
|
||||
|
||||
public void UpdateSubMessage(String submessage)
|
||||
{
|
||||
_submessage = submessage;
|
||||
if (_app != null && _progressDialog != null && _handler != null)
|
||||
{
|
||||
_handler.Post(() =>
|
||||
|
@@ -25,39 +25,107 @@ namespace keepass2android
|
||||
/// <summary>
|
||||
/// Class to run a task while a progress dialog is shown
|
||||
/// </summary>
|
||||
public class ProgressTask {
|
||||
private readonly Handler _handler;
|
||||
public class ProgressTask
|
||||
{
|
||||
//for handling Activity recreation situations, we need access to the currently active task. It must hold that there is no more than one active task.
|
||||
private static ProgressTask _currentTask = null;
|
||||
|
||||
public static void SetNewActiveActivity(Activity activeActivity)
|
||||
{
|
||||
if (_currentTask != null)
|
||||
{
|
||||
_currentTask.ActiveActivity = activeActivity;
|
||||
}
|
||||
}
|
||||
public static void RemoveActiveActivity(Activity activity)
|
||||
{
|
||||
if ((_currentTask != null) && (_currentTask._activeActivity == activity))
|
||||
_currentTask.ActiveActivity = null;
|
||||
|
||||
}
|
||||
|
||||
public Activity ActiveActivity
|
||||
{
|
||||
get { return _activeActivity; }
|
||||
private set
|
||||
{
|
||||
_activeActivity = value;
|
||||
if (_task != null)
|
||||
_task.ActiveActivity = _activeActivity;
|
||||
if (_activeActivity != null)
|
||||
{
|
||||
SetupProgressDialog(_app);
|
||||
_progressDialog.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Handler _handler;
|
||||
private readonly RunnableOnFinish _task;
|
||||
private readonly IProgressDialog _progressDialog;
|
||||
private IProgressDialog _progressDialog;
|
||||
private readonly IKp2aApp _app;
|
||||
private Thread _thread;
|
||||
private Activity _activeActivity;
|
||||
private ProgressDialogStatusLogger _progressDialogStatusLogger;
|
||||
|
||||
public ProgressTask(IKp2aApp app, Context ctx, RunnableOnFinish task) {
|
||||
public ProgressTask(IKp2aApp app, Activity activity, RunnableOnFinish task)
|
||||
{
|
||||
_activeActivity = activity;
|
||||
_task = task;
|
||||
_handler = app.UiThreadHandler;
|
||||
_app = app;
|
||||
|
||||
// Show process dialog
|
||||
_progressDialog = app.CreateProgressDialog(ctx);
|
||||
_progressDialog.SetTitle(_app.GetResourceString(UiStringKey.progress_title));
|
||||
_progressDialog.SetMessage("Initializing...");
|
||||
|
||||
// Set code to run when this is finished
|
||||
_task.OnFinishToRun = new AfterTask(task.OnFinishToRun, _handler, _progressDialog);
|
||||
_task.SetStatusLogger(new ProgressDialogStatusLogger(_app, _handler, _progressDialog));
|
||||
SetupProgressDialog(app);
|
||||
|
||||
// Set code to run when this is finished
|
||||
_task.OnFinishToRun = new AfterTask(activity, task.OnFinishToRun, _handler, this);
|
||||
|
||||
_task.SetStatusLogger(_progressDialogStatusLogger);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void Run() {
|
||||
// Show process dialog
|
||||
_progressDialog.Show();
|
||||
|
||||
private void SetupProgressDialog(IKp2aApp app)
|
||||
{
|
||||
string currentMessage = "Initializing...";
|
||||
string currentSubmessage = "";
|
||||
|
||||
if (_progressDialogStatusLogger != null)
|
||||
{
|
||||
currentMessage = _progressDialogStatusLogger.Message;
|
||||
currentSubmessage = _progressDialogStatusLogger.SubMessage;
|
||||
}
|
||||
|
||||
if (_progressDialog != null)
|
||||
{
|
||||
var pd = _progressDialog;
|
||||
app.UiThreadHandler.Post(() =>
|
||||
{
|
||||
pd.Dismiss();
|
||||
});
|
||||
}
|
||||
|
||||
// Show process dialog
|
||||
_progressDialog = app.CreateProgressDialog(_activeActivity);
|
||||
_progressDialog.SetTitle(_app.GetResourceString(UiStringKey.progress_title));
|
||||
_progressDialogStatusLogger = new ProgressDialogStatusLogger(_app, _handler, _progressDialog);
|
||||
_progressDialogStatusLogger.UpdateMessage(currentMessage);
|
||||
_progressDialogStatusLogger.UpdateSubMessage(currentSubmessage);
|
||||
}
|
||||
|
||||
public void Run(bool allowOverwriteCurrentTask = false)
|
||||
{
|
||||
if ((!allowOverwriteCurrentTask) && (_currentTask != null))
|
||||
throw new Exception("Cannot start another ProgressTask while ProgressTask is already running! " + _task.GetType().Name + "/" + _currentTask._task.GetType().Name);
|
||||
_currentTask = this;
|
||||
|
||||
// Show process dialog
|
||||
_progressDialog.Show();
|
||||
|
||||
|
||||
// Start Thread to Run task
|
||||
_thread = new Thread(_task.Run);
|
||||
_thread.Start();
|
||||
|
||||
}
|
||||
|
||||
public void JoinWorkerThread()
|
||||
@@ -66,11 +134,11 @@ namespace keepass2android
|
||||
}
|
||||
|
||||
private class AfterTask : OnFinish {
|
||||
readonly IProgressDialog _progressDialog;
|
||||
readonly ProgressTask _progressTask;
|
||||
|
||||
public AfterTask (OnFinish finish, Handler handler, IProgressDialog pd): base(finish, handler)
|
||||
public AfterTask (Activity activity, OnFinish finish, Handler handler, ProgressTask pt): base(activity, finish, handler)
|
||||
{
|
||||
_progressDialog = pd;
|
||||
_progressTask = pt;
|
||||
}
|
||||
|
||||
public override void Run() {
|
||||
@@ -79,17 +147,22 @@ namespace keepass2android
|
||||
if (Handler != null) //can be null in tests
|
||||
{
|
||||
// Remove the progress dialog
|
||||
Handler.Post(delegate { _progressDialog.Dismiss(); });
|
||||
Handler.Post(delegate
|
||||
{
|
||||
_progressTask._progressDialog.Dismiss();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
_progressDialog.Dismiss();
|
||||
_progressTask._progressDialog.Dismiss();
|
||||
}
|
||||
_currentTask = null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -63,7 +63,8 @@ namespace keepass2android
|
||||
AskDeletePermanentlyItems,
|
||||
AskDeletePermanentlyItemsNoRecycle,
|
||||
InOfflineMode,
|
||||
DuplicateTitle,
|
||||
DocumentAccessRevoked,
|
||||
DuplicateTitle,
|
||||
TemplateTitle_IdCard,
|
||||
TemplateField_IdCard_Name,
|
||||
TemplateField_IdCard_PlaceOfIssue,
|
||||
@@ -84,6 +85,7 @@ namespace keepass2android
|
||||
AskAddTemplatesMessage,
|
||||
ReadOnlyReason_PreKitKat,
|
||||
ReadOnlyReason_ReadOnlyFlag,
|
||||
ReadOnlyReason_ReadOnlyKitKat
|
||||
ReadOnlyReason_ReadOnlyKitKat,
|
||||
ReadOnlyReason_LocalBackup
|
||||
}
|
||||
}
|
||||
|
@@ -18,8 +18,8 @@ namespace keepass2android
|
||||
private readonly IKp2aApp _app;
|
||||
|
||||
|
||||
public CheckDatabaseForChanges(Context context, IKp2aApp app, OnFinish finish)
|
||||
: base(finish)
|
||||
public CheckDatabaseForChanges(Activity context, IKp2aApp app, OnFinish finish)
|
||||
: base(context, finish)
|
||||
{
|
||||
_context = context;
|
||||
_app = app;
|
||||
|
@@ -21,7 +21,6 @@ using System.IO;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Android.Content;
|
||||
using Java.Lang;
|
||||
using KeePassLib;
|
||||
using KeePassLib.Keys;
|
||||
@@ -152,27 +151,20 @@ namespace keepass2android
|
||||
set { _databaseFormat = value; }
|
||||
}
|
||||
|
||||
public static string GetFingerprintPrefKey(IOConnectionInfo ioc)
|
||||
{
|
||||
var iocAsHexString = IocAsHexString(ioc);
|
||||
public string IocAsHexString()
|
||||
{
|
||||
return IoUtil.IocAsHexString(Ioc);
|
||||
}
|
||||
|
||||
return "kp2a_ioc_" + iocAsHexString;
|
||||
}
|
||||
public static string GetFingerprintPrefKey(IOConnectionInfo ioc)
|
||||
{
|
||||
var iocAsHexString = IoUtil.IocAsHexString(ioc);
|
||||
|
||||
public string IocAsHexString()
|
||||
{
|
||||
return IocAsHexString(Ioc);
|
||||
}
|
||||
return "kp2a_ioc_" + iocAsHexString;
|
||||
}
|
||||
|
||||
private static string IocAsHexString(IOConnectionInfo ioc)
|
||||
{
|
||||
SHA256Managed sha256 = new SHA256Managed();
|
||||
string iocAsHexString =
|
||||
MemUtil.ByteArrayToHexString(sha256.ComputeHash(Encoding.Unicode.GetBytes(ioc.Path.ToCharArray())));
|
||||
return iocAsHexString;
|
||||
}
|
||||
|
||||
public static string GetFingerprintModePrefKey(IOConnectionInfo ioc)
|
||||
public static string GetFingerprintModePrefKey(IOConnectionInfo ioc)
|
||||
{
|
||||
return GetFingerprintPrefKey(ioc) + "_mode";
|
||||
}
|
||||
@@ -225,7 +217,7 @@ namespace keepass2android
|
||||
}
|
||||
|
||||
|
||||
public void SaveData(Context ctx) {
|
||||
public void SaveData() {
|
||||
|
||||
KpDatabase.UseFileTransactions = _app.GetBooleanPreference(PreferenceKey.UseFileTransactions);
|
||||
using (IWriteTransaction trans = _app.GetFileStorage(Ioc).OpenWriteTransaction(Ioc, KpDatabase.UseFileTransactions))
|
||||
|
@@ -11,12 +11,12 @@ namespace keepass2android
|
||||
{
|
||||
public class SynchronizeCachedDatabase: RunnableOnFinish
|
||||
{
|
||||
private readonly Context _context;
|
||||
private readonly Activity _context;
|
||||
private readonly IKp2aApp _app;
|
||||
private SaveDb _saveDb;
|
||||
|
||||
public SynchronizeCachedDatabase(Context context, IKp2aApp app, OnFinish finish)
|
||||
: base(finish)
|
||||
public SynchronizeCachedDatabase(Activity context, IKp2aApp app, OnFinish finish)
|
||||
: base(context, finish)
|
||||
{
|
||||
_context = context;
|
||||
_app = app;
|
||||
@@ -59,7 +59,7 @@ namespace keepass2android
|
||||
if (cachingFileStorage.HasLocalChanges(ioc))
|
||||
{
|
||||
//conflict! need to merge
|
||||
_saveDb = new SaveDb(_context, _app, new ActionOnFinish((success, result) =>
|
||||
_saveDb = new SaveDb(_context, _app, new ActionOnFinish(ActiveActivity, (success, result, activity) =>
|
||||
{
|
||||
if (!success)
|
||||
{
|
||||
|
@@ -16,41 +16,37 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using Android.App;
|
||||
using Android.OS;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
public class ActionOnFinish: OnFinish
|
||||
{
|
||||
public delegate void ActionToPerformOnFinsh(bool success, String message);
|
||||
public delegate void ActionToPerformOnFinsh(bool success, String message, Activity activeActivity);
|
||||
|
||||
readonly ActionToPerformOnFinsh _actionToPerform;
|
||||
|
||||
public ActionOnFinish(ActionToPerformOnFinsh actionToPerform) : base(null, null)
|
||||
public ActionOnFinish(Activity activity, ActionToPerformOnFinsh actionToPerform) : base(activity, null, null)
|
||||
{
|
||||
_actionToPerform = actionToPerform;
|
||||
}
|
||||
|
||||
public ActionOnFinish(ActionToPerformOnFinsh actionToPerform, OnFinish finish) : base(finish)
|
||||
public ActionOnFinish(Activity activity, ActionToPerformOnFinsh actionToPerform, OnFinish finish) : base(activity, finish)
|
||||
{
|
||||
_actionToPerform = actionToPerform;
|
||||
}
|
||||
|
||||
public ActionOnFinish(ActionToPerformOnFinsh actionToPerform, Handler handler) : base(handler)
|
||||
{
|
||||
_actionToPerform = actionToPerform;
|
||||
}
|
||||
|
||||
|
||||
public override void Run()
|
||||
{
|
||||
if (Message == null)
|
||||
Message = "";
|
||||
if (Handler != null)
|
||||
{
|
||||
Handler.Post(() => {_actionToPerform(Success, Message);});
|
||||
Handler.Post(() => {_actionToPerform(Success, Message, ActiveActivity);});
|
||||
}
|
||||
else
|
||||
_actionToPerform(Success, Message);
|
||||
_actionToPerform(Success, Message, ActiveActivity);
|
||||
base.Run();
|
||||
}
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
||||
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using KeePassLib;
|
||||
|
||||
@@ -29,20 +30,20 @@ namespace keepass2android
|
||||
private readonly IKp2aApp _app;
|
||||
private readonly PwEntry _entry;
|
||||
private readonly PwGroup _parentGroup;
|
||||
private readonly Context _ctx;
|
||||
private readonly Activity _ctx;
|
||||
|
||||
public static AddEntry GetInstance(Context ctx, IKp2aApp app, PwEntry entry, PwGroup parentGroup, OnFinish finish) {
|
||||
public static AddEntry GetInstance(Activity ctx, IKp2aApp app, PwEntry entry, PwGroup parentGroup, OnFinish finish) {
|
||||
|
||||
return new AddEntry(ctx, app, entry, parentGroup, finish);
|
||||
}
|
||||
|
||||
protected AddEntry(Context ctx, IKp2aApp app, PwEntry entry, PwGroup parentGroup, OnFinish finish):base(finish) {
|
||||
protected AddEntry(Activity ctx, IKp2aApp app, PwEntry entry, PwGroup parentGroup, OnFinish finish):base(ctx, finish) {
|
||||
_ctx = ctx;
|
||||
_parentGroup = parentGroup;
|
||||
_app = app;
|
||||
_entry = entry;
|
||||
|
||||
_onFinishToRun = new AfterAdd(app.GetDb(), entry, OnFinishToRun);
|
||||
_onFinishToRun = new AfterAdd(ctx, app.GetDb(), entry, OnFinishToRun);
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +69,7 @@ namespace keepass2android
|
||||
private readonly Database _db;
|
||||
private readonly PwEntry _entry;
|
||||
|
||||
public AfterAdd(Database db, PwEntry entry, OnFinish finish):base(finish) {
|
||||
public AfterAdd(Activity activity, Database db, PwEntry entry, OnFinish finish):base(activity, finish) {
|
||||
_db = db;
|
||||
_entry = entry;
|
||||
|
||||
|
@@ -16,6 +16,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
||||
*/
|
||||
|
||||
using System;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using KeePassLib;
|
||||
|
||||
@@ -34,16 +35,16 @@ namespace keepass2android
|
||||
internal PwGroup Group;
|
||||
internal PwGroup Parent;
|
||||
protected bool DontSave;
|
||||
readonly Context _ctx;
|
||||
readonly Activity _ctx;
|
||||
|
||||
|
||||
public static AddGroup GetInstance(Context ctx, IKp2aApp app, string name, int iconid, PwUuid groupCustomIconId, PwGroup parent, OnFinish finish, bool dontSave) {
|
||||
public static AddGroup GetInstance(Activity ctx, IKp2aApp app, string name, int iconid, PwUuid groupCustomIconId, PwGroup parent, OnFinish finish, bool dontSave) {
|
||||
return new AddGroup(ctx, app, name, iconid, groupCustomIconId, parent, finish, dontSave);
|
||||
}
|
||||
|
||||
|
||||
private AddGroup(Context ctx, IKp2aApp app, String name, int iconid, PwUuid groupCustomIconId, PwGroup parent, OnFinish finish, bool dontSave)
|
||||
: base(finish)
|
||||
private AddGroup(Activity ctx, IKp2aApp app, String name, int iconid, PwUuid groupCustomIconId, PwGroup parent, OnFinish finish, bool dontSave)
|
||||
: base(ctx, finish)
|
||||
{
|
||||
_ctx = ctx;
|
||||
_name = name;
|
||||
@@ -53,7 +54,7 @@ namespace keepass2android
|
||||
DontSave = dontSave;
|
||||
_app = app;
|
||||
|
||||
_onFinishToRun = new AfterAdd(this, OnFinishToRun);
|
||||
_onFinishToRun = new AfterAdd(ctx, this, OnFinishToRun);
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +77,7 @@ namespace keepass2android
|
||||
private class AfterAdd : OnFinish {
|
||||
readonly AddGroup _addGroup;
|
||||
|
||||
public AfterAdd(AddGroup addGroup,OnFinish finish): base(finish) {
|
||||
public AfterAdd(Activity activity, AddGroup addGroup,OnFinish finish): base(activity, finish) {
|
||||
_addGroup = addGroup;
|
||||
}
|
||||
|
||||
|
@@ -18,6 +18,7 @@ This file is part of Keepass2Android, Copyright 2016 Philipp Crocoll. This file
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using KeePassLib;
|
||||
using KeePassLib.Security;
|
||||
@@ -128,10 +129,10 @@ namespace keepass2android
|
||||
}
|
||||
|
||||
private readonly IKp2aApp _app;
|
||||
private readonly Context _ctx;
|
||||
private readonly Activity _ctx;
|
||||
|
||||
public AddTemplateEntries(Context ctx, IKp2aApp app, OnFinish finish)
|
||||
: base(finish)
|
||||
public AddTemplateEntries(Activity ctx, IKp2aApp app, OnFinish finish)
|
||||
: base(ctx, finish)
|
||||
{
|
||||
_ctx = ctx;
|
||||
_app = app;
|
||||
@@ -358,7 +359,7 @@ namespace keepass2android
|
||||
private readonly Database _db;
|
||||
private readonly List<PwEntry> _entries;
|
||||
|
||||
public AfterAdd(Database db, List<PwEntry> entries, OnFinish finish):base(finish) {
|
||||
public AfterAdd(Activity activity, Database db, List<PwEntry> entries, OnFinish finish):base(activity, finish) {
|
||||
_db = db;
|
||||
_entries = entries;
|
||||
|
||||
|
@@ -16,7 +16,7 @@ namespace keepass2android.database.edit
|
||||
{
|
||||
public class CopyEntry: AddEntry
|
||||
{
|
||||
public CopyEntry(Context ctx, IKp2aApp app, PwEntry entry, OnFinish finish)
|
||||
public CopyEntry(Activity ctx, IKp2aApp app, PwEntry entry, OnFinish finish)
|
||||
: base(ctx, app, CreateCopy(entry, app), entry.ParentGroup, finish)
|
||||
{
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using KeePassLib;
|
||||
using KeePassLib.Cryptography.KeyDerivation;
|
||||
@@ -31,19 +32,19 @@ namespace keepass2android
|
||||
|
||||
private readonly IOConnectionInfo _ioc;
|
||||
private readonly bool _dontSave;
|
||||
private readonly Context _ctx;
|
||||
private readonly Activity _ctx;
|
||||
private readonly IKp2aApp _app;
|
||||
private CompositeKey _key;
|
||||
|
||||
public CreateDb(IKp2aApp app, Context ctx, IOConnectionInfo ioc, OnFinish finish, bool dontSave): base(finish) {
|
||||
public CreateDb(IKp2aApp app, Activity ctx, IOConnectionInfo ioc, OnFinish finish, bool dontSave): base(ctx, finish) {
|
||||
_ctx = ctx;
|
||||
_ioc = ioc;
|
||||
_dontSave = dontSave;
|
||||
_app = app;
|
||||
}
|
||||
|
||||
public CreateDb(IKp2aApp app, Context ctx, IOConnectionInfo ioc, OnFinish finish, bool dontSave, CompositeKey key)
|
||||
: base(finish)
|
||||
public CreateDb(IKp2aApp app, Activity ctx, IOConnectionInfo ioc, OnFinish finish, bool dontSave, CompositeKey key)
|
||||
: base(ctx, finish)
|
||||
{
|
||||
_ctx = ctx;
|
||||
_ioc = ioc;
|
||||
|
@@ -17,6 +17,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using KeePassLib;
|
||||
using KeePassLib.Interfaces;
|
||||
@@ -28,8 +29,8 @@ namespace keepass2android
|
||||
private readonly PwEntry _entry;
|
||||
private UiStringKey _statusMessage;
|
||||
|
||||
public DeleteEntry(Context ctx, IKp2aApp app, PwEntry entry, OnFinish finish):base(finish, app) {
|
||||
Ctx = ctx;
|
||||
public DeleteEntry(Activity activiy, IKp2aApp app, PwEntry entry, OnFinish finish):base(activiy, finish, app) {
|
||||
Ctx = activiy;
|
||||
Db = app.GetDb();
|
||||
_entry = entry;
|
||||
|
||||
|
@@ -17,6 +17,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using KeePassLib;
|
||||
|
||||
@@ -28,10 +29,10 @@ namespace keepass2android
|
||||
private PwGroup _group;
|
||||
protected bool DontSave;
|
||||
|
||||
public DeleteGroup(Context ctx, IKp2aApp app, PwGroup group, OnFinish finish)
|
||||
: base(finish, app)
|
||||
public DeleteGroup(Activity activity, IKp2aApp app, PwGroup group, OnFinish finish)
|
||||
: base(activity, finish, app)
|
||||
{
|
||||
SetMembers(ctx, app, group, false);
|
||||
SetMembers(activity, app, group, false);
|
||||
}
|
||||
/*
|
||||
public DeleteGroup(Context ctx, Database db, PwGroup group, Activity act, OnFinish finish, bool dontSave)
|
||||
@@ -44,9 +45,9 @@ namespace keepass2android
|
||||
SetMembers(ctx, db, group, null, dontSave);
|
||||
}
|
||||
*/
|
||||
private void SetMembers(Context ctx, IKp2aApp app, PwGroup group, bool dontSave)
|
||||
private void SetMembers(Activity activity, IKp2aApp app, PwGroup group, bool dontSave)
|
||||
{
|
||||
base.SetMembers(ctx, app.GetDb());
|
||||
base.SetMembers(activity, app.GetDb());
|
||||
|
||||
_group = group;
|
||||
DontSave = dontSave;
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using KeePassLib;
|
||||
using KeePassLib.Interfaces;
|
||||
@@ -11,11 +12,11 @@ namespace keepass2android
|
||||
private readonly List<IStructureItem> _elementsToDelete;
|
||||
private readonly bool _canRecycle;
|
||||
|
||||
public DeleteMultipleItems(Context ctx, Database db, List<IStructureItem> elementsToDelete, OnFinish finish, IKp2aApp app)
|
||||
: base(finish, app)
|
||||
public DeleteMultipleItems(Activity activity, Database db, List<IStructureItem> elementsToDelete, OnFinish finish, IKp2aApp app)
|
||||
: base(activity, finish, app)
|
||||
{
|
||||
_elementsToDelete = elementsToDelete;
|
||||
SetMembers(ctx, db);
|
||||
SetMembers(activity, db);
|
||||
|
||||
//determine once. The property is queried for each delete operation, but might return false
|
||||
//after one entry/group is deleted (and thus in recycle bin and thus can't be recycled anymore)
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using KeePassLib;
|
||||
|
||||
@@ -7,8 +8,8 @@ namespace keepass2android
|
||||
{
|
||||
public abstract class DeleteRunnable : RunnableOnFinish
|
||||
{
|
||||
protected DeleteRunnable(OnFinish finish, IKp2aApp app)
|
||||
: base(finish)
|
||||
protected DeleteRunnable(Activity activity, OnFinish finish, IKp2aApp app)
|
||||
: base(activity, finish)
|
||||
{
|
||||
App = app;
|
||||
}
|
||||
@@ -17,11 +18,11 @@ namespace keepass2android
|
||||
|
||||
protected Database Db;
|
||||
|
||||
protected Context Ctx;
|
||||
protected Activity Ctx;
|
||||
|
||||
protected void SetMembers(Context ctx, Database db)
|
||||
protected void SetMembers(Activity activity, Database db)
|
||||
{
|
||||
Ctx = ctx;
|
||||
Ctx = activity;
|
||||
Db = db;
|
||||
}
|
||||
|
||||
@@ -209,7 +210,7 @@ namespace keepass2android
|
||||
Android.Util.Log.Debug("KP2A", "Calling PerformDelete..");
|
||||
PerformDelete(touchedGroups, permanentlyDeletedGroups);
|
||||
|
||||
_onFinishToRun = new ActionOnFinish((success, message) =>
|
||||
_onFinishToRun = new ActionOnFinish(ActiveActivity,(success, message, activity) =>
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
|
@@ -16,6 +16,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
||||
*/
|
||||
|
||||
using System;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using KeePassLib;
|
||||
|
||||
@@ -32,10 +33,10 @@ namespace keepass2android
|
||||
private readonly PwIcon _iconId;
|
||||
private readonly PwUuid _customIconId;
|
||||
internal PwGroup Group;
|
||||
readonly Context _ctx;
|
||||
readonly Activity _ctx;
|
||||
|
||||
public EditGroup(Context ctx, IKp2aApp app, String name, PwIcon iconid, PwUuid customIconId, PwGroup group, OnFinish finish)
|
||||
: base(finish)
|
||||
public EditGroup(Activity ctx, IKp2aApp app, String name, PwIcon iconid, PwUuid customIconId, PwGroup group, OnFinish finish)
|
||||
: base(ctx, finish)
|
||||
{
|
||||
_ctx = ctx;
|
||||
_name = name;
|
||||
@@ -44,7 +45,7 @@ namespace keepass2android
|
||||
_customIconId = customIconId;
|
||||
_app = app;
|
||||
|
||||
_onFinishToRun = new AfterEdit(this, OnFinishToRun);
|
||||
_onFinishToRun = new AfterEdit(ctx, this, OnFinishToRun);
|
||||
}
|
||||
|
||||
|
||||
@@ -64,8 +65,8 @@ namespace keepass2android
|
||||
private class AfterEdit : OnFinish {
|
||||
readonly EditGroup _editGroup;
|
||||
|
||||
public AfterEdit(EditGroup editGroup, OnFinish finish)
|
||||
: base(finish)
|
||||
public AfterEdit(Activity ctx, EditGroup editGroup, OnFinish finish)
|
||||
: base(ctx, finish)
|
||||
{
|
||||
_editGroup = editGroup;
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
||||
*/
|
||||
|
||||
using System;
|
||||
using Android.App;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
@@ -23,7 +24,7 @@ namespace keepass2android
|
||||
public abstract class FileOnFinish : OnFinish {
|
||||
private String _filename = "";
|
||||
|
||||
protected FileOnFinish(FileOnFinish finish):base(finish) {
|
||||
protected FileOnFinish(Activity activity, FileOnFinish finish):base(activity, finish) {
|
||||
}
|
||||
|
||||
public string Filename
|
||||
|
@@ -18,7 +18,9 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Android.App;
|
||||
using KeePassLib;
|
||||
using KeePassLib.Keys;
|
||||
using KeePassLib.Serialization;
|
||||
@@ -34,7 +36,7 @@ namespace keepass2android
|
||||
private readonly bool _rememberKeyfile;
|
||||
IDatabaseFormat _format;
|
||||
|
||||
public LoadDb(IKp2aApp app, IOConnectionInfo ioc, Task<MemoryStream> databaseData, CompositeKey compositeKey, String keyfileOrProvider, OnFinish finish): base(finish)
|
||||
public LoadDb(Activity activity, IKp2aApp app, IOConnectionInfo ioc, Task<MemoryStream> databaseData, CompositeKey compositeKey, String keyfileOrProvider, OnFinish finish): base(activity, finish)
|
||||
{
|
||||
_app = app;
|
||||
_ioc = ioc;
|
||||
@@ -53,9 +55,11 @@ namespace keepass2android
|
||||
{
|
||||
try
|
||||
{
|
||||
//make sure the file data is stored in the recent files list even if loading fails
|
||||
SaveFileData(_ioc, _keyfileOrProvider);
|
||||
|
||||
|
||||
StatusLogger.UpdateMessage(UiStringKey.loading_database);
|
||||
StatusLogger.UpdateMessage(UiStringKey.loading_database);
|
||||
//get the stream data into a single stream variable (databaseStream) regardless whether its preloaded or not:
|
||||
MemoryStream preloadedMemoryStream = _databaseData == null ? null : _databaseData.Result;
|
||||
MemoryStream databaseStream;
|
||||
@@ -134,10 +138,14 @@ namespace keepass2android
|
||||
//now let's go:
|
||||
try
|
||||
{
|
||||
_app.LoadDatabase(_ioc, workingCopy, _compositeKey, StatusLogger, _format);
|
||||
SaveFileData(_ioc, _keyfileOrProvider);
|
||||
_app.LoadDatabase(_ioc, workingCopy, _compositeKey, StatusLogger, _format);
|
||||
Kp2aLog.Log("LoadDB OK");
|
||||
Finish(true, _format.SuccessMessage);
|
||||
|
||||
//make sure the stored access time for the actual file is more recent than that of its backup
|
||||
Thread.Sleep(10);
|
||||
SaveFileData(_ioc, _keyfileOrProvider);
|
||||
|
||||
Finish(true, _format.SuccessMessage);
|
||||
}
|
||||
catch (OldFormatException)
|
||||
{
|
||||
|
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using KeePassLib;
|
||||
using KeePassLib.Interfaces;
|
||||
@@ -12,10 +13,10 @@ namespace keepass2android.database.edit
|
||||
{
|
||||
private readonly List<IStructureItem> _elementsToMove;
|
||||
private readonly PwGroup _targetGroup;
|
||||
private readonly Context _ctx;
|
||||
private readonly Activity _ctx;
|
||||
private readonly IKp2aApp _app;
|
||||
|
||||
public MoveElements(List<IStructureItem> elementsToMove, PwGroup targetGroup, Context ctx, IKp2aApp app, OnFinish finish) : base(finish)
|
||||
public MoveElements(List<IStructureItem> elementsToMove, PwGroup targetGroup, Activity ctx, IKp2aApp app, OnFinish finish) : base(ctx, finish)
|
||||
{
|
||||
_elementsToMove = elementsToMove;
|
||||
_targetGroup = targetGroup;
|
||||
@@ -82,7 +83,7 @@ namespace keepass2android.database.edit
|
||||
}
|
||||
|
||||
|
||||
_onFinishToRun = new ActionOnFinish((success, message) =>
|
||||
_onFinishToRun = new ActionOnFinish(ActiveActivity, (success, message, activity) =>
|
||||
{
|
||||
if (!success)
|
||||
{ // Let's not bother recovering from a failure.
|
||||
|
@@ -16,6 +16,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
||||
*/
|
||||
|
||||
using System;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Android.Widget;
|
||||
@@ -31,27 +32,47 @@ namespace keepass2android
|
||||
protected OnFinish BaseOnFinish;
|
||||
protected Handler Handler;
|
||||
private ProgressDialogStatusLogger _statusLogger = new ProgressDialogStatusLogger(); //default: no logging but not null -> can be used whenever desired
|
||||
|
||||
private Activity _activeActivity;
|
||||
|
||||
public ProgressDialogStatusLogger StatusLogger
|
||||
|
||||
public ProgressDialogStatusLogger StatusLogger
|
||||
{
|
||||
get { return _statusLogger; }
|
||||
set { _statusLogger = value; }
|
||||
}
|
||||
|
||||
public Activity ActiveActivity
|
||||
{
|
||||
get { return _activeActivity; }
|
||||
set
|
||||
{
|
||||
_activeActivity = value;
|
||||
if (BaseOnFinish != null)
|
||||
{
|
||||
BaseOnFinish.ActiveActivity = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected OnFinish(Handler handler) {
|
||||
|
||||
protected OnFinish(Activity activeActivity, Handler handler)
|
||||
{
|
||||
ActiveActivity = activeActivity;
|
||||
BaseOnFinish = null;
|
||||
Handler = handler;
|
||||
|
||||
}
|
||||
|
||||
protected OnFinish(OnFinish finish, Handler handler) {
|
||||
protected OnFinish(Activity activeActivity, OnFinish finish, Handler handler)
|
||||
{
|
||||
ActiveActivity = activeActivity;
|
||||
BaseOnFinish = finish;
|
||||
Handler = handler;
|
||||
}
|
||||
|
||||
protected OnFinish(OnFinish finish) {
|
||||
protected OnFinish(Activity activeActivity, OnFinish finish)
|
||||
{
|
||||
ActiveActivity = activeActivity;
|
||||
BaseOnFinish = finish;
|
||||
Handler = null;
|
||||
}
|
||||
@@ -86,7 +107,7 @@ namespace keepass2android
|
||||
{
|
||||
if ( !String.IsNullOrEmpty(message) ) {
|
||||
Kp2aLog.Log("OnFinish message: "+message);
|
||||
Toast.MakeText(ctx, message, ToastLength.Long).Show();
|
||||
Toast.MakeText(ctx ?? Application.Context, message, ToastLength.Long).Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -15,6 +15,8 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
||||
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using System;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
|
||||
namespace keepass2android
|
||||
{
|
||||
@@ -23,8 +25,11 @@ namespace keepass2android
|
||||
|
||||
protected OnFinish _onFinishToRun;
|
||||
public ProgressDialogStatusLogger StatusLogger = new ProgressDialogStatusLogger(); //default: empty but not null
|
||||
private Activity _activeActivity;
|
||||
|
||||
protected RunnableOnFinish(OnFinish finish) {
|
||||
protected RunnableOnFinish(Activity activeActivity, OnFinish finish)
|
||||
{
|
||||
_activeActivity = activeActivity;
|
||||
_onFinishToRun = finish;
|
||||
}
|
||||
|
||||
@@ -34,7 +39,18 @@ namespace keepass2android
|
||||
set { _onFinishToRun = value; }
|
||||
}
|
||||
|
||||
protected void Finish(bool result, String message, Exception exception = null) {
|
||||
public Activity ActiveActivity
|
||||
{
|
||||
get { return _activeActivity; }
|
||||
set
|
||||
{
|
||||
_activeActivity = value;
|
||||
if (_onFinishToRun != null)
|
||||
_onFinishToRun.ActiveActivity = _activeActivity;
|
||||
}
|
||||
}
|
||||
|
||||
protected void Finish(bool result, String message, Exception exception = null) {
|
||||
if ( OnFinishToRun != null ) {
|
||||
OnFinishToRun.SetResult(result, message, exception);
|
||||
OnFinishToRun.Run();
|
||||
@@ -56,7 +72,7 @@ namespace keepass2android
|
||||
StatusLogger = status;
|
||||
}
|
||||
|
||||
abstract public void Run();
|
||||
public abstract void Run();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -18,6 +18,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Java.Lang;
|
||||
@@ -42,8 +43,8 @@ namespace keepass2android
|
||||
private readonly Context _ctx;
|
||||
private Thread _workerThread;
|
||||
|
||||
public SaveDb(Context ctx, IKp2aApp app, OnFinish finish, bool dontSave)
|
||||
: base(finish)
|
||||
public SaveDb(Activity ctx, IKp2aApp app, OnFinish finish, bool dontSave)
|
||||
: base(ctx, finish)
|
||||
{
|
||||
_ctx = ctx;
|
||||
_app = app;
|
||||
@@ -58,8 +59,8 @@ namespace keepass2android
|
||||
/// <param name="finish"></param>
|
||||
/// <param name="dontSave"></param>
|
||||
/// <param name="streamForOrigFile">Stream for reading the data from the (changed) original location</param>
|
||||
public SaveDb(Context ctx, IKp2aApp app, OnFinish finish, bool dontSave, Stream streamForOrigFile)
|
||||
: base(finish)
|
||||
public SaveDb(Activity ctx, IKp2aApp app, OnFinish finish, bool dontSave, Stream streamForOrigFile)
|
||||
: base(ctx, finish)
|
||||
{
|
||||
_ctx = ctx;
|
||||
_app = app;
|
||||
@@ -67,8 +68,8 @@ namespace keepass2android
|
||||
_streamForOrigFile = streamForOrigFile;
|
||||
}
|
||||
|
||||
public SaveDb(Context ctx, IKp2aApp app, OnFinish finish)
|
||||
: base(finish)
|
||||
public SaveDb(Activity ctx, IKp2aApp app, OnFinish finish)
|
||||
: base(ctx, finish)
|
||||
{
|
||||
_ctx = ctx;
|
||||
_app = app;
|
||||
@@ -248,7 +249,7 @@ namespace keepass2android
|
||||
private void PerformSaveWithoutCheck(IFileStorage fileStorage, IOConnectionInfo ioc)
|
||||
{
|
||||
StatusLogger.UpdateSubMessage("");
|
||||
_app.GetDb().SaveData(_ctx);
|
||||
_app.GetDb().SaveData();
|
||||
_app.GetDb().LastFileVersion = fileStorage.GetCurrentFileVersionFast(ioc);
|
||||
}
|
||||
|
||||
|
@@ -15,6 +15,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
||||
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
using System;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using KeePassLib;
|
||||
using KeePassLib.Keys;
|
||||
@@ -27,9 +28,9 @@ namespace keepass2android
|
||||
private readonly String _keyfile;
|
||||
private readonly IKp2aApp _app;
|
||||
private readonly bool _dontSave;
|
||||
private readonly Context _ctx;
|
||||
private readonly Activity _ctx;
|
||||
|
||||
public SetPassword(Context ctx, IKp2aApp app, String password, String keyfile, OnFinish finish): base(finish) {
|
||||
public SetPassword(Activity ctx, IKp2aApp app, String password, String keyfile, OnFinish finish): base(ctx, finish) {
|
||||
_ctx = ctx;
|
||||
_app = app;
|
||||
_password = password;
|
||||
@@ -37,8 +38,8 @@ namespace keepass2android
|
||||
_dontSave = false;
|
||||
}
|
||||
|
||||
public SetPassword(Context ctx, IKp2aApp app, String password, String keyfile, OnFinish finish, bool dontSave)
|
||||
: base(finish)
|
||||
public SetPassword(Activity ctx, IKp2aApp app, String password, String keyfile, OnFinish finish, bool dontSave)
|
||||
: base(ctx, finish)
|
||||
{
|
||||
_ctx = ctx;
|
||||
_app = app;
|
||||
@@ -72,7 +73,7 @@ namespace keepass2android
|
||||
pm.MasterKey = newKey;
|
||||
|
||||
// Save Database
|
||||
_onFinishToRun = new AfterSave(previousKey, previousMasterKeyChanged, pm, OnFinishToRun);
|
||||
_onFinishToRun = new AfterSave(ActiveActivity, previousKey, previousMasterKeyChanged, pm, OnFinishToRun);
|
||||
SaveDb save = new SaveDb(_ctx, _app, OnFinishToRun, _dontSave);
|
||||
save.SetStatusLogger(StatusLogger);
|
||||
save.Run();
|
||||
@@ -83,7 +84,7 @@ namespace keepass2android
|
||||
private readonly DateTime _previousKeyChanged;
|
||||
private readonly PwDatabase _db;
|
||||
|
||||
public AfterSave(CompositeKey backup, DateTime previousKeyChanged, PwDatabase db, OnFinish finish): base(finish) {
|
||||
public AfterSave(Activity activity, CompositeKey backup, DateTime previousKeyChanged, PwDatabase db, OnFinish finish): base(activity, finish) {
|
||||
_previousKeyChanged = previousKeyChanged;
|
||||
_backup = backup;
|
||||
_db = db;
|
||||
|
@@ -15,6 +15,7 @@ This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file
|
||||
along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using KeePassLib;
|
||||
|
||||
@@ -23,13 +24,13 @@ namespace keepass2android
|
||||
|
||||
public class UpdateEntry : RunnableOnFinish {
|
||||
private readonly IKp2aApp _app;
|
||||
private readonly Context _ctx;
|
||||
private readonly Activity _ctx;
|
||||
|
||||
public UpdateEntry(Context ctx, IKp2aApp app, PwEntry oldE, PwEntry newE, OnFinish finish):base(finish) {
|
||||
public UpdateEntry(Activity ctx, IKp2aApp app, PwEntry oldE, PwEntry newE, OnFinish finish):base(ctx, finish) {
|
||||
_ctx = ctx;
|
||||
_app = app;
|
||||
|
||||
_onFinishToRun = new AfterUpdate(oldE, newE, app, finish);
|
||||
_onFinishToRun = new AfterUpdate(ctx, oldE, newE, app, finish);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +46,7 @@ namespace keepass2android
|
||||
private readonly PwEntry _updatedEntry;
|
||||
private readonly IKp2aApp _app;
|
||||
|
||||
public AfterUpdate(PwEntry backup, PwEntry updatedEntry, IKp2aApp app, OnFinish finish):base(finish) {
|
||||
public AfterUpdate(Activity activity, PwEntry backup, PwEntry updatedEntry, IKp2aApp app, OnFinish finish):base(activity, finish) {
|
||||
_backup = backup;
|
||||
_updatedEntry = updatedEntry;
|
||||
_app = app;
|
||||
|
@@ -1,4 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Xamarin.Android.Arch.Core.Common" version="1.0.0" targetFramework="monoandroid81" />
|
||||
<package id="Xamarin.Android.Arch.Lifecycle.Common" version="1.0.1" targetFramework="monoandroid81" />
|
||||
<package id="Xamarin.Android.Arch.Lifecycle.Runtime" version="1.0.0" targetFramework="monoandroid81" />
|
||||
<package id="Xamarin.Android.Support.Annotations" version="26.1.0.1" targetFramework="monoandroid81" />
|
||||
<package id="Xamarin.Android.Support.Compat" version="26.1.0.1" targetFramework="monoandroid81" />
|
||||
<package id="Xamarin.Android.Support.Core.UI" version="26.1.0.1" targetFramework="monoandroid81" />
|
||||
<package id="Xamarin.Android.Support.Core.Utils" version="26.1.0.1" targetFramework="monoandroid81" />
|
||||
<package id="Xamarin.Android.Support.Fragment" version="26.1.0.1" targetFramework="monoandroid81" />
|
||||
<package id="Xamarin.Android.Support.Media.Compat" version="26.1.0.1" targetFramework="monoandroid81" />
|
||||
<package id="Xamarin.Android.Support.v13" version="26.1.0.1" targetFramework="monoandroid81" />
|
||||
<package id="Xamarin.Android.Support.v4" version="23.1.1.0" targetFramework="MonoAndroid50" />
|
||||
</packages>
|
@@ -32,11 +32,11 @@ dependencies {
|
||||
compile 'com.google.apis:google-api-services-drive:v2-rev102-1.16.0-rc'
|
||||
compile 'com.dropbox.core:dropbox-core-sdk:3.0.3'
|
||||
//onedrive:
|
||||
compile('com.onedrive.sdk:onedrive-sdk-android:1.2+') {
|
||||
compile('com.onedrive.sdk:onedrive-sdk-android:1.2.0') {
|
||||
transitive = false
|
||||
}
|
||||
compile 'com.google.code.gson:gson:2.3.1'
|
||||
compile 'com.microsoft.services.msa:msa-auth:0.8.+'
|
||||
compile 'com.microsoft.aad:adal:1.1.+'
|
||||
compile 'com.microsoft.services.msa:msa-auth:0.8.6'
|
||||
compile 'com.microsoft.aad:adal:1.14.0'
|
||||
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
@@ -213,8 +213,11 @@ public class Buffer{
|
||||
}
|
||||
|
||||
void checkFreeSize(int n){
|
||||
if(buffer.length<index+n){
|
||||
byte[] tmp = new byte[buffer.length*2];
|
||||
int size = index+n+Session.buffer_margin;
|
||||
if(buffer.length<size){
|
||||
int i = buffer.length*2;
|
||||
if(i<size) i = size;
|
||||
byte[] tmp = new byte[i];
|
||||
System.arraycopy(buffer, 0, tmp, 0, index);
|
||||
buffer = tmp;
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
@@ -192,29 +192,38 @@ public abstract class Channel implements Runnable{
|
||||
io.setExtOutputStream(out, dontclose);
|
||||
}
|
||||
public InputStream getInputStream() throws IOException {
|
||||
PipedInputStream in=
|
||||
int max_input_buffer_size = 32*1024;
|
||||
try {
|
||||
max_input_buffer_size =
|
||||
Integer.parseInt(getSession().getConfig("max_input_buffer_size"));
|
||||
}
|
||||
catch(Exception e){}
|
||||
PipedInputStream in =
|
||||
new MyPipedInputStream(
|
||||
32*1024 // this value should be customizable.
|
||||
32*1024, // this value should be customizable.
|
||||
max_input_buffer_size
|
||||
);
|
||||
io.setOutputStream(new PassiveOutputStream(in), false);
|
||||
boolean resizable = 32*1024<max_input_buffer_size;
|
||||
io.setOutputStream(new PassiveOutputStream(in, resizable), false);
|
||||
return in;
|
||||
}
|
||||
public InputStream getExtInputStream() throws IOException {
|
||||
PipedInputStream in=
|
||||
int max_input_buffer_size = 32*1024;
|
||||
try {
|
||||
max_input_buffer_size =
|
||||
Integer.parseInt(getSession().getConfig("max_input_buffer_size"));
|
||||
}
|
||||
catch(Exception e){}
|
||||
PipedInputStream in =
|
||||
new MyPipedInputStream(
|
||||
32*1024 // this value should be customizable.
|
||||
32*1024, // this value should be customizable.
|
||||
max_input_buffer_size
|
||||
);
|
||||
io.setExtOutputStream(new PassiveOutputStream(in), false);
|
||||
boolean resizable = 32*1024<max_input_buffer_size;
|
||||
io.setExtOutputStream(new PassiveOutputStream(in, resizable), false);
|
||||
return in;
|
||||
}
|
||||
public OutputStream getOutputStream() throws IOException {
|
||||
/*
|
||||
PipedOutputStream out=new PipedOutputStream();
|
||||
io.setInputStream(new PassiveInputStream(out
|
||||
, 32*1024
|
||||
), false);
|
||||
return out;
|
||||
*/
|
||||
|
||||
final Channel channel=this;
|
||||
OutputStream out=new OutputStream(){
|
||||
@@ -317,15 +326,24 @@ public abstract class Channel implements Runnable{
|
||||
}
|
||||
|
||||
class MyPipedInputStream extends PipedInputStream{
|
||||
private int BUFFER_SIZE = 1024;
|
||||
private int max_buffer_size = BUFFER_SIZE;
|
||||
MyPipedInputStream() throws IOException{ super(); }
|
||||
MyPipedInputStream(int size) throws IOException{
|
||||
super();
|
||||
buffer=new byte[size];
|
||||
BUFFER_SIZE = size;
|
||||
max_buffer_size = size;
|
||||
}
|
||||
MyPipedInputStream(int size, int max_buffer_size) throws IOException{
|
||||
this(size);
|
||||
this.max_buffer_size = max_buffer_size;
|
||||
}
|
||||
MyPipedInputStream(PipedOutputStream out) throws IOException{ super(out); }
|
||||
MyPipedInputStream(PipedOutputStream out, int size) throws IOException{
|
||||
super(out);
|
||||
buffer=new byte[size];
|
||||
BUFFER_SIZE=size;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -343,12 +361,66 @@ public abstract class Channel implements Runnable{
|
||||
buffer[in++] = 0;
|
||||
read();
|
||||
}
|
||||
|
||||
private int freeSpace(){
|
||||
int size = 0;
|
||||
if(out < in) {
|
||||
size = buffer.length-in;
|
||||
}
|
||||
else if(in < out){
|
||||
if(in == -1) size = buffer.length;
|
||||
else size = out - in;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
synchronized void checkSpace(int len) throws IOException {
|
||||
int size = freeSpace();
|
||||
if(size<len){
|
||||
int datasize=buffer.length-size;
|
||||
int foo = buffer.length;
|
||||
while((foo - datasize) < len){
|
||||
foo*=2;
|
||||
}
|
||||
|
||||
if(foo > max_buffer_size){
|
||||
foo = max_buffer_size;
|
||||
}
|
||||
if((foo - datasize) < len) return;
|
||||
|
||||
byte[] tmp = new byte[foo];
|
||||
if(out < in) {
|
||||
System.arraycopy(buffer, 0, tmp, 0, buffer.length);
|
||||
}
|
||||
else if(in < out){
|
||||
if(in == -1) {
|
||||
}
|
||||
else {
|
||||
System.arraycopy(buffer, 0, tmp, 0, in);
|
||||
System.arraycopy(buffer, out,
|
||||
tmp, tmp.length-(buffer.length-out),
|
||||
(buffer.length-out));
|
||||
out = tmp.length-(buffer.length-out);
|
||||
}
|
||||
}
|
||||
else if(in == out){
|
||||
System.arraycopy(buffer, 0, tmp, 0, buffer.length);
|
||||
in=buffer.length;
|
||||
}
|
||||
buffer=tmp;
|
||||
}
|
||||
else if(buffer.length == size && size > BUFFER_SIZE) {
|
||||
int i = size/2;
|
||||
if(i<BUFFER_SIZE) i = BUFFER_SIZE;
|
||||
byte[] tmp = new byte[i];
|
||||
buffer=tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
void setLocalWindowSizeMax(int foo){ this.lwsize_max=foo; }
|
||||
void setLocalWindowSize(int foo){ this.lwsize=foo; }
|
||||
void setLocalPacketSize(int foo){ this.lmpsize=foo; }
|
||||
synchronized void setRemoteWindowSize(long foo){ this.rwsize=foo; }
|
||||
synchronized void addRemoteWindowSize(int foo){
|
||||
synchronized void addRemoteWindowSize(long foo){
|
||||
this.rwsize+=foo;
|
||||
if(notifyme>0)
|
||||
notifyAll();
|
||||
@@ -384,12 +456,15 @@ public abstract class Channel implements Runnable{
|
||||
if(eof_local)return;
|
||||
eof_local=true;
|
||||
|
||||
int i = getRecipient();
|
||||
if(i == -1) return;
|
||||
|
||||
try{
|
||||
Buffer buf=new Buffer(100);
|
||||
Packet packet=new Packet(buf);
|
||||
packet.reset();
|
||||
buf.putByte((byte)Session.SSH_MSG_CHANNEL_EOF);
|
||||
buf.putInt(getRecipient());
|
||||
buf.putInt(i);
|
||||
synchronized(this){
|
||||
if(!close)
|
||||
getSession().write(packet);
|
||||
@@ -445,12 +520,15 @@ public abstract class Channel implements Runnable{
|
||||
close=true;
|
||||
eof_local=eof_remote=true;
|
||||
|
||||
int i = getRecipient();
|
||||
if(i == -1) return;
|
||||
|
||||
try{
|
||||
Buffer buf=new Buffer(100);
|
||||
Packet packet=new Packet(buf);
|
||||
packet.reset();
|
||||
buf.putByte((byte)Session.SSH_MSG_CHANNEL_CLOSE);
|
||||
buf.putInt(getRecipient());
|
||||
buf.putInt(i);
|
||||
synchronized(this){
|
||||
getSession().write(packet);
|
||||
}
|
||||
@@ -561,8 +639,25 @@ public abstract class Channel implements Runnable{
|
||||
}
|
||||
}
|
||||
class PassiveOutputStream extends PipedOutputStream{
|
||||
PassiveOutputStream(PipedInputStream in) throws IOException{
|
||||
private MyPipedInputStream _sink=null;
|
||||
PassiveOutputStream(PipedInputStream in,
|
||||
boolean resizable_buffer) throws IOException{
|
||||
super(in);
|
||||
if(resizable_buffer && (in instanceof MyPipedInputStream)) {
|
||||
this._sink=(MyPipedInputStream)in;
|
||||
}
|
||||
}
|
||||
public void write(int b) throws IOException {
|
||||
if(_sink != null) {
|
||||
_sink.checkSpace(1);
|
||||
}
|
||||
super.write(b);
|
||||
}
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
if(_sink != null) {
|
||||
_sink.checkSpace(len);
|
||||
}
|
||||
super.write(b, off, len);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -636,7 +731,7 @@ public abstract class Channel implements Runnable{
|
||||
Packet packet = genChannelOpenPacket();
|
||||
_session.write(packet);
|
||||
|
||||
int retry=10;
|
||||
int retry=2000;
|
||||
long start=System.currentTimeMillis();
|
||||
long timeout=connectTimeout;
|
||||
if(timeout!=0L) retry = 1;
|
||||
@@ -651,7 +746,7 @@ public abstract class Channel implements Runnable{
|
||||
}
|
||||
}
|
||||
try{
|
||||
long t = timeout==0L ? 5000L : timeout;
|
||||
long t = timeout==0L ? 10L : timeout;
|
||||
this.notifyme=1;
|
||||
wait(t);
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2006-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2006-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
@@ -120,7 +120,16 @@ public class ChannelDirectTCPIP extends Channel{
|
||||
}
|
||||
}
|
||||
catch(Exception e){
|
||||
// Whenever an exception is thrown by sendChannelOpen(),
|
||||
// 'connected' is false.
|
||||
if(!connected){
|
||||
connected=true;
|
||||
}
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
eof();
|
||||
disconnect();
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
@@ -648,7 +648,6 @@ public class ChannelSftp extends ChannelSession{
|
||||
if((seq-1)==startid ||
|
||||
((seq-startid)-ackcount)>=bulk_requests){
|
||||
while(((seq-startid)-ackcount)>=bulk_requests){
|
||||
if(this.rwsize>=foo) break;
|
||||
if(checkStatus(ackid, header)){
|
||||
int _ackid = ackid[0];
|
||||
if(startid>_ackid || _ackid>seq-1){
|
||||
@@ -666,7 +665,16 @@ public class ChannelSftp extends ChannelSession{
|
||||
}
|
||||
}
|
||||
}
|
||||
foo-=sendWRITE(handle, offset, data, 0, foo);
|
||||
if(dontcopy){
|
||||
foo-=sendWRITE(handle, offset, data, 0, foo);
|
||||
if(data!=obuf.buffer){
|
||||
data=obuf.buffer;
|
||||
_datalen=obuf.buffer.length-_s-Session.buffer_margin;
|
||||
}
|
||||
}
|
||||
else {
|
||||
foo-=sendWRITE(handle, offset, data, _s, foo);
|
||||
}
|
||||
}
|
||||
offset+=count;
|
||||
if(monitor!=null && !monitor.count(count)){
|
||||
@@ -736,6 +744,12 @@ public class ChannelSftp extends ChannelSession{
|
||||
}
|
||||
}
|
||||
|
||||
if(monitor!=null){
|
||||
monitor.init(SftpProgressMonitor.PUT,
|
||||
"-", dst,
|
||||
SftpProgressMonitor.UNKNOWN_SIZE);
|
||||
}
|
||||
|
||||
if(mode==OVERWRITE){ sendOPENW(dstb); }
|
||||
else{ sendOPENA(dstb); }
|
||||
|
||||
@@ -923,6 +937,15 @@ public class ChannelSftp extends ChannelSession{
|
||||
if(i==-1) dstsb.append(_src);
|
||||
else dstsb.append(_src.substring(i + 1));
|
||||
_dst=dstsb.toString();
|
||||
if(_dst.indexOf("..")!=-1){
|
||||
String dstc = (new java.io.File(dst)).getCanonicalPath();
|
||||
String _dstc = (new java.io.File(_dst)).getCanonicalPath();
|
||||
if(!(_dstc.length()>dstc.length() &&
|
||||
_dstc.substring(0, dstc.length()+1).equals(dstc+file_separator))){
|
||||
throw new SftpException(SSH_FX_FAILURE,
|
||||
"writing to an unexpected file "+_src);
|
||||
}
|
||||
}
|
||||
dstsb.delete(dst.length(), _dst.length());
|
||||
}
|
||||
else{
|
||||
@@ -1375,7 +1398,10 @@ public class ChannelSftp extends ChannelSession{
|
||||
len=1024;
|
||||
}
|
||||
|
||||
if(rq.count()==0) {
|
||||
if(rq.count()==0
|
||||
|| true // working around slow transfer speed for
|
||||
// some sftp servers including Titan FTP.
|
||||
) {
|
||||
int request_len = buf.buffer.length-13;
|
||||
if(server_version==0){ request_len=1024; }
|
||||
|
||||
@@ -1782,10 +1808,17 @@ public class ChannelSftp extends ChannelSession{
|
||||
try{
|
||||
((MyPipedInputStream)io_in).updateReadSide();
|
||||
|
||||
oldpath=remoteAbsolutePath(oldpath);
|
||||
String _oldpath=remoteAbsolutePath(oldpath);
|
||||
newpath=remoteAbsolutePath(newpath);
|
||||
|
||||
oldpath=isUnique(oldpath);
|
||||
_oldpath=isUnique(_oldpath);
|
||||
if(oldpath.charAt(0)!='/'){ // relative path
|
||||
String cwd=getCwd();
|
||||
oldpath=_oldpath.substring(cwd.length()+(cwd.endsWith("/")?0:1));
|
||||
}
|
||||
else {
|
||||
oldpath=_oldpath;
|
||||
}
|
||||
|
||||
if(isPattern(newpath)){
|
||||
throw new SftpException(SSH_FX_FAILURE, newpath);
|
||||
@@ -1827,10 +1860,17 @@ public class ChannelSftp extends ChannelSession{
|
||||
try{
|
||||
((MyPipedInputStream)io_in).updateReadSide();
|
||||
|
||||
oldpath=remoteAbsolutePath(oldpath);
|
||||
String _oldpath=remoteAbsolutePath(oldpath);
|
||||
newpath=remoteAbsolutePath(newpath);
|
||||
|
||||
oldpath=isUnique(oldpath);
|
||||
_oldpath=isUnique(_oldpath);
|
||||
if(oldpath.charAt(0)!='/'){ // relative path
|
||||
String cwd=getCwd();
|
||||
oldpath=_oldpath.substring(cwd.length()+(cwd.endsWith("/")?0:1));
|
||||
}
|
||||
else {
|
||||
oldpath=_oldpath;
|
||||
}
|
||||
|
||||
if(isPattern(newpath)){
|
||||
throw new SftpException(SSH_FX_FAILURE, newpath);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2005-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2005-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2013 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2013-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
@@ -36,4 +36,8 @@ public interface DH{
|
||||
byte[] getE() throws Exception;
|
||||
void setF(byte[] f);
|
||||
byte[] getK() throws Exception;
|
||||
|
||||
// checkRange() will check if e and f are in [1,p-1]
|
||||
// as defined at https://tools.ietf.org/html/rfc4253#section-8
|
||||
void checkRange() throws Exception;
|
||||
}
|
||||
|
@@ -0,0 +1,37 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2015-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.jcraft.jsch;
|
||||
|
||||
public class DHEC256 extends DHECN {
|
||||
public DHEC256(){
|
||||
sha_name="sha-256";
|
||||
key_size=256;
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2015-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.jcraft.jsch;
|
||||
|
||||
public class DHEC384 extends DHECN {
|
||||
public DHEC384(){
|
||||
sha_name="sha-384";
|
||||
key_size=384;
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2015-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.jcraft.jsch;
|
||||
|
||||
public class DHEC521 extends DHECN {
|
||||
public DHEC521(){
|
||||
sha_name="sha-512";
|
||||
key_size=521;
|
||||
}
|
||||
}
|
@@ -0,0 +1,187 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2015-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.jcraft.jsch;
|
||||
|
||||
public abstract class DHECN extends KeyExchange{
|
||||
|
||||
private static final int SSH_MSG_KEX_ECDH_INIT = 30;
|
||||
private static final int SSH_MSG_KEX_ECDH_REPLY= 31;
|
||||
private int state;
|
||||
|
||||
byte[] Q_C;
|
||||
|
||||
byte[] V_S;
|
||||
byte[] V_C;
|
||||
byte[] I_S;
|
||||
byte[] I_C;
|
||||
|
||||
byte[] e;
|
||||
|
||||
private Buffer buf;
|
||||
private Packet packet;
|
||||
|
||||
private ECDH ecdh;
|
||||
|
||||
protected String sha_name;
|
||||
protected int key_size;
|
||||
|
||||
public void init(Session session,
|
||||
byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C) throws Exception{
|
||||
this.session=session;
|
||||
this.V_S=V_S;
|
||||
this.V_C=V_C;
|
||||
this.I_S=I_S;
|
||||
this.I_C=I_C;
|
||||
|
||||
try{
|
||||
Class c=Class.forName(session.getConfig(sha_name));
|
||||
sha=(HASH)(c.newInstance());
|
||||
sha.init();
|
||||
}
|
||||
catch(Exception e){
|
||||
System.err.println(e);
|
||||
}
|
||||
|
||||
buf=new Buffer();
|
||||
packet=new Packet(buf);
|
||||
|
||||
packet.reset();
|
||||
buf.putByte((byte)SSH_MSG_KEX_ECDH_INIT);
|
||||
|
||||
try{
|
||||
Class c=Class.forName(session.getConfig("ecdh-sha2-nistp"));
|
||||
ecdh=(ECDH)(c.newInstance());
|
||||
ecdh.init(key_size);
|
||||
|
||||
Q_C = ecdh.getQ();
|
||||
buf.putString(Q_C);
|
||||
}
|
||||
catch(Exception e){
|
||||
if(e instanceof Throwable)
|
||||
throw new JSchException(e.toString(), (Throwable)e);
|
||||
throw new JSchException(e.toString());
|
||||
}
|
||||
|
||||
if(V_S==null){ // This is a really ugly hack for Session.checkKexes ;-(
|
||||
return;
|
||||
}
|
||||
|
||||
session.write(packet);
|
||||
|
||||
if(JSch.getLogger().isEnabled(Logger.INFO)){
|
||||
JSch.getLogger().log(Logger.INFO,
|
||||
"SSH_MSG_KEX_ECDH_INIT sent");
|
||||
JSch.getLogger().log(Logger.INFO,
|
||||
"expecting SSH_MSG_KEX_ECDH_REPLY");
|
||||
}
|
||||
|
||||
state=SSH_MSG_KEX_ECDH_REPLY;
|
||||
}
|
||||
|
||||
public boolean next(Buffer _buf) throws Exception{
|
||||
int i,j;
|
||||
switch(state){
|
||||
case SSH_MSG_KEX_ECDH_REPLY:
|
||||
// The server responds with:
|
||||
// byte SSH_MSG_KEX_ECDH_REPLY
|
||||
// string K_S, server's public host key
|
||||
// string Q_S, server's ephemeral public key octet string
|
||||
// string the signature on the exchange hash
|
||||
j=_buf.getInt();
|
||||
j=_buf.getByte();
|
||||
j=_buf.getByte();
|
||||
if(j!=31){
|
||||
System.err.println("type: must be 31 "+j);
|
||||
return false;
|
||||
}
|
||||
|
||||
K_S=_buf.getString();
|
||||
|
||||
byte[] Q_S=_buf.getString();
|
||||
|
||||
byte[][] r_s = KeyPairECDSA.fromPoint(Q_S);
|
||||
|
||||
// RFC 5656,
|
||||
// 4. ECDH Key Exchange
|
||||
// All elliptic curve public keys MUST be validated after they are
|
||||
// received. An example of a validation algorithm can be found in
|
||||
// Section 3.2.2 of [SEC1]. If a key fails validation,
|
||||
// the key exchange MUST fail.
|
||||
if(!ecdh.validate(r_s[0], r_s[1])){
|
||||
return false;
|
||||
}
|
||||
|
||||
K = ecdh.getSecret(r_s[0], r_s[1]);
|
||||
K=normalize(K);
|
||||
|
||||
byte[] sig_of_H=_buf.getString();
|
||||
|
||||
//The hash H is computed as the HASH hash of the concatenation of the
|
||||
//following:
|
||||
// string V_C, client's identification string (CR and LF excluded)
|
||||
// string V_S, server's identification string (CR and LF excluded)
|
||||
// string I_C, payload of the client's SSH_MSG_KEXINIT
|
||||
// string I_S, payload of the server's SSH_MSG_KEXINIT
|
||||
// string K_S, server's public host key
|
||||
// string Q_C, client's ephemeral public key octet string
|
||||
// string Q_S, server's ephemeral public key octet string
|
||||
// mpint K, shared secret
|
||||
|
||||
// This value is called the exchange hash, and it is used to authenti-
|
||||
// cate the key exchange.
|
||||
buf.reset();
|
||||
buf.putString(V_C); buf.putString(V_S);
|
||||
buf.putString(I_C); buf.putString(I_S);
|
||||
buf.putString(K_S);
|
||||
buf.putString(Q_C); buf.putString(Q_S);
|
||||
buf.putMPInt(K);
|
||||
byte[] foo=new byte[buf.getLength()];
|
||||
buf.getByte(foo);
|
||||
|
||||
sha.update(foo, 0, foo.length);
|
||||
H=sha.digest();
|
||||
|
||||
i=0;
|
||||
j=0;
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
String alg=Util.byte2str(K_S, i, j);
|
||||
i+=j;
|
||||
|
||||
boolean result = verify(alg, K_S, i, sig_of_H);
|
||||
|
||||
state=STATE_END;
|
||||
return result;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getState(){return state; }
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
@@ -55,25 +55,15 @@ public class DHG1 extends KeyExchange{
|
||||
private static final int SSH_MSG_KEXDH_INIT= 30;
|
||||
private static final int SSH_MSG_KEXDH_REPLY= 31;
|
||||
|
||||
static final int RSA=0;
|
||||
static final int DSS=1;
|
||||
private int type=0;
|
||||
|
||||
private int state;
|
||||
|
||||
DH dh;
|
||||
// HASH sha;
|
||||
|
||||
// byte[] K;
|
||||
// byte[] H;
|
||||
|
||||
byte[] V_S;
|
||||
byte[] V_C;
|
||||
byte[] I_S;
|
||||
byte[] I_C;
|
||||
|
||||
// byte[] K_S;
|
||||
|
||||
byte[] e;
|
||||
|
||||
private Buffer buf;
|
||||
@@ -87,8 +77,6 @@ public class DHG1 extends KeyExchange{
|
||||
this.I_S=I_S;
|
||||
this.I_C=I_C;
|
||||
|
||||
// sha=new SHA1();
|
||||
// sha.init();
|
||||
try{
|
||||
Class c=Class.forName(session.getConfig("sha-1"));
|
||||
sha=(HASH)(c.newInstance());
|
||||
@@ -155,24 +143,14 @@ public class DHG1 extends KeyExchange{
|
||||
}
|
||||
|
||||
K_S=_buf.getString();
|
||||
// K_S is server_key_blob, which includes ....
|
||||
// string ssh-dss
|
||||
// impint p of dsa
|
||||
// impint q of dsa
|
||||
// impint g of dsa
|
||||
// impint pub_key of dsa
|
||||
//System.err.print("K_S: "); //dump(K_S, 0, K_S.length);
|
||||
|
||||
byte[] f=_buf.getMPInt();
|
||||
byte[] sig_of_H=_buf.getString();
|
||||
/*
|
||||
for(int ii=0; ii<sig_of_H.length;ii++){
|
||||
System.err.print(Integer.toHexString(sig_of_H[ii]&0xff));
|
||||
System.err.print(": ");
|
||||
}
|
||||
System.err.println("");
|
||||
*/
|
||||
|
||||
dh.setF(f);
|
||||
|
||||
dh.checkRange();
|
||||
|
||||
K=normalize(dh.getK());
|
||||
|
||||
//The hash H is computed as the HASH hash of the concatenation of the
|
||||
@@ -206,105 +184,13 @@ System.err.println("");
|
||||
String alg=Util.byte2str(K_S, i, j);
|
||||
i+=j;
|
||||
|
||||
boolean result=false;
|
||||
boolean result = verify(alg, K_S, i, sig_of_H);
|
||||
|
||||
if(alg.equals("ssh-rsa")){
|
||||
byte[] tmp;
|
||||
byte[] ee;
|
||||
byte[] n;
|
||||
|
||||
type=RSA;
|
||||
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
ee=tmp;
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
n=tmp;
|
||||
|
||||
// SignatureRSA sig=new SignatureRSA();
|
||||
// sig.init();
|
||||
|
||||
SignatureRSA sig=null;
|
||||
try{
|
||||
Class c=Class.forName(session.getConfig("signature.rsa"));
|
||||
sig=(SignatureRSA)(c.newInstance());
|
||||
sig.init();
|
||||
}
|
||||
catch(Exception e){
|
||||
System.err.println(e);
|
||||
}
|
||||
|
||||
sig.setPubKey(ee, n);
|
||||
sig.update(H);
|
||||
result=sig.verify(sig_of_H);
|
||||
|
||||
if(JSch.getLogger().isEnabled(Logger.INFO)){
|
||||
JSch.getLogger().log(Logger.INFO,
|
||||
"ssh_rsa_verify: signature "+result);
|
||||
}
|
||||
|
||||
}
|
||||
else if(alg.equals("ssh-dss")){
|
||||
byte[] q=null;
|
||||
byte[] tmp;
|
||||
byte[] p;
|
||||
byte[] g;
|
||||
|
||||
type=DSS;
|
||||
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
p=tmp;
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
q=tmp;
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
g=tmp;
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
f=tmp;
|
||||
// SignatureDSA sig=new SignatureDSA();
|
||||
// sig.init();
|
||||
SignatureDSA sig=null;
|
||||
try{
|
||||
Class c=Class.forName(session.getConfig("signature.dss"));
|
||||
sig=(SignatureDSA)(c.newInstance());
|
||||
sig.init();
|
||||
}
|
||||
catch(Exception e){
|
||||
System.err.println(e);
|
||||
}
|
||||
sig.setPubKey(f, p, q, g);
|
||||
sig.update(H);
|
||||
result=sig.verify(sig_of_H);
|
||||
|
||||
if(JSch.getLogger().isEnabled(Logger.INFO)){
|
||||
JSch.getLogger().log(Logger.INFO,
|
||||
"ssh_dss_verify: signature "+result);
|
||||
}
|
||||
|
||||
}
|
||||
else{
|
||||
System.err.println("unknown alg");
|
||||
}
|
||||
state=STATE_END;
|
||||
return result;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getKeyType(){
|
||||
if(type==DSS) return "DSA";
|
||||
return "RSA";
|
||||
}
|
||||
|
||||
public int getState(){return state; }
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
@@ -71,10 +71,6 @@ public class DHG14 extends KeyExchange{
|
||||
private static final int SSH_MSG_KEXDH_INIT= 30;
|
||||
private static final int SSH_MSG_KEXDH_REPLY= 31;
|
||||
|
||||
static final int RSA=0;
|
||||
static final int DSS=1;
|
||||
private int type=0;
|
||||
|
||||
private int state;
|
||||
|
||||
DH dh;
|
||||
@@ -166,17 +162,14 @@ public class DHG14 extends KeyExchange{
|
||||
}
|
||||
|
||||
K_S=_buf.getString();
|
||||
// K_S is server_key_blob, which includes ....
|
||||
// string ssh-dss
|
||||
// impint p of dsa
|
||||
// impint q of dsa
|
||||
// impint g of dsa
|
||||
// impint pub_key of dsa
|
||||
//System.err.print("K_S: "); //dump(K_S, 0, K_S.length);
|
||||
|
||||
byte[] f=_buf.getMPInt();
|
||||
byte[] sig_of_H=_buf.getString();
|
||||
|
||||
dh.setF(f);
|
||||
|
||||
dh.checkRange();
|
||||
|
||||
K=normalize(dh.getK());
|
||||
|
||||
//The hash H is computed as the HASH hash of the concatenation of the
|
||||
@@ -210,101 +203,13 @@ public class DHG14 extends KeyExchange{
|
||||
String alg=Util.byte2str(K_S, i, j);
|
||||
i+=j;
|
||||
|
||||
boolean result=false;
|
||||
boolean result = verify(alg, K_S, i, sig_of_H);
|
||||
|
||||
if(alg.equals("ssh-rsa")){
|
||||
byte[] tmp;
|
||||
byte[] ee;
|
||||
byte[] n;
|
||||
|
||||
type=RSA;
|
||||
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
ee=tmp;
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
n=tmp;
|
||||
|
||||
SignatureRSA sig=null;
|
||||
try{
|
||||
Class c=Class.forName(session.getConfig("signature.rsa"));
|
||||
sig=(SignatureRSA)(c.newInstance());
|
||||
sig.init();
|
||||
}
|
||||
catch(Exception e){
|
||||
System.err.println(e);
|
||||
}
|
||||
|
||||
sig.setPubKey(ee, n);
|
||||
sig.update(H);
|
||||
result=sig.verify(sig_of_H);
|
||||
|
||||
if(JSch.getLogger().isEnabled(Logger.INFO)){
|
||||
JSch.getLogger().log(Logger.INFO,
|
||||
"ssh_rsa_verify: signature "+result);
|
||||
}
|
||||
|
||||
}
|
||||
else if(alg.equals("ssh-dss")){
|
||||
byte[] q=null;
|
||||
byte[] tmp;
|
||||
byte[] p;
|
||||
byte[] g;
|
||||
|
||||
type=DSS;
|
||||
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
p=tmp;
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
q=tmp;
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
g=tmp;
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
f=tmp;
|
||||
|
||||
SignatureDSA sig=null;
|
||||
try{
|
||||
Class c=Class.forName(session.getConfig("signature.dss"));
|
||||
sig=(SignatureDSA)(c.newInstance());
|
||||
sig.init();
|
||||
}
|
||||
catch(Exception e){
|
||||
System.err.println(e);
|
||||
}
|
||||
sig.setPubKey(f, p, q, g);
|
||||
sig.update(H);
|
||||
result=sig.verify(sig_of_H);
|
||||
|
||||
if(JSch.getLogger().isEnabled(Logger.INFO)){
|
||||
JSch.getLogger().log(Logger.INFO,
|
||||
"ssh_dss_verify: signature "+result);
|
||||
}
|
||||
|
||||
}
|
||||
else{
|
||||
System.err.println("unknown alg");
|
||||
}
|
||||
state=STATE_END;
|
||||
return result;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getKeyType(){
|
||||
if(type==DSS) return "DSA";
|
||||
return "RSA";
|
||||
}
|
||||
|
||||
public int getState(){return state; }
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
@@ -37,21 +37,11 @@ public class DHGEX extends KeyExchange{
|
||||
private static final int SSH_MSG_KEX_DH_GEX_REQUEST= 34;
|
||||
|
||||
static int min=1024;
|
||||
|
||||
// static int min=512;
|
||||
static int preferred=1024;
|
||||
static int max=1024;
|
||||
|
||||
// static int preferred=1024;
|
||||
// static int max=2000;
|
||||
|
||||
static final int RSA=0;
|
||||
static final int DSS=1;
|
||||
private int type=0;
|
||||
int max=1024;
|
||||
|
||||
private int state;
|
||||
|
||||
// com.jcraft.jsch.DH dh;
|
||||
DH dh;
|
||||
|
||||
byte[] V_S;
|
||||
@@ -65,7 +55,8 @@ public class DHGEX extends KeyExchange{
|
||||
private byte[] p;
|
||||
private byte[] g;
|
||||
private byte[] e;
|
||||
//private byte[] f;
|
||||
|
||||
protected String hash="sha-1";
|
||||
|
||||
public void init(Session session,
|
||||
byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C) throws Exception{
|
||||
@@ -76,7 +67,7 @@ public class DHGEX extends KeyExchange{
|
||||
this.I_C=I_C;
|
||||
|
||||
try{
|
||||
Class c=Class.forName(session.getConfig("sha-1"));
|
||||
Class c=Class.forName(session.getConfig(hash));
|
||||
sha=(HASH)(c.newInstance());
|
||||
sha.init();
|
||||
}
|
||||
@@ -89,11 +80,13 @@ public class DHGEX extends KeyExchange{
|
||||
|
||||
try{
|
||||
Class c=Class.forName(session.getConfig("dh"));
|
||||
// Since JDK8, SunJCE has lifted the keysize restrictions
|
||||
// from 1024 to 2048 for DH.
|
||||
preferred = max = check2048(c, max);
|
||||
dh=(com.jcraft.jsch.DH)(c.newInstance());
|
||||
dh.init();
|
||||
}
|
||||
catch(Exception e){
|
||||
// System.err.println(e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
@@ -131,18 +124,9 @@ public class DHGEX extends KeyExchange{
|
||||
|
||||
p=_buf.getMPInt();
|
||||
g=_buf.getMPInt();
|
||||
/*
|
||||
for(int iii=0; iii<p.length; iii++){
|
||||
System.err.println("0x"+Integer.toHexString(p[iii]&0xff)+",");
|
||||
}
|
||||
System.err.println("");
|
||||
for(int iii=0; iii<g.length; iii++){
|
||||
System.err.println("0x"+Integer.toHexString(g[iii]&0xff)+",");
|
||||
}
|
||||
*/
|
||||
|
||||
dh.setP(p);
|
||||
dh.setG(g);
|
||||
|
||||
// The client responds with:
|
||||
// byte SSH_MSG_KEX_DH_GEX_INIT(32)
|
||||
// mpint e <- g^x mod p
|
||||
@@ -181,18 +165,14 @@ System.err.println("0x"+Integer.toHexString(g[iii]&0xff)+",");
|
||||
}
|
||||
|
||||
K_S=_buf.getString();
|
||||
// K_S is server_key_blob, which includes ....
|
||||
// string ssh-dss
|
||||
// impint p of dsa
|
||||
// impint q of dsa
|
||||
// impint g of dsa
|
||||
// impint pub_key of dsa
|
||||
//System.err.print("K_S: "); dump(K_S, 0, K_S.length);
|
||||
|
||||
byte[] f=_buf.getMPInt();
|
||||
byte[] sig_of_H=_buf.getString();
|
||||
|
||||
dh.setF(f);
|
||||
|
||||
dh.checkRange();
|
||||
|
||||
K=normalize(dh.getK());
|
||||
|
||||
//The hash H is computed as the HASH hash of the concatenation of the
|
||||
@@ -236,105 +216,30 @@ System.err.println("0x"+Integer.toHexString(g[iii]&0xff)+",");
|
||||
String alg=Util.byte2str(K_S, i, j);
|
||||
i+=j;
|
||||
|
||||
boolean result=false;
|
||||
if(alg.equals("ssh-rsa")){
|
||||
byte[] tmp;
|
||||
byte[] ee;
|
||||
byte[] n;
|
||||
|
||||
type=RSA;
|
||||
boolean result = verify(alg, K_S, i, sig_of_H);
|
||||
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
ee=tmp;
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
n=tmp;
|
||||
|
||||
// SignatureRSA sig=new SignatureRSA();
|
||||
// sig.init();
|
||||
|
||||
SignatureRSA sig=null;
|
||||
try{
|
||||
Class c=Class.forName(session.getConfig("signature.rsa"));
|
||||
sig=(SignatureRSA)(c.newInstance());
|
||||
sig.init();
|
||||
}
|
||||
catch(Exception e){
|
||||
System.err.println(e);
|
||||
}
|
||||
|
||||
sig.setPubKey(ee, n);
|
||||
sig.update(H);
|
||||
result=sig.verify(sig_of_H);
|
||||
|
||||
if(JSch.getLogger().isEnabled(Logger.INFO)){
|
||||
JSch.getLogger().log(Logger.INFO,
|
||||
"ssh_rsa_verify: signature "+result);
|
||||
}
|
||||
|
||||
}
|
||||
else if(alg.equals("ssh-dss")){
|
||||
byte[] q=null;
|
||||
byte[] tmp;
|
||||
|
||||
type=DSS;
|
||||
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
p=tmp;
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
q=tmp;
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
g=tmp;
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
f=tmp;
|
||||
|
||||
// SignatureDSA sig=new SignatureDSA();
|
||||
// sig.init();
|
||||
|
||||
SignatureDSA sig=null;
|
||||
try{
|
||||
Class c=Class.forName(session.getConfig("signature.dss"));
|
||||
sig=(SignatureDSA)(c.newInstance());
|
||||
sig.init();
|
||||
}
|
||||
catch(Exception e){
|
||||
System.err.println(e);
|
||||
}
|
||||
|
||||
sig.setPubKey(f, p, q, g);
|
||||
sig.update(H);
|
||||
result=sig.verify(sig_of_H);
|
||||
|
||||
if(JSch.getLogger().isEnabled(Logger.INFO)){
|
||||
JSch.getLogger().log(Logger.INFO,
|
||||
"ssh_dss_verify: signature "+result);
|
||||
}
|
||||
|
||||
}
|
||||
else{
|
||||
System.err.println("unknown alg");
|
||||
}
|
||||
state=STATE_END;
|
||||
return result;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getKeyType(){
|
||||
if(type==DSS) return "DSA";
|
||||
return "RSA";
|
||||
}
|
||||
|
||||
public int getState(){return state; }
|
||||
|
||||
protected int check2048(Class c, int _max) throws Exception {
|
||||
DH dh=(com.jcraft.jsch.DH)(c.newInstance());
|
||||
dh.init();
|
||||
byte[] foo = new byte[257];
|
||||
foo[1]=(byte)0xdd;
|
||||
foo[256]=0x73;
|
||||
dh.setP(foo);
|
||||
byte[] bar = {(byte)0x02};
|
||||
dh.setG(bar);
|
||||
try {
|
||||
dh.getE();
|
||||
_max=2048;
|
||||
}
|
||||
catch(Exception e){ }
|
||||
return _max;
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
@@ -29,312 +29,8 @@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.jcraft.jsch;
|
||||
|
||||
public class DHGEX256 extends KeyExchange{
|
||||
|
||||
private static final int SSH_MSG_KEX_DH_GEX_GROUP= 31;
|
||||
private static final int SSH_MSG_KEX_DH_GEX_INIT= 32;
|
||||
private static final int SSH_MSG_KEX_DH_GEX_REPLY= 33;
|
||||
private static final int SSH_MSG_KEX_DH_GEX_REQUEST= 34;
|
||||
|
||||
static int min=1024;
|
||||
|
||||
// static int min=512;
|
||||
static int preferred=1024;
|
||||
static int max=1024;
|
||||
|
||||
// static int preferred=1024;
|
||||
// static int max=2000;
|
||||
|
||||
static final int RSA=0;
|
||||
static final int DSS=1;
|
||||
private int type=0;
|
||||
|
||||
private int state;
|
||||
|
||||
// com.jcraft.jsch.DH dh;
|
||||
DH dh;
|
||||
|
||||
byte[] V_S;
|
||||
byte[] V_C;
|
||||
byte[] I_S;
|
||||
byte[] I_C;
|
||||
|
||||
private Buffer buf;
|
||||
private Packet packet;
|
||||
|
||||
private byte[] p;
|
||||
private byte[] g;
|
||||
private byte[] e;
|
||||
//private byte[] f;
|
||||
|
||||
public void init(Session session,
|
||||
byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C) throws Exception{
|
||||
this.session=session;
|
||||
this.V_S=V_S;
|
||||
this.V_C=V_C;
|
||||
this.I_S=I_S;
|
||||
this.I_C=I_C;
|
||||
|
||||
try{
|
||||
Class c=Class.forName(session.getConfig("sha-256"));
|
||||
sha=(HASH)(c.newInstance());
|
||||
sha.init();
|
||||
}
|
||||
catch(Exception e){
|
||||
System.err.println(e);
|
||||
}
|
||||
|
||||
buf=new Buffer();
|
||||
packet=new Packet(buf);
|
||||
|
||||
try{
|
||||
Class c=Class.forName(session.getConfig("dh"));
|
||||
dh=(com.jcraft.jsch.DH)(c.newInstance());
|
||||
dh.init();
|
||||
}
|
||||
catch(Exception e){
|
||||
// System.err.println(e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
packet.reset();
|
||||
buf.putByte((byte)SSH_MSG_KEX_DH_GEX_REQUEST);
|
||||
buf.putInt(min);
|
||||
buf.putInt(preferred);
|
||||
buf.putInt(max);
|
||||
session.write(packet);
|
||||
|
||||
if(JSch.getLogger().isEnabled(Logger.INFO)){
|
||||
JSch.getLogger().log(Logger.INFO,
|
||||
"SSH_MSG_KEX_DH_GEX_REQUEST("+min+"<"+preferred+"<"+max+") sent");
|
||||
JSch.getLogger().log(Logger.INFO,
|
||||
"expecting SSH_MSG_KEX_DH_GEX_GROUP");
|
||||
}
|
||||
|
||||
state=SSH_MSG_KEX_DH_GEX_GROUP;
|
||||
public class DHGEX256 extends DHGEX {
|
||||
DHGEX256(){
|
||||
hash="sha-256";
|
||||
}
|
||||
|
||||
public boolean next(Buffer _buf) throws Exception{
|
||||
int i,j;
|
||||
switch(state){
|
||||
case SSH_MSG_KEX_DH_GEX_GROUP:
|
||||
// byte SSH_MSG_KEX_DH_GEX_GROUP(31)
|
||||
// mpint p, safe prime
|
||||
// mpint g, generator for subgroup in GF (p)
|
||||
_buf.getInt();
|
||||
_buf.getByte();
|
||||
j=_buf.getByte();
|
||||
if(j!=SSH_MSG_KEX_DH_GEX_GROUP){
|
||||
System.err.println("type: must be SSH_MSG_KEX_DH_GEX_GROUP "+j);
|
||||
return false;
|
||||
}
|
||||
|
||||
p=_buf.getMPInt();
|
||||
g=_buf.getMPInt();
|
||||
/*
|
||||
for(int iii=0; iii<p.length; iii++){
|
||||
System.err.println("0x"+Integer.toHexString(p[iii]&0xff)+",");
|
||||
}
|
||||
System.err.println("");
|
||||
for(int iii=0; iii<g.length; iii++){
|
||||
System.err.println("0x"+Integer.toHexString(g[iii]&0xff)+",");
|
||||
}
|
||||
*/
|
||||
dh.setP(p);
|
||||
dh.setG(g);
|
||||
|
||||
// The client responds with:
|
||||
// byte SSH_MSG_KEX_DH_GEX_INIT(32)
|
||||
// mpint e <- g^x mod p
|
||||
// x is a random number (1 < x < (p-1)/2)
|
||||
|
||||
e=dh.getE();
|
||||
|
||||
packet.reset();
|
||||
buf.putByte((byte)SSH_MSG_KEX_DH_GEX_INIT);
|
||||
buf.putMPInt(e);
|
||||
session.write(packet);
|
||||
|
||||
if(JSch.getLogger().isEnabled(Logger.INFO)){
|
||||
JSch.getLogger().log(Logger.INFO,
|
||||
"SSH_MSG_KEX_DH_GEX_INIT sent");
|
||||
JSch.getLogger().log(Logger.INFO,
|
||||
"expecting SSH_MSG_KEX_DH_GEX_REPLY");
|
||||
}
|
||||
|
||||
state=SSH_MSG_KEX_DH_GEX_REPLY;
|
||||
return true;
|
||||
//break;
|
||||
|
||||
case SSH_MSG_KEX_DH_GEX_REPLY:
|
||||
// The server responds with:
|
||||
// byte SSH_MSG_KEX_DH_GEX_REPLY(33)
|
||||
// string server public host key and certificates (K_S)
|
||||
// mpint f
|
||||
// string signature of H
|
||||
j=_buf.getInt();
|
||||
j=_buf.getByte();
|
||||
j=_buf.getByte();
|
||||
if(j!=SSH_MSG_KEX_DH_GEX_REPLY){
|
||||
System.err.println("type: must be SSH_MSG_KEX_DH_GEX_REPLY "+j);
|
||||
return false;
|
||||
}
|
||||
|
||||
K_S=_buf.getString();
|
||||
// K_S is server_key_blob, which includes ....
|
||||
// string ssh-dss
|
||||
// impint p of dsa
|
||||
// impint q of dsa
|
||||
// impint g of dsa
|
||||
// impint pub_key of dsa
|
||||
//System.err.print("K_S: "); dump(K_S, 0, K_S.length);
|
||||
|
||||
byte[] f=_buf.getMPInt();
|
||||
byte[] sig_of_H=_buf.getString();
|
||||
|
||||
dh.setF(f);
|
||||
K=normalize(dh.getK());
|
||||
|
||||
//The hash H is computed as the HASH hash of the concatenation of the
|
||||
//following:
|
||||
// string V_C, the client's version string (CR and NL excluded)
|
||||
// string V_S, the server's version string (CR and NL excluded)
|
||||
// string I_C, the payload of the client's SSH_MSG_KEXINIT
|
||||
// string I_S, the payload of the server's SSH_MSG_KEXINIT
|
||||
// string K_S, the host key
|
||||
// uint32 min, minimal size in bits of an acceptable group
|
||||
// uint32 n, preferred size in bits of the group the server should send
|
||||
// uint32 max, maximal size in bits of an acceptable group
|
||||
// mpint p, safe prime
|
||||
// mpint g, generator for subgroup
|
||||
// mpint e, exchange value sent by the client
|
||||
// mpint f, exchange value sent by the server
|
||||
// mpint K, the shared secret
|
||||
// This value is called the exchange hash, and it is used to authenti-
|
||||
// cate the key exchange.
|
||||
|
||||
buf.reset();
|
||||
buf.putString(V_C); buf.putString(V_S);
|
||||
buf.putString(I_C); buf.putString(I_S);
|
||||
buf.putString(K_S);
|
||||
buf.putInt(min); buf.putInt(preferred); buf.putInt(max);
|
||||
buf.putMPInt(p); buf.putMPInt(g); buf.putMPInt(e); buf.putMPInt(f);
|
||||
buf.putMPInt(K);
|
||||
|
||||
byte[] foo=new byte[buf.getLength()];
|
||||
buf.getByte(foo);
|
||||
sha.update(foo, 0, foo.length);
|
||||
|
||||
H=sha.digest();
|
||||
|
||||
// System.err.print("H -> "); dump(H, 0, H.length);
|
||||
|
||||
i=0;
|
||||
j=0;
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
String alg=Util.byte2str(K_S, i, j);
|
||||
i+=j;
|
||||
|
||||
boolean result=false;
|
||||
if(alg.equals("ssh-rsa")){
|
||||
byte[] tmp;
|
||||
byte[] ee;
|
||||
byte[] n;
|
||||
|
||||
type=RSA;
|
||||
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
ee=tmp;
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
n=tmp;
|
||||
|
||||
// SignatureRSA sig=new SignatureRSA();
|
||||
// sig.init();
|
||||
|
||||
SignatureRSA sig=null;
|
||||
try{
|
||||
Class c=Class.forName(session.getConfig("signature.rsa"));
|
||||
sig=(SignatureRSA)(c.newInstance());
|
||||
sig.init();
|
||||
}
|
||||
catch(Exception e){
|
||||
System.err.println(e);
|
||||
}
|
||||
|
||||
sig.setPubKey(ee, n);
|
||||
sig.update(H);
|
||||
result=sig.verify(sig_of_H);
|
||||
|
||||
if(JSch.getLogger().isEnabled(Logger.INFO)){
|
||||
JSch.getLogger().log(Logger.INFO,
|
||||
"ssh_rsa_verify: signature "+result);
|
||||
}
|
||||
|
||||
}
|
||||
else if(alg.equals("ssh-dss")){
|
||||
byte[] q=null;
|
||||
byte[] tmp;
|
||||
|
||||
type=DSS;
|
||||
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
p=tmp;
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
q=tmp;
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
g=tmp;
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
f=tmp;
|
||||
|
||||
// SignatureDSA sig=new SignatureDSA();
|
||||
// sig.init();
|
||||
|
||||
SignatureDSA sig=null;
|
||||
try{
|
||||
Class c=Class.forName(session.getConfig("signature.dss"));
|
||||
sig=(SignatureDSA)(c.newInstance());
|
||||
sig.init();
|
||||
}
|
||||
catch(Exception e){
|
||||
System.err.println(e);
|
||||
}
|
||||
|
||||
sig.setPubKey(f, p, q, g);
|
||||
sig.update(H);
|
||||
result=sig.verify(sig_of_H);
|
||||
|
||||
if(JSch.getLogger().isEnabled(Logger.INFO)){
|
||||
JSch.getLogger().log(Logger.INFO,
|
||||
"ssh_dss_verify: signature "+result);
|
||||
}
|
||||
|
||||
}
|
||||
else{
|
||||
System.err.println("unknown alg");
|
||||
}
|
||||
state=STATE_END;
|
||||
return result;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getKeyType(){
|
||||
if(type==DSS) return "DSA";
|
||||
return "RSA";
|
||||
}
|
||||
|
||||
public int getState(){return state; }
|
||||
}
|
||||
|
@@ -0,0 +1,37 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2015-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.jcraft.jsch;
|
||||
|
||||
public interface ECDH {
|
||||
void init(int size) throws Exception;
|
||||
byte[] getSecret(byte[] r, byte[] s) throws Exception;
|
||||
byte[] getQ() throws Exception;
|
||||
boolean validate(byte[] r, byte[] s) throws Exception;
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2004-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2004-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
@@ -30,13 +30,22 @@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
package com.jcraft.jsch;
|
||||
|
||||
public class HostKey{
|
||||
private static final byte[] sshdss=Util.str2byte("ssh-dss");
|
||||
private static final byte[] sshrsa=Util.str2byte("ssh-rsa");
|
||||
|
||||
private static final byte[][] names = {
|
||||
Util.str2byte("ssh-dss"),
|
||||
Util.str2byte("ssh-rsa"),
|
||||
Util.str2byte("ecdsa-sha2-nistp256"),
|
||||
Util.str2byte("ecdsa-sha2-nistp384"),
|
||||
Util.str2byte("ecdsa-sha2-nistp521")
|
||||
};
|
||||
|
||||
protected static final int GUESS=0;
|
||||
public static final int SSHDSS=1;
|
||||
public static final int SSHRSA=2;
|
||||
static final int UNKNOWN=3;
|
||||
public static final int ECDSA256=3;
|
||||
public static final int ECDSA384=4;
|
||||
public static final int ECDSA521=5;
|
||||
static final int UNKNOWN=6;
|
||||
|
||||
protected String marker;
|
||||
protected String host;
|
||||
@@ -60,6 +69,9 @@ public class HostKey{
|
||||
if(type==GUESS){
|
||||
if(key[8]=='d'){ this.type=SSHDSS; }
|
||||
else if(key[8]=='r'){ this.type=SSHRSA; }
|
||||
else if(key[8]=='a' && key[20]=='2'){ this.type=ECDSA256; }
|
||||
else if(key[8]=='a' && key[20]=='3'){ this.type=ECDSA384; }
|
||||
else if(key[8]=='a' && key[20]=='5'){ this.type=ECDSA521; }
|
||||
else { throw new JSchException("invalid key type");}
|
||||
}
|
||||
else{
|
||||
@@ -71,10 +83,23 @@ public class HostKey{
|
||||
|
||||
public String getHost(){ return host; }
|
||||
public String getType(){
|
||||
if(type==SSHDSS){ return Util.byte2str(sshdss); }
|
||||
if(type==SSHRSA){ return Util.byte2str(sshrsa);}
|
||||
if(type==SSHDSS ||
|
||||
type==SSHRSA ||
|
||||
type==ECDSA256 ||
|
||||
type==ECDSA384 ||
|
||||
type==ECDSA521){
|
||||
return Util.byte2str(names[type-1]);
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
protected static int name2type(String name){
|
||||
for(int i = 0; i < names.length; i++){
|
||||
if(Util.byte2str(names[i]).equals(name)){
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
public String getKey(){
|
||||
return Util.byte2str(Util.toBase64(key, 0, key.length));
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2004-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2004-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
@@ -34,11 +34,61 @@ public interface HostKeyRepository{
|
||||
final int NOT_INCLUDED=1;
|
||||
final int CHANGED=2;
|
||||
|
||||
/**
|
||||
* Checks if <code>host</code> is included with the <code>key</code>.
|
||||
*
|
||||
* @return #NOT_INCLUDED, #OK or #CHANGED
|
||||
* @see #NOT_INCLUDED
|
||||
* @see #OK
|
||||
* @see #CHANGED
|
||||
*/
|
||||
int check(String host, byte[] key);
|
||||
|
||||
/**
|
||||
* Adds a host key <code>hostkey</code>
|
||||
*
|
||||
* @param hostkey a host key to be added
|
||||
* @param ui a user interface for showing messages or promping inputs.
|
||||
* @see UserInfo
|
||||
*/
|
||||
void add(HostKey hostkey, UserInfo ui);
|
||||
|
||||
/**
|
||||
* Removes a host key if there exists mached key with
|
||||
* <code>host</code>, <code>type</code>.
|
||||
*
|
||||
* @see #remove(String host, String type, byte[] key)
|
||||
*/
|
||||
void remove(String host, String type);
|
||||
|
||||
/**
|
||||
* Removes a host key if there exists a matched key with
|
||||
* <code>host</code>, <code>type</code> and <code>key</code>.
|
||||
*/
|
||||
void remove(String host, String type, byte[] key);
|
||||
|
||||
/**
|
||||
* Returns id of this repository.
|
||||
*
|
||||
* @return identity in String
|
||||
*/
|
||||
String getKnownHostsRepositoryID();
|
||||
|
||||
/**
|
||||
* Retuns a list for host keys managed in this repository.
|
||||
*
|
||||
* @see #getHostKey(String host, String type)
|
||||
*/
|
||||
HostKey[] getHostKey();
|
||||
|
||||
/**
|
||||
* Retuns a list for host keys managed in this repository.
|
||||
*
|
||||
* @param host a hostname used in searching host keys.
|
||||
* If <code>null</code> is given, every host key will be listed.
|
||||
* @param type a key type used in searching host keys,
|
||||
* and it should be "ssh-dss" or "ssh-rsa".
|
||||
* If <code>null</code> is given, a key type type will not be ignored.
|
||||
*/
|
||||
HostKey[] getHostKey(String host, String type);
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
@@ -42,6 +42,7 @@ class IdentityFile implements Identity{
|
||||
}
|
||||
|
||||
static IdentityFile newInstance(String name, byte[] prvkey, byte[] pubkey, JSch jsch) throws JSchException{
|
||||
|
||||
KeyPair kpair = KeyPair.load(jsch, prvkey, pubkey);
|
||||
return new IdentityFile(jsch, name, kpair);
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2012-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
@@ -36,17 +36,16 @@ public class JSch{
|
||||
/**
|
||||
* The version number.
|
||||
*/
|
||||
public static final String VERSION = "0.1.50";
|
||||
public static final String VERSION = "0.1.54";
|
||||
|
||||
static java.util.Hashtable config=new java.util.Hashtable();
|
||||
static{
|
||||
config.put("kex", "diffie-hellman-group1-sha1,diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha1");
|
||||
config.put("server_host_key", "ssh-rsa,ssh-dss");
|
||||
|
||||
config.put("kex", "ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1,diffie-hellman-group1-sha1");
|
||||
config.put("server_host_key", "ssh-rsa,ssh-dss,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521");
|
||||
config.put("cipher.s2c",
|
||||
"aes128-ctr,aes128-cbc,3des-ctr,3des-cbc,blowfish-cbc,aes192-cbc,aes256-cbc");
|
||||
"aes128-ctr,aes128-cbc,3des-ctr,3des-cbc,blowfish-cbc,aes192-ctr,aes192-cbc,aes256-ctr,aes256-cbc");
|
||||
config.put("cipher.c2s",
|
||||
"aes128-ctr,aes128-cbc,3des-ctr,3des-cbc,blowfish-cbc,aes192-cbc,aes256-cbc");
|
||||
"aes128-ctr,aes128-cbc,3des-ctr,3des-cbc,blowfish-cbc,aes192-ctr,aes192-cbc,aes256-ctr,aes256-cbc");
|
||||
|
||||
config.put("mac.s2c", "hmac-md5,hmac-sha1,hmac-sha2-256,hmac-sha1-96,hmac-md5-96");
|
||||
config.put("mac.c2s", "hmac-md5,hmac-sha1,hmac-sha2-256,hmac-sha1-96,hmac-md5-96");
|
||||
@@ -63,9 +62,19 @@ public class JSch{
|
||||
config.put("diffie-hellman-group1-sha1",
|
||||
"com.jcraft.jsch.DHG1");
|
||||
config.put("diffie-hellman-group14-sha1",
|
||||
"com.jcraft.jsch.DHG14");
|
||||
"com.jcraft.jsch.DHG14"); // available since JDK8.
|
||||
config.put("diffie-hellman-group-exchange-sha256",
|
||||
"com.jcraft.jsch.DHGEX256"); // avaibale since JDK1.4.2.
|
||||
"com.jcraft.jsch.DHGEX256"); // available since JDK1.4.2.
|
||||
// On JDK8, 2048bits will be used.
|
||||
config.put("ecdsa-sha2-nistp256", "com.jcraft.jsch.jce.SignatureECDSA");
|
||||
config.put("ecdsa-sha2-nistp384", "com.jcraft.jsch.jce.SignatureECDSA");
|
||||
config.put("ecdsa-sha2-nistp521", "com.jcraft.jsch.jce.SignatureECDSA");
|
||||
|
||||
config.put("ecdh-sha2-nistp256", "com.jcraft.jsch.DHEC256");
|
||||
config.put("ecdh-sha2-nistp384", "com.jcraft.jsch.DHEC384");
|
||||
config.put("ecdh-sha2-nistp521", "com.jcraft.jsch.DHEC521");
|
||||
|
||||
config.put("ecdh-sha2-nistp", "com.jcraft.jsch.jce.ECDHN");
|
||||
|
||||
config.put("dh", "com.jcraft.jsch.jce.DH");
|
||||
config.put("3des-cbc", "com.jcraft.jsch.jce.TripleDESCBC");
|
||||
@@ -80,11 +89,15 @@ public class JSch{
|
||||
config.put("hmac-md5-96", "com.jcraft.jsch.jce.HMACMD596");
|
||||
config.put("sha-1", "com.jcraft.jsch.jce.SHA1");
|
||||
config.put("sha-256", "com.jcraft.jsch.jce.SHA256");
|
||||
config.put("sha-384", "com.jcraft.jsch.jce.SHA384");
|
||||
config.put("sha-512", "com.jcraft.jsch.jce.SHA512");
|
||||
config.put("md5", "com.jcraft.jsch.jce.MD5");
|
||||
config.put("signature.dss", "com.jcraft.jsch.jce.SignatureDSA");
|
||||
config.put("signature.rsa", "com.jcraft.jsch.jce.SignatureRSA");
|
||||
config.put("signature.ecdsa", "com.jcraft.jsch.jce.SignatureECDSA");
|
||||
config.put("keypairgen.dsa", "com.jcraft.jsch.jce.KeyPairGenDSA");
|
||||
config.put("keypairgen.rsa", "com.jcraft.jsch.jce.KeyPairGenRSA");
|
||||
config.put("keypairgen.ecdsa", "com.jcraft.jsch.jce.KeyPairGenECDSA");
|
||||
config.put("random", "com.jcraft.jsch.jce.Random");
|
||||
|
||||
config.put("none", "com.jcraft.jsch.CipherNone");
|
||||
@@ -111,13 +124,16 @@ public class JSch{
|
||||
config.put("zlib", "com.jcraft.jsch.jcraft.Compression");
|
||||
config.put("zlib@openssh.com", "com.jcraft.jsch.jcraft.Compression");
|
||||
|
||||
config.put("pbkdf", "com.jcraft.jsch.jce.PBKDF");
|
||||
|
||||
config.put("StrictHostKeyChecking", "ask");
|
||||
config.put("HashKnownHosts", "no");
|
||||
|
||||
config.put("PreferredAuthentications", "gssapi-with-mic,publickey,keyboard-interactive,password");
|
||||
|
||||
config.put("CheckCiphers", "aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc,3des-ctr,arcfour,arcfour128,arcfour256");
|
||||
config.put("CheckKexes", "diffie-hellman-group14-sha1");
|
||||
config.put("CheckKexes", "diffie-hellman-group14-sha1,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521");
|
||||
config.put("CheckSignatures", "ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521");
|
||||
|
||||
config.put("MaxAuthTries", "6");
|
||||
config.put("ClearAllForwardings", "no");
|
||||
@@ -171,7 +187,9 @@ public class JSch{
|
||||
static Logger logger=DEVNULL;
|
||||
|
||||
public JSch(){
|
||||
|
||||
/*
|
||||
// The JCE of Sun's Java5 on Mac OS X has the resource leak bug
|
||||
// in calculating HMAC, so we need to use our own implementations.
|
||||
try{
|
||||
String osname=(String)(System.getProperties().get("os.name"));
|
||||
if(osname!=null && osname.equals("Mac OS X")){
|
||||
@@ -183,7 +201,7 @@ public class JSch{
|
||||
}
|
||||
catch(Exception e){
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
@@ -70,19 +70,24 @@ public abstract class KeyExchange{
|
||||
public abstract void init(Session session,
|
||||
byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C) throws Exception;
|
||||
public abstract boolean next(Buffer buf) throws Exception;
|
||||
public abstract String getKeyType();
|
||||
|
||||
public abstract int getState();
|
||||
|
||||
/*
|
||||
void dump(byte[] foo){
|
||||
for(int i=0; i<foo.length; i++){
|
||||
if((foo[i]&0xf0)==0)System.err.print("0");
|
||||
System.err.print(Integer.toHexString(foo[i]&0xff));
|
||||
if(i%16==15){System.err.println(""); continue;}
|
||||
if(i%2==1)System.err.print(" ");
|
||||
}
|
||||
}
|
||||
*/
|
||||
protected final int RSA=0;
|
||||
protected final int DSS=1;
|
||||
protected final int ECDSA=2;
|
||||
private int type=0;
|
||||
private String key_alg_name = "";
|
||||
|
||||
public String getKeyType() {
|
||||
if(type==DSS) return "DSA";
|
||||
if(type==RSA) return "RSA";
|
||||
return "ECDSA";
|
||||
}
|
||||
|
||||
public String getKeyAlgorithName() {
|
||||
return key_alg_name;
|
||||
}
|
||||
|
||||
protected static String[] guess(byte[]I_S, byte[]I_C){
|
||||
String[] guess=new String[PROPOSAL_MAX];
|
||||
@@ -176,8 +181,145 @@ public abstract class KeyExchange{
|
||||
secret[0] == 0 && (secret[1]&0x80) == 0) {
|
||||
byte[] tmp=new byte[secret.length-1];
|
||||
System.arraycopy(secret, 1, tmp, 0, tmp.length);
|
||||
secret=tmp;
|
||||
return normalize(tmp);
|
||||
}
|
||||
else {
|
||||
return secret;
|
||||
}
|
||||
return secret;
|
||||
}
|
||||
|
||||
protected boolean verify(String alg, byte[] K_S, int index,
|
||||
byte[] sig_of_H) throws Exception {
|
||||
int i,j;
|
||||
|
||||
i=index;
|
||||
boolean result=false;
|
||||
|
||||
if(alg.equals("ssh-rsa")){
|
||||
byte[] tmp;
|
||||
byte[] ee;
|
||||
byte[] n;
|
||||
|
||||
type=RSA;
|
||||
key_alg_name=alg;
|
||||
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
ee=tmp;
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
n=tmp;
|
||||
|
||||
SignatureRSA sig=null;
|
||||
try{
|
||||
Class c=Class.forName(session.getConfig("signature.rsa"));
|
||||
sig=(SignatureRSA)(c.newInstance());
|
||||
sig.init();
|
||||
}
|
||||
catch(Exception e){
|
||||
System.err.println(e);
|
||||
}
|
||||
sig.setPubKey(ee, n);
|
||||
sig.update(H);
|
||||
result=sig.verify(sig_of_H);
|
||||
|
||||
if(JSch.getLogger().isEnabled(Logger.INFO)){
|
||||
JSch.getLogger().log(Logger.INFO,
|
||||
"ssh_rsa_verify: signature "+result);
|
||||
}
|
||||
}
|
||||
else if(alg.equals("ssh-dss")){
|
||||
byte[] q=null;
|
||||
byte[] tmp;
|
||||
byte[] p;
|
||||
byte[] g;
|
||||
byte[] f;
|
||||
|
||||
type=DSS;
|
||||
key_alg_name=alg;
|
||||
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
p=tmp;
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
q=tmp;
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
g=tmp;
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
f=tmp;
|
||||
|
||||
SignatureDSA sig=null;
|
||||
try{
|
||||
Class c=Class.forName(session.getConfig("signature.dss"));
|
||||
sig=(SignatureDSA)(c.newInstance());
|
||||
sig.init();
|
||||
}
|
||||
catch(Exception e){
|
||||
System.err.println(e);
|
||||
}
|
||||
sig.setPubKey(f, p, q, g);
|
||||
sig.update(H);
|
||||
result=sig.verify(sig_of_H);
|
||||
|
||||
if(JSch.getLogger().isEnabled(Logger.INFO)){
|
||||
JSch.getLogger().log(Logger.INFO,
|
||||
"ssh_dss_verify: signature "+result);
|
||||
}
|
||||
}
|
||||
else if(alg.equals("ecdsa-sha2-nistp256") ||
|
||||
alg.equals("ecdsa-sha2-nistp384") ||
|
||||
alg.equals("ecdsa-sha2-nistp521")) {
|
||||
byte[] tmp;
|
||||
byte[] r;
|
||||
byte[] s;
|
||||
|
||||
// RFC 5656,
|
||||
type=ECDSA;
|
||||
key_alg_name=alg;
|
||||
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
tmp=new byte[j]; System.arraycopy(K_S, i, tmp, 0, j); i+=j;
|
||||
j=((K_S[i++]<<24)&0xff000000)|((K_S[i++]<<16)&0x00ff0000)|
|
||||
((K_S[i++]<<8)&0x0000ff00)|((K_S[i++])&0x000000ff);
|
||||
i++;
|
||||
tmp=new byte[(j-1)/2];
|
||||
System.arraycopy(K_S, i, tmp, 0, tmp.length); i+=(j-1)/2;
|
||||
r=tmp;
|
||||
tmp=new byte[(j-1)/2];
|
||||
System.arraycopy(K_S, i, tmp, 0, tmp.length); i+=(j-1)/2;
|
||||
s=tmp;
|
||||
|
||||
SignatureECDSA sig=null;
|
||||
try{
|
||||
Class c=Class.forName(session.getConfig("signature.ecdsa"));
|
||||
sig=(SignatureECDSA)(c.newInstance());
|
||||
sig.init();
|
||||
}
|
||||
catch(Exception e){
|
||||
System.err.println(e);
|
||||
}
|
||||
|
||||
sig.setPubKey(r, s);
|
||||
|
||||
sig.update(H);
|
||||
|
||||
result=sig.verify(sig_of_H);
|
||||
}
|
||||
else{
|
||||
System.err.println("unknown alg");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
@@ -38,11 +38,13 @@ public abstract class KeyPair{
|
||||
public static final int ERROR=0;
|
||||
public static final int DSA=1;
|
||||
public static final int RSA=2;
|
||||
public static final int UNKNOWN=3;
|
||||
public static final int ECDSA=3;
|
||||
public static final int UNKNOWN=4;
|
||||
|
||||
static final int VENDOR_OPENSSH=0;
|
||||
static final int VENDOR_FSECURE=1;
|
||||
static final int VENDOR_PUTTY=2;
|
||||
static final int VENDOR_PKCS8=3;
|
||||
|
||||
int vendor=VENDOR_OPENSSH;
|
||||
|
||||
@@ -55,6 +57,7 @@ public abstract class KeyPair{
|
||||
KeyPair kpair=null;
|
||||
if(type==DSA){ kpair=new KeyPairDSA(jsch); }
|
||||
else if(type==RSA){ kpair=new KeyPairRSA(jsch); }
|
||||
else if(type==ECDSA){ kpair=new KeyPairECDSA(jsch); }
|
||||
if(kpair!=null){
|
||||
kpair.generate(key_size);
|
||||
}
|
||||
@@ -338,6 +341,22 @@ public abstract class KeyPair{
|
||||
return index;
|
||||
}
|
||||
|
||||
int writeOCTETSTRING(byte[] buf, int index, byte[] data){
|
||||
buf[index++]=0x04;
|
||||
index=writeLength(buf, index, data.length);
|
||||
System.arraycopy(data, 0, buf, index, data.length);
|
||||
index+=data.length;
|
||||
return index;
|
||||
}
|
||||
|
||||
int writeDATA(byte[] buf, byte n, int index, byte[] data){
|
||||
buf[index++]=n;
|
||||
index=writeLength(buf, index, data.length);
|
||||
System.arraycopy(data, 0, buf, index, data.length);
|
||||
index+=data.length;
|
||||
return index;
|
||||
}
|
||||
|
||||
int countLength(int len){
|
||||
int i=1;
|
||||
if(len<=0x7f) return i;
|
||||
@@ -474,8 +493,8 @@ public abstract class KeyPair{
|
||||
this.passphrase=passphrase;
|
||||
}
|
||||
|
||||
private boolean encrypted=false;
|
||||
private byte[] data=null;
|
||||
protected boolean encrypted=false;
|
||||
protected byte[] data=null;
|
||||
private byte[] iv=null;
|
||||
private byte[] publickeyblob=null;
|
||||
|
||||
@@ -563,7 +582,8 @@ public abstract class KeyPair{
|
||||
if(pubkey==null &&
|
||||
prvkey!=null &&
|
||||
(prvkey.length>11 &&
|
||||
prvkey[0]==0 && prvkey[1]==0 && prvkey[2]==0 && prvkey[3]==7)){
|
||||
prvkey[0]==0 && prvkey[1]==0 && prvkey[2]==0 &&
|
||||
(prvkey[3]==7 || prvkey[3]==19))){
|
||||
|
||||
Buffer buf=new Buffer(prvkey);
|
||||
buf.skip(prvkey.length); // for using Buffer#available()
|
||||
@@ -577,6 +597,11 @@ public abstract class KeyPair{
|
||||
else if(_type.equals("ssh-dss")){
|
||||
kpair=KeyPairDSA.fromSSHAgent(jsch, buf);
|
||||
}
|
||||
else if(_type.equals("ecdsa-sha2-nistp256") ||
|
||||
_type.equals("ecdsa-sha2-nistp384") ||
|
||||
_type.equals("ecdsa-sha2-nistp512")){
|
||||
kpair=KeyPairECDSA.fromSSHAgent(jsch, buf);
|
||||
}
|
||||
else{
|
||||
throw new JSchException("privatekey: invalid key "+new String(prvkey, 4, 7));
|
||||
}
|
||||
@@ -612,10 +637,29 @@ public abstract class KeyPair{
|
||||
throw new JSchException("invalid privatekey: "+prvkey);
|
||||
if(buf[i]=='D'&& buf[i+1]=='S'&& buf[i+2]=='A'){ type=DSA; }
|
||||
else if(buf[i]=='R'&& buf[i+1]=='S'&& buf[i+2]=='A'){ type=RSA; }
|
||||
else if(buf[i]=='E'&& buf[i+1]=='C'){ type=ECDSA; }
|
||||
else if(buf[i]=='S'&& buf[i+1]=='S'&& buf[i+2]=='H'){ // FSecure
|
||||
type=UNKNOWN;
|
||||
vendor=VENDOR_FSECURE;
|
||||
}
|
||||
else if(i+6 < len &&
|
||||
buf[i]=='P' && buf[i+1]=='R' &&
|
||||
buf[i+2]=='I' && buf[i+3]=='V' &&
|
||||
buf[i+4]=='A' && buf[i+5]=='T' && buf[i+6]=='E'){
|
||||
type=UNKNOWN;
|
||||
vendor=VENDOR_PKCS8;
|
||||
encrypted=false;
|
||||
i+=3;
|
||||
}
|
||||
else if(i+8 < len &&
|
||||
buf[i]=='E' && buf[i+1]=='N' &&
|
||||
buf[i+2]=='C' && buf[i+3]=='R' &&
|
||||
buf[i+4]=='Y' && buf[i+5]=='P' && buf[i+6]=='T' &&
|
||||
buf[i+7]=='E' && buf[i+8]=='D'){
|
||||
type=UNKNOWN;
|
||||
vendor=VENDOR_PKCS8;
|
||||
i+=5;
|
||||
}
|
||||
else{
|
||||
throw new JSchException("invalid privatekey: "+prvkey);
|
||||
}
|
||||
@@ -689,7 +733,8 @@ public abstract class KeyPair{
|
||||
}
|
||||
if(!inheader){
|
||||
i++;
|
||||
encrypted=false; // no passphrase
|
||||
if(vendor!=VENDOR_PKCS8)
|
||||
encrypted=false; // no passphrase
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -724,12 +769,8 @@ public abstract class KeyPair{
|
||||
while(i<_len){
|
||||
if(_buf[i]==0x0a){
|
||||
boolean xd=(_buf[i-1]==0x0d);
|
||||
// move 0x0a (or 0x0d0x0a) to the end of '_buf'.
|
||||
System.arraycopy(_buf, i+1,
|
||||
_buf,
|
||||
i-(xd ? 1 : 0),
|
||||
_len-i-1-(xd ? 1 : 0)
|
||||
);
|
||||
// ignore 0x0a (or 0x0d0x0a)
|
||||
System.arraycopy(_buf, i+1, _buf, i-(xd ? 1 : 0), _len-(i+1));
|
||||
if(xd)_len--;
|
||||
_len--;
|
||||
continue;
|
||||
@@ -841,11 +882,32 @@ public abstract class KeyPair{
|
||||
if(i++<len){
|
||||
int start=i;
|
||||
while(i<len){ if(buf[i]=='\n')break; i++;}
|
||||
if(i<len){
|
||||
if(i>0 && buf[i-1]==0x0d) i--;
|
||||
if(start<i){
|
||||
publicKeyComment = new String(buf, start, i-start);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(buf[0]=='e'&& buf[1]=='c'&& buf[2]=='d' && buf[3]=='s'){
|
||||
if(prvkey==null && buf.length>7){
|
||||
type=ECDSA;
|
||||
}
|
||||
i=0;
|
||||
while(i<len){ if(buf[i]==' ')break; i++;} i++;
|
||||
if(i<len){
|
||||
int start=i;
|
||||
while(i<len){ if(buf[i]==' ')break; i++;}
|
||||
publickeyblob=Util.fromBase64(buf, start, i-start);
|
||||
}
|
||||
if(i++<len){
|
||||
int start=i;
|
||||
while(i<len){ if(buf[i]=='\n')break; i++;}
|
||||
if(i>0 && buf[i-1]==0x0d) i--;
|
||||
if(start<i){
|
||||
publicKeyComment = new String(buf, start, i-start);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception ee){
|
||||
@@ -862,6 +924,8 @@ public abstract class KeyPair{
|
||||
KeyPair kpair=null;
|
||||
if(type==DSA){ kpair=new KeyPairDSA(jsch); }
|
||||
else if(type==RSA){ kpair=new KeyPairRSA(jsch); }
|
||||
else if(type==ECDSA){ kpair=new KeyPairECDSA(jsch); }
|
||||
else if(vendor==VENDOR_PKCS8){ kpair = new KeyPairPKCS8(jsch); }
|
||||
|
||||
if(kpair!=null){
|
||||
kpair.encrypted=encrypted;
|
||||
@@ -1101,4 +1165,91 @@ public abstract class KeyPair{
|
||||
|
||||
return (key != null && value != null);
|
||||
}
|
||||
|
||||
void copy(KeyPair kpair){
|
||||
this.publickeyblob=kpair.publickeyblob;
|
||||
this.vendor=kpair.vendor;
|
||||
this.publicKeyComment=kpair.publicKeyComment;
|
||||
this.cipher=kpair.cipher;
|
||||
}
|
||||
|
||||
class ASN1Exception extends Exception {
|
||||
}
|
||||
|
||||
class ASN1 {
|
||||
byte[] buf;
|
||||
int start;
|
||||
int length;
|
||||
ASN1(byte[] buf) throws ASN1Exception {
|
||||
this(buf, 0, buf.length);
|
||||
}
|
||||
ASN1(byte[] buf, int start, int length) throws ASN1Exception {
|
||||
this.buf = buf;
|
||||
this.start = start;
|
||||
this.length = length;
|
||||
if(start+length>buf.length)
|
||||
throw new ASN1Exception();
|
||||
}
|
||||
int getType() {
|
||||
return buf[start]&0xff;
|
||||
}
|
||||
boolean isSEQUENCE() {
|
||||
return getType()==(0x30&0xff);
|
||||
}
|
||||
boolean isINTEGER() {
|
||||
return getType()==(0x02&0xff);
|
||||
}
|
||||
boolean isOBJECT() {
|
||||
return getType()==(0x06&0xff);
|
||||
}
|
||||
boolean isOCTETSTRING() {
|
||||
return getType()==(0x04&0xff);
|
||||
}
|
||||
private int getLength(int[] indexp) {
|
||||
int index=indexp[0];
|
||||
int length=buf[index++]&0xff;
|
||||
if((length&0x80)!=0) {
|
||||
int foo=length&0x7f; length=0;
|
||||
while(foo-->0){ length=(length<<8)+(buf[index++]&0xff); }
|
||||
}
|
||||
indexp[0]=index;
|
||||
return length;
|
||||
}
|
||||
byte[] getContent() {
|
||||
int[] indexp=new int[1];
|
||||
indexp[0]=start+1;
|
||||
int length = getLength(indexp);
|
||||
int index=indexp[0];
|
||||
byte[] tmp = new byte[length];
|
||||
System.arraycopy(buf, index, tmp, 0, tmp.length);
|
||||
return tmp;
|
||||
}
|
||||
ASN1[] getContents() throws ASN1Exception {
|
||||
int typ = buf[start];
|
||||
int[] indexp=new int[1];
|
||||
indexp[0]=start+1;
|
||||
int length = getLength(indexp);
|
||||
if(typ == 0x05){
|
||||
return new ASN1[0];
|
||||
}
|
||||
int index=indexp[0];
|
||||
java.util.Vector values = new java.util.Vector();
|
||||
while(length>0) {
|
||||
index++; length--;
|
||||
int tmp=index;
|
||||
indexp[0]=index;
|
||||
int l=getLength(indexp);
|
||||
index=indexp[0];
|
||||
length-=(index-tmp);
|
||||
values.addElement(new ASN1(buf, tmp-1, 1+(index-tmp)+l));
|
||||
index+=l;
|
||||
length-=l;
|
||||
}
|
||||
ASN1[] result = new ASN1[values.size()];
|
||||
for(int i = 0; i <values.size(); i++) {
|
||||
result[i]=(ASN1)values.elementAt(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -0,0 +1,391 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2015-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.jcraft.jsch;
|
||||
|
||||
public class KeyPairECDSA extends KeyPair{
|
||||
|
||||
private static byte[][] oids = {
|
||||
{(byte)0x06, (byte)0x08, (byte)0x2a, (byte)0x86, (byte)0x48, // 256
|
||||
(byte)0xce, (byte)0x3d, (byte)0x03, (byte)0x01, (byte)0x07},
|
||||
{(byte)0x06, (byte)0x05, (byte)0x2b, (byte)0x81, (byte)0x04, // 384
|
||||
(byte)0x00, (byte)0x22},
|
||||
{(byte)0x06, (byte)0x05, (byte)0x2b, (byte)0x81, (byte)0x04, //521
|
||||
(byte)0x00, (byte)0x23},
|
||||
};
|
||||
|
||||
private static String[] names = {
|
||||
"nistp256", "nistp384", "nistp521"
|
||||
};
|
||||
|
||||
private byte[] name=Util.str2byte(names[0]);
|
||||
private byte[] r_array;
|
||||
private byte[] s_array;
|
||||
private byte[] prv_array;
|
||||
|
||||
private int key_size=256;
|
||||
|
||||
public KeyPairECDSA(JSch jsch){
|
||||
this(jsch, null, null, null, null);
|
||||
}
|
||||
|
||||
public KeyPairECDSA(JSch jsch,
|
||||
byte[] name,
|
||||
byte[] r_array,
|
||||
byte[] s_array,
|
||||
byte[] prv_array){
|
||||
super(jsch);
|
||||
if(name!=null)
|
||||
this.name = name;
|
||||
this.r_array = r_array;
|
||||
this.s_array = s_array;
|
||||
this.prv_array = prv_array;
|
||||
if(prv_array!=null)
|
||||
key_size = prv_array.length>=64 ? 521 :
|
||||
(prv_array.length>=48 ? 384 : 256);
|
||||
}
|
||||
|
||||
void generate(int key_size) throws JSchException{
|
||||
this.key_size=key_size;
|
||||
try{
|
||||
Class c=Class.forName(jsch.getConfig("keypairgen.ecdsa"));
|
||||
KeyPairGenECDSA keypairgen=(KeyPairGenECDSA)(c.newInstance());
|
||||
keypairgen.init(key_size);
|
||||
prv_array=keypairgen.getD();
|
||||
r_array=keypairgen.getR();
|
||||
s_array=keypairgen.getS();
|
||||
name=Util.str2byte(names[prv_array.length>=64 ? 2 :
|
||||
(prv_array.length>=48 ? 1 : 0)]);
|
||||
keypairgen=null;
|
||||
}
|
||||
catch(Exception e){
|
||||
if(e instanceof Throwable)
|
||||
throw new JSchException(e.toString(), (Throwable)e);
|
||||
throw new JSchException(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static final byte[] begin =
|
||||
Util.str2byte("-----BEGIN EC PRIVATE KEY-----");
|
||||
private static final byte[] end =
|
||||
Util.str2byte("-----END EC PRIVATE KEY-----");
|
||||
|
||||
byte[] getBegin(){ return begin; }
|
||||
byte[] getEnd(){ return end; }
|
||||
|
||||
byte[] getPrivateKey(){
|
||||
|
||||
byte[] tmp = new byte[1]; tmp[0]=1;
|
||||
|
||||
byte[] oid = oids[
|
||||
(r_array.length>=64) ? 2 :
|
||||
((r_array.length>=48) ? 1 : 0)
|
||||
];
|
||||
|
||||
byte[] point = toPoint(r_array, s_array);
|
||||
|
||||
int bar = ((point.length+1)&0x80)==0 ? 3 : 4;
|
||||
byte[] foo = new byte[point.length+bar];
|
||||
System.arraycopy(point, 0, foo, bar, point.length);
|
||||
foo[0]=0x03; // BITSTRING
|
||||
if(bar==3){
|
||||
foo[1]=(byte)(point.length+1);
|
||||
}
|
||||
else {
|
||||
foo[1]=(byte)0x81;
|
||||
foo[2]=(byte)(point.length+1);
|
||||
}
|
||||
point = foo;
|
||||
|
||||
int content=
|
||||
1+countLength(tmp.length) + tmp.length +
|
||||
1+countLength(prv_array.length) + prv_array.length +
|
||||
1+countLength(oid.length) + oid.length +
|
||||
1+countLength(point.length) + point.length;
|
||||
|
||||
int total=
|
||||
1+countLength(content)+content; // SEQUENCE
|
||||
|
||||
byte[] plain=new byte[total];
|
||||
int index=0;
|
||||
index=writeSEQUENCE(plain, index, content);
|
||||
index=writeINTEGER(plain, index, tmp);
|
||||
index=writeOCTETSTRING(plain, index, prv_array);
|
||||
index=writeDATA(plain, (byte)0xa0, index, oid);
|
||||
index=writeDATA(plain, (byte)0xa1, index, point);
|
||||
|
||||
return plain;
|
||||
}
|
||||
|
||||
boolean parse(byte[] plain){
|
||||
try{
|
||||
|
||||
if(vendor==VENDOR_FSECURE){
|
||||
/*
|
||||
if(plain[0]!=0x30){ // FSecure
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
else if(vendor==VENDOR_PUTTY){
|
||||
/*
|
||||
Buffer buf=new Buffer(plain);
|
||||
buf.skip(plain.length);
|
||||
|
||||
try {
|
||||
byte[][] tmp = buf.getBytes(1, "");
|
||||
prv_array = tmp[0];
|
||||
}
|
||||
catch(JSchException e){
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
int index=0;
|
||||
int length=0;
|
||||
|
||||
if(plain[index]!=0x30)return false;
|
||||
index++; // SEQUENCE
|
||||
length=plain[index++]&0xff;
|
||||
if((length&0x80)!=0){
|
||||
int foo=length&0x7f; length=0;
|
||||
while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
|
||||
}
|
||||
|
||||
if(plain[index]!=0x02)return false;
|
||||
index++; // INTEGER
|
||||
|
||||
length=plain[index++]&0xff;
|
||||
if((length&0x80)!=0){
|
||||
int foo=length&0x7f; length=0;
|
||||
while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
|
||||
}
|
||||
|
||||
index+=length;
|
||||
index++; // 0x04
|
||||
|
||||
length=plain[index++]&0xff;
|
||||
if((length&0x80)!=0){
|
||||
int foo=length&0x7f; length=0;
|
||||
while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
|
||||
}
|
||||
|
||||
prv_array=new byte[length];
|
||||
System.arraycopy(plain, index, prv_array, 0, length);
|
||||
|
||||
index+=length;
|
||||
|
||||
index++; // 0xa0
|
||||
|
||||
length=plain[index++]&0xff;
|
||||
if((length&0x80)!=0){
|
||||
int foo=length&0x7f; length=0;
|
||||
while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
|
||||
}
|
||||
|
||||
byte[] oid_array=new byte[length];
|
||||
System.arraycopy(plain, index, oid_array, 0, length);
|
||||
index+=length;
|
||||
|
||||
for(int i = 0; i<oids.length; i++){
|
||||
if(Util.array_equals(oids[i], oid_array)){
|
||||
name = Util.str2byte(names[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
index++; // 0xa1
|
||||
|
||||
length=plain[index++]&0xff;
|
||||
if((length&0x80)!=0){
|
||||
int foo=length&0x7f; length=0;
|
||||
while(foo-->0){ length=(length<<8)+(plain[index++]&0xff); }
|
||||
}
|
||||
|
||||
byte[] Q_array=new byte[length];
|
||||
System.arraycopy(plain, index, Q_array, 0, length);
|
||||
index+=length;
|
||||
|
||||
byte[][] tmp = fromPoint(Q_array);
|
||||
r_array = tmp[0];
|
||||
s_array = tmp[1];
|
||||
|
||||
if(prv_array!=null)
|
||||
key_size = prv_array.length>=64 ? 521 :
|
||||
(prv_array.length>=48 ? 384 : 256);
|
||||
}
|
||||
catch(Exception e){
|
||||
//System.err.println(e);
|
||||
//e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public byte[] getPublicKeyBlob(){
|
||||
byte[] foo = super.getPublicKeyBlob();
|
||||
|
||||
if(foo!=null) return foo;
|
||||
|
||||
if(r_array==null) return null;
|
||||
|
||||
byte[][] tmp = new byte[3][];
|
||||
tmp[0] = Util.str2byte("ecdsa-sha2-"+new String(name));
|
||||
tmp[1] = name;
|
||||
tmp[2] = new byte[1+r_array.length+s_array.length];
|
||||
tmp[2][0] = 4; // POINT_CONVERSION_UNCOMPRESSED
|
||||
System.arraycopy(r_array, 0, tmp[2], 1, r_array.length);
|
||||
System.arraycopy(s_array, 0, tmp[2], 1+r_array.length, s_array.length);
|
||||
|
||||
return Buffer.fromBytes(tmp).buffer;
|
||||
}
|
||||
|
||||
byte[] getKeyTypeName(){
|
||||
return Util.str2byte("ecdsa-sha2-"+new String(name));
|
||||
}
|
||||
public int getKeyType(){
|
||||
return ECDSA;
|
||||
}
|
||||
public int getKeySize(){
|
||||
return key_size;
|
||||
}
|
||||
|
||||
public byte[] getSignature(byte[] data){
|
||||
try{
|
||||
Class c=Class.forName((String)jsch.getConfig("signature.ecdsa"));
|
||||
SignatureECDSA ecdsa=(SignatureECDSA)(c.newInstance());
|
||||
ecdsa.init();
|
||||
ecdsa.setPrvKey(prv_array);
|
||||
|
||||
ecdsa.update(data);
|
||||
byte[] sig = ecdsa.sign();
|
||||
|
||||
byte[][] tmp = new byte[2][];
|
||||
tmp[0] = Util.str2byte("ecdsa-sha2-"+new String(name));
|
||||
tmp[1] = sig;
|
||||
return Buffer.fromBytes(tmp).buffer;
|
||||
}
|
||||
catch(Exception e){
|
||||
//System.err.println("e "+e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Signature getVerifier(){
|
||||
try{
|
||||
Class c=Class.forName((String)jsch.getConfig("signature.ecdsa"));
|
||||
final SignatureECDSA ecdsa=(SignatureECDSA)(c.newInstance());
|
||||
ecdsa.init();
|
||||
|
||||
if(r_array == null && s_array == null && getPublicKeyBlob()!=null){
|
||||
Buffer buf = new Buffer(getPublicKeyBlob());
|
||||
buf.getString(); // ecdsa-sha2-nistp256
|
||||
buf.getString(); // nistp256
|
||||
byte[][] tmp = fromPoint(buf.getString());
|
||||
r_array = tmp[0];
|
||||
s_array = tmp[1];
|
||||
}
|
||||
ecdsa.setPubKey(r_array, s_array);
|
||||
return ecdsa;
|
||||
}
|
||||
catch(Exception e){
|
||||
//System.err.println("e "+e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static KeyPair fromSSHAgent(JSch jsch, Buffer buf) throws JSchException {
|
||||
|
||||
byte[][] tmp = buf.getBytes(5, "invalid key format");
|
||||
|
||||
byte[] name = tmp[1]; // nistp256
|
||||
byte[][] foo = fromPoint(tmp[2]);
|
||||
byte[] r_array = foo[0];
|
||||
byte[] s_array = foo[1];
|
||||
|
||||
byte[] prv_array = tmp[3];
|
||||
KeyPairECDSA kpair = new KeyPairECDSA(jsch,
|
||||
name,
|
||||
r_array, s_array,
|
||||
prv_array);
|
||||
kpair.publicKeyComment = new String(tmp[4]);
|
||||
kpair.vendor=VENDOR_OPENSSH;
|
||||
return kpair;
|
||||
}
|
||||
|
||||
public byte[] forSSHAgent() throws JSchException {
|
||||
if(isEncrypted()){
|
||||
throw new JSchException("key is encrypted.");
|
||||
}
|
||||
Buffer buf = new Buffer();
|
||||
buf.putString(Util.str2byte("ecdsa-sha2-"+new String(name)));
|
||||
buf.putString(name);
|
||||
buf.putString(toPoint(r_array, s_array));
|
||||
buf.putString(prv_array);
|
||||
buf.putString(Util.str2byte(publicKeyComment));
|
||||
byte[] result = new byte[buf.getLength()];
|
||||
buf.getByte(result, 0, result.length);
|
||||
return result;
|
||||
}
|
||||
|
||||
static byte[] toPoint(byte[] r_array, byte[] s_array) {
|
||||
byte[] tmp = new byte[1+r_array.length+s_array.length];
|
||||
tmp[0]=0x04;
|
||||
System.arraycopy(r_array, 0, tmp, 1, r_array.length);
|
||||
System.arraycopy(s_array, 0, tmp, 1+r_array.length, s_array.length);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static byte[][] fromPoint(byte[] point) {
|
||||
int i = 0;
|
||||
while(point[i]!=4) i++;
|
||||
i++;
|
||||
byte[][] tmp = new byte[2][];
|
||||
byte[] r_array = new byte[(point.length-i)/2];
|
||||
byte[] s_array = new byte[(point.length-i)/2];
|
||||
// point[0] == 0x04 == POINT_CONVERSION_UNCOMPRESSED
|
||||
System.arraycopy(point, i, r_array, 0, r_array.length);
|
||||
System.arraycopy(point, i+r_array.length, s_array, 0, s_array.length);
|
||||
tmp[0] = r_array;
|
||||
tmp[1] = s_array;
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
public void dispose(){
|
||||
super.dispose();
|
||||
Util.bzero(prv_array);
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -0,0 +1,37 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2015-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.jcraft.jsch;
|
||||
|
||||
public interface KeyPairGenECDSA{
|
||||
void init(int key_size) throws Exception;
|
||||
byte[] getD();
|
||||
byte[] getR();
|
||||
byte[] getS();
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -0,0 +1,363 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2013-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.jcraft.jsch;
|
||||
|
||||
import java.util.Vector;
|
||||
import java.math.BigInteger;
|
||||
|
||||
public class KeyPairPKCS8 extends KeyPair {
|
||||
private static final byte[] rsaEncryption = {
|
||||
(byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86,
|
||||
(byte)0xf7, (byte)0x0d, (byte)0x01, (byte)0x01, (byte)0x01
|
||||
};
|
||||
|
||||
private static final byte[] dsaEncryption = {
|
||||
(byte)0x2a, (byte)0x86, (byte)0x48, (byte)0xce,
|
||||
(byte)0x38, (byte)0x04, (byte)0x1
|
||||
};
|
||||
|
||||
private static final byte[] pbes2 = {
|
||||
(byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7,
|
||||
(byte)0x0d, (byte)0x01, (byte)0x05, (byte)0x0d
|
||||
};
|
||||
|
||||
private static final byte[] pbkdf2 = {
|
||||
(byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7,
|
||||
(byte)0x0d, (byte)0x01, (byte)0x05, (byte)0x0c
|
||||
};
|
||||
|
||||
private static final byte[] aes128cbc = {
|
||||
(byte)0x60, (byte)0x86, (byte)0x48, (byte)0x01, (byte)0x65,
|
||||
(byte)0x03, (byte)0x04, (byte)0x01, (byte)0x02
|
||||
};
|
||||
|
||||
private static final byte[] aes192cbc = {
|
||||
(byte)0x60, (byte)0x86, (byte)0x48, (byte)0x01, (byte)0x65,
|
||||
(byte)0x03, (byte)0x04, (byte)0x01, (byte)0x16
|
||||
};
|
||||
|
||||
private static final byte[] aes256cbc = {
|
||||
(byte)0x60, (byte)0x86, (byte)0x48, (byte)0x01, (byte)0x65,
|
||||
(byte)0x03, (byte)0x04, (byte)0x01, (byte)0x2a
|
||||
};
|
||||
|
||||
private static final byte[] pbeWithMD5AndDESCBC = {
|
||||
(byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7,
|
||||
(byte)0x0d, (byte)0x01, (byte)0x05, (byte)0x03
|
||||
};
|
||||
|
||||
private KeyPair kpair = null;
|
||||
|
||||
public KeyPairPKCS8(JSch jsch){
|
||||
super(jsch);
|
||||
}
|
||||
|
||||
void generate(int key_size) throws JSchException{
|
||||
}
|
||||
|
||||
private static final byte[] begin=Util.str2byte("-----BEGIN DSA PRIVATE KEY-----");
|
||||
private static final byte[] end=Util.str2byte("-----END DSA PRIVATE KEY-----");
|
||||
|
||||
byte[] getBegin(){ return begin; }
|
||||
byte[] getEnd(){ return end; }
|
||||
|
||||
byte[] getPrivateKey(){
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean parse(byte[] plain){
|
||||
|
||||
/* from RFC5208
|
||||
PrivateKeyInfo ::= SEQUENCE {
|
||||
version Version,
|
||||
privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
|
||||
privateKey PrivateKey,
|
||||
attributes [0] IMPLICIT Attributes OPTIONAL
|
||||
}
|
||||
Version ::= INTEGER
|
||||
PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
|
||||
PrivateKey ::= OCTET STRING
|
||||
Attributes ::= SET OF Attribute
|
||||
}
|
||||
*/
|
||||
|
||||
try{
|
||||
Vector values = new Vector();
|
||||
|
||||
ASN1[] contents = null;
|
||||
ASN1 asn1 = new ASN1(plain);
|
||||
contents = asn1.getContents();
|
||||
|
||||
ASN1 privateKeyAlgorithm = contents[1];
|
||||
ASN1 privateKey = contents[2];
|
||||
|
||||
contents = privateKeyAlgorithm.getContents();
|
||||
byte[] privateKeyAlgorithmID = contents[0].getContent();
|
||||
contents = contents[1].getContents();
|
||||
if(contents.length>0){
|
||||
for(int i = 0; i < contents.length; i++){
|
||||
values.addElement(contents[i].getContent());
|
||||
}
|
||||
}
|
||||
|
||||
byte[] _data = privateKey.getContent();
|
||||
|
||||
KeyPair _kpair = null;
|
||||
if(Util.array_equals(privateKeyAlgorithmID, rsaEncryption)){
|
||||
_kpair = new KeyPairRSA(jsch);
|
||||
_kpair.copy(this);
|
||||
if(_kpair.parse(_data)){
|
||||
kpair = _kpair;
|
||||
}
|
||||
}
|
||||
else if(Util.array_equals(privateKeyAlgorithmID, dsaEncryption)){
|
||||
asn1 = new ASN1(_data);
|
||||
if(values.size() == 0) { // embedded DSA parameters format
|
||||
/*
|
||||
SEQUENCE
|
||||
SEQUENCE
|
||||
INTEGER // P_array
|
||||
INTEGER // Q_array
|
||||
INTEGER // G_array
|
||||
INTEGER // prv_array
|
||||
*/
|
||||
contents = asn1.getContents();
|
||||
byte[] bar = contents[1].getContent();
|
||||
contents = contents[0].getContents();
|
||||
for(int i = 0; i < contents.length; i++){
|
||||
values.addElement(contents[i].getContent());
|
||||
}
|
||||
values.addElement(bar);
|
||||
}
|
||||
else {
|
||||
/*
|
||||
INTEGER // prv_array
|
||||
*/
|
||||
values.addElement(asn1.getContent());
|
||||
}
|
||||
|
||||
byte[] P_array = (byte[])values.elementAt(0);
|
||||
byte[] Q_array = (byte[])values.elementAt(1);
|
||||
byte[] G_array = (byte[])values.elementAt(2);
|
||||
byte[] prv_array = (byte[])values.elementAt(3);
|
||||
// Y = g^X mode p
|
||||
byte[] pub_array =
|
||||
(new BigInteger(G_array)).
|
||||
modPow(new BigInteger(prv_array), new BigInteger(P_array)).
|
||||
toByteArray();
|
||||
|
||||
KeyPairDSA _key = new KeyPairDSA(jsch,
|
||||
P_array, Q_array, G_array,
|
||||
pub_array, prv_array);
|
||||
plain = _key.getPrivateKey();
|
||||
|
||||
_kpair = new KeyPairDSA(jsch);
|
||||
_kpair.copy(this);
|
||||
if(_kpair.parse(plain)){
|
||||
kpair = _kpair;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(ASN1Exception e){
|
||||
return false;
|
||||
}
|
||||
catch(Exception e){
|
||||
//System.err.println(e);
|
||||
return false;
|
||||
}
|
||||
return kpair != null;
|
||||
}
|
||||
|
||||
public byte[] getPublicKeyBlob(){
|
||||
return kpair.getPublicKeyBlob();
|
||||
}
|
||||
|
||||
byte[] getKeyTypeName(){ return kpair.getKeyTypeName();}
|
||||
public int getKeyType(){return kpair.getKeyType();}
|
||||
|
||||
public int getKeySize(){
|
||||
return kpair.getKeySize();
|
||||
}
|
||||
|
||||
public byte[] getSignature(byte[] data){
|
||||
return kpair.getSignature(data);
|
||||
}
|
||||
|
||||
public Signature getVerifier(){
|
||||
return kpair.getVerifier();
|
||||
}
|
||||
|
||||
public byte[] forSSHAgent() throws JSchException {
|
||||
return kpair.forSSHAgent();
|
||||
}
|
||||
|
||||
public boolean decrypt(byte[] _passphrase){
|
||||
if(!isEncrypted()){
|
||||
return true;
|
||||
}
|
||||
if(_passphrase==null){
|
||||
return !isEncrypted();
|
||||
}
|
||||
|
||||
/*
|
||||
SEQUENCE
|
||||
SEQUENCE
|
||||
OBJECT :PBES2
|
||||
SEQUENCE
|
||||
SEQUENCE
|
||||
OBJECT :PBKDF2
|
||||
SEQUENCE
|
||||
OCTET STRING [HEX DUMP]:E4E24ADC9C00BD4D
|
||||
INTEGER :0800
|
||||
SEQUENCE
|
||||
OBJECT :aes-128-cbc
|
||||
OCTET STRING [HEX DUMP]:5B66E6B3BF03944C92317BC370CC3AD0
|
||||
OCTET STRING [HEX DUMP]:
|
||||
|
||||
or
|
||||
|
||||
SEQUENCE
|
||||
SEQUENCE
|
||||
OBJECT :pbeWithMD5AndDES-CBC
|
||||
SEQUENCE
|
||||
OCTET STRING [HEX DUMP]:DBF75ECB69E3C0FC
|
||||
INTEGER :0800
|
||||
OCTET STRING [HEX DUMP]
|
||||
*/
|
||||
|
||||
try{
|
||||
|
||||
ASN1[] contents = null;
|
||||
ASN1 asn1 = new ASN1(data);
|
||||
|
||||
contents = asn1.getContents();
|
||||
|
||||
byte[] _data = contents[1].getContent();
|
||||
|
||||
ASN1 pbes = contents[0];
|
||||
contents = pbes.getContents();
|
||||
byte[] pbesid = contents[0].getContent();
|
||||
ASN1 pbesparam = contents[1];
|
||||
|
||||
byte[] salt = null;
|
||||
int iterations = 0;
|
||||
byte[] iv = null;
|
||||
byte[] encryptfuncid = null;
|
||||
|
||||
if(Util.array_equals(pbesid, pbes2)){
|
||||
contents = pbesparam.getContents();
|
||||
ASN1 pbkdf = contents[0];
|
||||
ASN1 encryptfunc = contents[1];
|
||||
contents = pbkdf.getContents();
|
||||
byte[] pbkdfid = contents[0].getContent();
|
||||
ASN1 pbkdffunc = contents[1];
|
||||
contents = pbkdffunc.getContents();
|
||||
salt = contents[0].getContent();
|
||||
iterations =
|
||||
Integer.parseInt((new BigInteger(contents[1].getContent())).toString());
|
||||
|
||||
contents = encryptfunc.getContents();
|
||||
encryptfuncid = contents[0].getContent();
|
||||
iv = contents[1].getContent();
|
||||
}
|
||||
else if(Util.array_equals(pbesid, pbeWithMD5AndDESCBC)){
|
||||
// not supported
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
Cipher cipher=getCipher(encryptfuncid);
|
||||
if(cipher==null) return false;
|
||||
|
||||
byte[] key=null;
|
||||
try{
|
||||
Class c=Class.forName((String)jsch.getConfig("pbkdf"));
|
||||
PBKDF tmp=(PBKDF)(c.newInstance());
|
||||
key = tmp.getKey(_passphrase, salt, iterations, cipher.getBlockSize());
|
||||
}
|
||||
catch(Exception ee){
|
||||
}
|
||||
|
||||
if(key==null){
|
||||
return false;
|
||||
}
|
||||
|
||||
cipher.init(Cipher.DECRYPT_MODE, key, iv);
|
||||
Util.bzero(key);
|
||||
byte[] plain=new byte[_data.length];
|
||||
cipher.update(_data, 0, _data.length, plain, 0);
|
||||
if(parse(plain)){
|
||||
encrypted=false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch(ASN1Exception e){
|
||||
// System.err.println(e);
|
||||
}
|
||||
catch(Exception e){
|
||||
// System.err.println(e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Cipher getCipher(byte[] id){
|
||||
Cipher cipher=null;
|
||||
String name = null;
|
||||
try{
|
||||
if(Util.array_equals(id, aes128cbc)){
|
||||
name="aes128-cbc";
|
||||
}
|
||||
else if(Util.array_equals(id, aes192cbc)){
|
||||
name="aes192-cbc";
|
||||
}
|
||||
else if(Util.array_equals(id, aes256cbc)){
|
||||
name="aes256-cbc";
|
||||
}
|
||||
Class c=Class.forName((String)jsch.getConfig(name));
|
||||
cipher=(Cipher)(c.newInstance());
|
||||
}
|
||||
catch(Exception e){
|
||||
if(JSch.getLogger().isEnabled(Logger.FATAL)){
|
||||
String message="";
|
||||
if(name==null){
|
||||
message="unknown oid: "+Util.toHex(id);
|
||||
}
|
||||
else {
|
||||
message="function "+name+" is not supported";
|
||||
}
|
||||
JSch.getLogger().log(Logger.FATAL, "PKCS8: "+message);
|
||||
}
|
||||
}
|
||||
return cipher;
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
@@ -35,12 +35,6 @@ public
|
||||
class KnownHosts implements HostKeyRepository{
|
||||
private static final String _known_hosts="known_hosts";
|
||||
|
||||
/*
|
||||
static final int SSHDSS=0;
|
||||
static final int SSHRSA=1;
|
||||
static final int UNKNOWN=2;
|
||||
*/
|
||||
|
||||
private JSch jsch=null;
|
||||
private String known_hosts=null;
|
||||
private java.util.Vector pool=null;
|
||||
@@ -50,26 +44,28 @@ class KnownHosts implements HostKeyRepository{
|
||||
KnownHosts(JSch jsch){
|
||||
super();
|
||||
this.jsch=jsch;
|
||||
this.hmacsha1 = getHMACSHA1();
|
||||
pool=new java.util.Vector();
|
||||
}
|
||||
|
||||
void setKnownHosts(String foo) throws JSchException{
|
||||
void setKnownHosts(String filename) throws JSchException{
|
||||
try{
|
||||
known_hosts = foo;
|
||||
FileInputStream fis=new FileInputStream(Util.checkTilde(foo));
|
||||
known_hosts = filename;
|
||||
FileInputStream fis=new FileInputStream(Util.checkTilde(filename));
|
||||
setKnownHosts(fis);
|
||||
}
|
||||
catch(FileNotFoundException e){
|
||||
// The non-existing file should be allowed.
|
||||
}
|
||||
}
|
||||
void setKnownHosts(InputStream foo) throws JSchException{
|
||||
void setKnownHosts(InputStream input) throws JSchException{
|
||||
pool.removeAllElements();
|
||||
StringBuffer sb=new StringBuffer();
|
||||
byte i;
|
||||
int j;
|
||||
boolean error=false;
|
||||
try{
|
||||
InputStream fis=foo;
|
||||
InputStream fis=input;
|
||||
String host;
|
||||
String key=null;
|
||||
int type;
|
||||
@@ -158,8 +154,10 @@ loop:
|
||||
if(i==0x20 || i=='\t'){ break; }
|
||||
sb.append((char)i);
|
||||
}
|
||||
if(sb.toString().equals("ssh-dss")){ type=HostKey.SSHDSS; }
|
||||
else if(sb.toString().equals("ssh-rsa")){ type=HostKey.SSHRSA; }
|
||||
String tmp = sb.toString();
|
||||
if(HostKey.name2type(tmp)!=HostKey.UNKNOWN){
|
||||
type=HostKey.name2type(tmp);
|
||||
}
|
||||
else { j=bufl; }
|
||||
if(j>=bufl){
|
||||
addInvalidLine(Util.byte2str(buf, 0, bufl));
|
||||
@@ -223,7 +221,6 @@ loop:
|
||||
key.length()), comment);
|
||||
pool.addElement(hk);
|
||||
}
|
||||
fis.close();
|
||||
if(error){
|
||||
throw new JSchException("KnownHosts: invalid format");
|
||||
}
|
||||
@@ -235,6 +232,12 @@ loop:
|
||||
throw new JSchException(e.toString(), (Throwable)e);
|
||||
throw new JSchException(e.toString());
|
||||
}
|
||||
finally {
|
||||
try{ input.close(); }
|
||||
catch(IOException e){
|
||||
throw new JSchException(e.toString(), (Throwable)e);
|
||||
}
|
||||
}
|
||||
}
|
||||
private void addInvalidLine(String line) throws JSchException {
|
||||
HostKey hk = new HostKey(line, HostKey.UNKNOWN, null);
|
||||
@@ -249,14 +252,19 @@ loop:
|
||||
return result;
|
||||
}
|
||||
|
||||
int type=getType(key);
|
||||
HostKey hk;
|
||||
HostKey hk = null;
|
||||
try {
|
||||
hk = new HostKey(host, HostKey.GUESS, key);
|
||||
}
|
||||
catch(JSchException e){ // unsupported key
|
||||
return result;
|
||||
}
|
||||
|
||||
synchronized(pool){
|
||||
for(int i=0; i<pool.size(); i++){
|
||||
hk=(HostKey)(pool.elementAt(i));
|
||||
if(hk.isMatched(host) && hk.type==type){
|
||||
if(Util.array_equals(hk.key, key)){
|
||||
HostKey _hk=(HostKey)(pool.elementAt(i));
|
||||
if(_hk.isMatched(host) && _hk.type==hk.type){
|
||||
if(Util.array_equals(_hk.key, key)){
|
||||
return OK;
|
||||
}
|
||||
else{
|
||||
@@ -347,27 +355,29 @@ loop:
|
||||
}
|
||||
public HostKey[] getHostKey(String host, String type){
|
||||
synchronized(pool){
|
||||
int count=0;
|
||||
java.util.ArrayList v = new java.util.ArrayList();
|
||||
for(int i=0; i<pool.size(); i++){
|
||||
HostKey hk=(HostKey)pool.elementAt(i);
|
||||
if(hk.type==HostKey.UNKNOWN) continue;
|
||||
if(host==null ||
|
||||
(hk.isMatched(host) &&
|
||||
(type==null || hk.getType().equals(type)))){
|
||||
count++;
|
||||
v.add(hk);
|
||||
}
|
||||
}
|
||||
if(count==0)return null;
|
||||
HostKey[] foo=new HostKey[count];
|
||||
int j=0;
|
||||
for(int i=0; i<pool.size(); i++){
|
||||
HostKey hk=(HostKey)pool.elementAt(i);
|
||||
if(hk.type==HostKey.UNKNOWN) continue;
|
||||
if(host==null ||
|
||||
(hk.isMatched(host) &&
|
||||
(type==null || hk.getType().equals(type)))){
|
||||
foo[j++]=hk;
|
||||
}
|
||||
HostKey[] foo = new HostKey[v.size()];
|
||||
for(int i=0; i<v.size(); i++){
|
||||
foo[i] = (HostKey)v.get(i);
|
||||
}
|
||||
if(host != null && host.startsWith("[") && host.indexOf("]:")>1){
|
||||
HostKey[] tmp =
|
||||
getHostKey(host.substring(1, host.indexOf("]:")), type);
|
||||
if(tmp.length > 0){
|
||||
HostKey[] bar = new HostKey[foo.length + tmp.length];
|
||||
System.arraycopy(foo, 0, bar, 0, foo.length);
|
||||
System.arraycopy(tmp, 0, bar, foo.length, tmp.length);
|
||||
foo = bar;
|
||||
}
|
||||
}
|
||||
return foo;
|
||||
}
|
||||
@@ -452,11 +462,7 @@ loop:
|
||||
System.err.println(e);
|
||||
}
|
||||
}
|
||||
private int getType(byte[] key){
|
||||
if(key[8]=='d') return HostKey.SSHDSS;
|
||||
if(key[8]=='r') return HostKey.SSHRSA;
|
||||
return HostKey.UNKNOWN;
|
||||
}
|
||||
|
||||
private String deleteSubString(String hosts, String host){
|
||||
int i=0;
|
||||
int hostlen=host.length();
|
||||
@@ -477,7 +483,7 @@ loop:
|
||||
return hosts;
|
||||
}
|
||||
|
||||
private synchronized MAC getHMACSHA1(){
|
||||
private MAC getHMACSHA1(){
|
||||
if(hmacsha1==null){
|
||||
try{
|
||||
Class c=Class.forName(jsch.getConfig("hmac-sha1"));
|
||||
@@ -503,7 +509,6 @@ loop:
|
||||
byte[] salt=null;
|
||||
byte[] hash=null;
|
||||
|
||||
|
||||
HashedHostKey(String host, byte[] key) throws JSchException {
|
||||
this(host, GUESS, key);
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2012-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
@@ -50,6 +50,7 @@ class LocalIdentityRepository implements IdentityRepository {
|
||||
}
|
||||
|
||||
public synchronized Vector getIdentities() {
|
||||
removeDupulicates();
|
||||
Vector v = new Vector();
|
||||
for(int i=0; i<identities.size(); i++){
|
||||
v.addElement(identities.elementAt(i));
|
||||
@@ -59,6 +60,23 @@ class LocalIdentityRepository implements IdentityRepository {
|
||||
|
||||
public synchronized void add(Identity identity) {
|
||||
if(!identities.contains(identity)) {
|
||||
byte[] blob1 = identity.getPublicKeyBlob();
|
||||
if(blob1 == null) {
|
||||
identities.addElement(identity);
|
||||
return;
|
||||
}
|
||||
for(int i = 0; i<identities.size(); i++){
|
||||
byte[] blob2 = ((Identity)identities.elementAt(i)).getPublicKeyBlob();
|
||||
if(blob2 != null && Util.array_equals(blob1, blob2)){
|
||||
if(!identity.isEncrypted() &&
|
||||
((Identity)identities.elementAt(i)).isEncrypted()){
|
||||
remove(blob2);
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
identities.addElement(identity);
|
||||
}
|
||||
}
|
||||
@@ -67,7 +85,7 @@ class LocalIdentityRepository implements IdentityRepository {
|
||||
try{
|
||||
Identity _identity =
|
||||
IdentityFile.newInstance("from remote:", identity, null, jsch);
|
||||
identities.addElement(_identity);
|
||||
add(_identity);
|
||||
return true;
|
||||
}
|
||||
catch(JSchException e){
|
||||
@@ -76,7 +94,13 @@ class LocalIdentityRepository implements IdentityRepository {
|
||||
}
|
||||
|
||||
synchronized void remove(Identity identity) {
|
||||
identities.removeElement(identity);
|
||||
if(identities.contains(identity)) {
|
||||
identities.removeElement(identity);
|
||||
identity.clear();
|
||||
}
|
||||
else {
|
||||
remove(identity.getPublicKeyBlob());
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized boolean remove(byte[] blob) {
|
||||
@@ -100,4 +124,28 @@ class LocalIdentityRepository implements IdentityRepository {
|
||||
}
|
||||
identities.removeAllElements();
|
||||
}
|
||||
|
||||
private void removeDupulicates(){
|
||||
Vector v = new Vector();
|
||||
int len = identities.size();
|
||||
if(len == 0) return;
|
||||
for(int i=0; i<len; i++){
|
||||
Identity foo = (Identity)identities.elementAt(i);
|
||||
byte[] foo_blob = foo.getPublicKeyBlob();
|
||||
if(foo_blob == null) continue;
|
||||
for(int j=i+1; j<len; j++){
|
||||
Identity bar = (Identity)identities.elementAt(j);
|
||||
byte[] bar_blob = bar.getPublicKeyBlob();
|
||||
if(bar_blob == null) continue;
|
||||
if(Util.array_equals(foo_blob, bar_blob) &&
|
||||
foo.isEncrypted() == bar.isEncrypted()){
|
||||
v.addElement(foo_blob);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int i=0; i<v.size(); i++){
|
||||
remove((byte[])v.elementAt(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2006-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2006-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2013 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2013-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
@@ -30,10 +30,9 @@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
package com.jcraft.jsch;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.util.Hashtable;
|
||||
@@ -78,12 +77,12 @@ public class OpenSSHConfig implements ConfigRepository {
|
||||
* @return an instanceof OpenSSHConfig
|
||||
*/
|
||||
public static OpenSSHConfig parse(String conf) throws IOException {
|
||||
InputStream in = new ByteArrayInputStream(Util.str2byte(conf));
|
||||
Reader r = new StringReader(conf);
|
||||
try {
|
||||
return new OpenSSHConfig(in);
|
||||
return new OpenSSHConfig(r);
|
||||
}
|
||||
finally {
|
||||
in.close();
|
||||
r.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,25 +93,24 @@ public class OpenSSHConfig implements ConfigRepository {
|
||||
* @return an instanceof OpenSSHConfig
|
||||
*/
|
||||
public static OpenSSHConfig parseFile(String file) throws IOException {
|
||||
byte[] conf = Util.fromFile(file);
|
||||
InputStream in = new ByteArrayInputStream(conf);
|
||||
Reader r = new FileReader(Util.checkTilde(file));
|
||||
try {
|
||||
return new OpenSSHConfig(in);
|
||||
return new OpenSSHConfig(r);
|
||||
}
|
||||
finally {
|
||||
in.close();
|
||||
r.close();
|
||||
}
|
||||
}
|
||||
|
||||
OpenSSHConfig(InputStream in) throws IOException {
|
||||
_parse(in);
|
||||
OpenSSHConfig(Reader r) throws IOException {
|
||||
_parse(r);
|
||||
}
|
||||
|
||||
private final Hashtable config = new Hashtable();
|
||||
private final Vector hosts = new Vector();
|
||||
|
||||
private void _parse(InputStream in) throws IOException {
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(in));
|
||||
private void _parse(Reader r) throws IOException {
|
||||
BufferedReader br = new BufferedReader(r);
|
||||
|
||||
String host = "";
|
||||
Vector/*<String[]>*/ kv = new Vector();
|
||||
@@ -200,12 +198,13 @@ public class OpenSSHConfig implements ConfigRepository {
|
||||
if(keymap.get(key)!=null) {
|
||||
key = (String)keymap.get(key);
|
||||
}
|
||||
key = key.toUpperCase();
|
||||
String value = null;
|
||||
for(int i = 0; i < _configs.size(); i++) {
|
||||
Vector v = (Vector)_configs.elementAt(i);
|
||||
for(int j = 0; j < v.size(); j++) {
|
||||
String[] kv = (String[])v.elementAt(j);
|
||||
if(kv[0].equals(key)) {
|
||||
if(kv[0].toUpperCase().equals(key)) {
|
||||
value = kv[1];
|
||||
break;
|
||||
}
|
||||
@@ -217,12 +216,13 @@ public class OpenSSHConfig implements ConfigRepository {
|
||||
}
|
||||
|
||||
private String[] multiFind(String key) {
|
||||
key = key.toUpperCase();
|
||||
Vector value = new Vector();
|
||||
for(int i = 0; i < _configs.size(); i++) {
|
||||
Vector v = (Vector)_configs.elementAt(i);
|
||||
for(int j = 0; j < v.size(); j++) {
|
||||
String[] kv = (String[])v.elementAt(j);
|
||||
if(kv[0].equals(key)) {
|
||||
if(kv[0].toUpperCase().equals(key)) {
|
||||
String foo = kv[1];
|
||||
if(foo != null) {
|
||||
value.remove(foo);
|
||||
|
@@ -0,0 +1,34 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2013-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of the authors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.jcraft.jsch;
|
||||
|
||||
public interface PBKDF {
|
||||
byte[] getKey(byte[] pass, byte[] salt, int iteration, int size);
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
Copyright (c) 2002-2012 ymnk, JCraft,Inc. All rights reserved.
|
||||
Copyright (c) 2002-2016 ymnk, JCraft,Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user