Discussion:
[1/5] mina-sshd git commit: [SSHD-757] Added initial support for reading PGP private key files
l***@apache.org
2018-10-31 05:03:09 UTC
Permalink
Repository: mina-sshd
Updated Branches:
refs/heads/master 4e12d7e17 -> 259ee8c7f


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/RSA-4096-vp2p0p8-public.asc
----------------------------------------------------------------------
diff --git a/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/RSA-4096-vp2p0p8-public.asc b/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/RSA-4096-vp2p0p8-public.asc
new file mode 100644
index 0000000..57cf1a1
--- /dev/null
+++ b/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/RSA-4096-vp2p0p8-public.asc
@@ -0,0 +1,76 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: OpenPGP v2.0.8
+Comment: https://sela.io/pgp/
+
+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
+TNL65xN7VI82PYZzd5AD5324ptXWtfyr
+=c2Hk
+-----END PGP PUBLIC KEY BLOCK-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/ed25519-gpg4win-3.1.3.asc
----------------------------------------------------------------------
diff --git a/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/ed25519-gpg4win-3.1.3.asc b/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/ed25519-gpg4win-3.1.3.asc
new file mode 100644
index 0000000..951be81
--- /dev/null
+++ b/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/ed25519-gpg4win-3.1.3.asc
@@ -0,0 +1,9 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mDMEW5PrKhYJKwYBBAHaRw8BAQdAFEr4U460sxSsPsAlkOWUfwDKKcPKc7IbtGlO
+rRd1XKC0Jkx5b3IgR29sZHN0ZWluIDxsZ29sZHN0ZWluQGFwYWNoZS5vcmc+iJAE
+ExYIADgWIQSw+xFriixO62qMvtQcQjYkqkw8iwUCW5PrKgIbIwULCQgHAgYVCgkI
+CwIEFgIDAQIeAQIXgAAKCRAcQjYkqkw8i5KtAPwKzy3gOTZEMcPJa1QnuwubHUx8
+nGoWX1yt6wZMApIS0QD/fuwsTXfs/LnuUcpqvgFNAeVZ7Xj5Mq+Tm8b/tk+uJQ0=
+=rzUO
+-----END PGP PUBLIC KEY BLOCK-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/super-secret-passphrase-DSA-2048-gpg4win-3.1.3.gpg
----------------------------------------------------------------------
diff --git a/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/super-secret-passphrase-DSA-2048-gpg4win-3.1.3.gpg b/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/super-secret-passphrase-DSA-2048-gpg4win-3.1.3.gpg
new file mode 100644
index 0000000..310f4bc
--- /dev/null
+++ b/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/super-secret-passphrase-DSA-2048-gpg4win-3.1.3.gpg
@@ -0,0 +1,27 @@
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+
+lQOBBFuT5zARCADAPwVpS4++wgCKgYNq/J0afO3lAsBKGBdRIPKNS4aFpEZNxVxF
+QbeazCE9qKh360hYy8bO0JeJeto59ZdZ1DxPmLm7RSpkvvDLcZzgEqa0HTPH4WaZ
+5xANi1p1SgCs6BHKcuPVj42BPNMmxGSXVW7/dsr8GKOAq45nXllwJrUC3Vs9AEtz
+nSCjudw5os03OhPj31+EYnGnFvQSgZIpWVyDyjapd0fmnYGKg3G+cKuw4KZ6zR9G
+LY5/hu6QPmilBwQFhmJhnd5vSQTFiLpLXvHgfdQ6CtMPng4njydBKCTL7a/qP5t1
+c2t3Tc1JGmN0vq1lH2/weug12icfI2VARgX/AQCL2NCPIv0b5XtvHd/YEijPkapL
+AfxwCA0K55/lqjFMlwf+KB9MY1xl0r0FBx+x+1xa/pvgBXjQwh1g+mvxWY1f2YZo
+VIGWBRD+b1m2MJKgDZFgJ4bAhUWmll96+7Scxy7fO1PBWKdxiXBfwxjYgTipaxOB
+oDYEVwt/ZEm7oI1NXyZMIRNkINwz1L5AvZg9zAXVAhaxJvwpWJU7YK42QMzJkaF8
+AIlfjbrh7g4ordGj6OH/pzxB8fTYmM79hg7oN1syXdxiUX8h37tnvBehlCOyD68k
+UoEmP8EEaeukHSUXbSObHNyPAJmEjzkpEfSPNGeuU3VaLeca5Ofw5tBRgzAyjj9j
+7VG2yfn34ox2t4CfCS+Lrn1yvLuhd/AUgHfC7T0+Zwf/R/Pq7JFIT/nWgZQIf4TC
+7Up6fj1QoOy8qwQ2oh6ugCtSGO6pxCcebjZntg18NUQwMBW1h+gASi3vAWhOcsEh
+cRr5ZQE3wgxSfIvNiFNJmxjBqQztN1Ur+eXSPUX3eS+xiTyKGIYoAv1p3vSkrGd/
+8pb5rq0i4xaOFrAgMQhe/vby0KRULZpYjog4lzDbLHDcg+qyOusJ1wsKYWhGlJe3
+edb75xE9lpT/jEyLFkFU9KOhtw4kpbU3/KpaZBTPbTv2cZqaYrtve4j7Qi3rBsJq
+uURZlAWCeJjtmRlVEtKOzCd+ba4Afmy+pBkngy7qb/Ms2HFO4QeAjvMQROgJRo9I
+rP4HAwIJrLqA/EFKZsPDXN1rnZvURTVpIM7T9k8RpZAMIS08BKfzcn1y5fn1nRWf
+qYwyP6ta1tmBJ1AkN0noJQcevvfAvBDuVXzifj8sGSZLAaZ5tCZMeW9yIEdvbGRz
+dGVpbiA8bGdvbGRzdGVpbkBhcGFjaGUub3JnPoiQBBMRCAA4FiEESLDM6PGGKw9K
+12frjBd5IzvR48AFAluT5zACGyMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQ
+jBd5IzvR48Av/QEAipbEPorqWUj01YrmNdOmkF4d8QYLXzZj0QCRvEYu0AwA/RFi
+3jUK6GPq6h1xkaihb/b+k7d9tvpl4YMA3mjzRsGB
+=lGVm
+-----END PGP PRIVATE KEY BLOCK-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/super-secret-passphrase-EC-384-v1p0-private.gpg
----------------------------------------------------------------------
diff --git a/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/super-secret-passphrase-EC-384-v1p0-private.gpg b/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/super-secret-passphrase-EC-384-v1p0-private.gpg
new file mode 100644
index 0000000..5ee00ec
--- /dev/null
+++ b/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/super-secret-passphrase-EC-384-v1p0-private.gpg
@@ -0,0 +1,32 @@
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: Keybase OpenPGP v1.0.0
+Comment: https://keybase.io/crypto
+
+xcASBFuTTZgTBSuBBAAiAwME9DA8eCc2MsDiKSighjCpK0lU+AHkzqUB4ayFkZiv
+UWyYWErFI9xtqk+M+kYMOzD7O5bcgpLo3zccXrdESr2GYW3PVx/xfajkrLB1/Xwu
+r8S+PyV9oa8+zjYBF/9AuFIV/gkDCMrUKO/Ri8LiYPvugKZmoY3YJtE2n1WGzNpj
+UbD+SJAd4PGUJCZKksWGmuAJEm+e8P7F8uYbrgyhIKgMQXxJ2zAZeqjl+GFE/05/
+udwNv3/m0ET10RTD5Io8wZIuzePFzUlMeW9yIEdvbGRzdGVpbiAoQXBhY2hlIFNT
+SEQgRUNDIDM0OCBQR1AgdGVzdCBrZXkpIDxsZ29sZHN0ZWluQGFwYWNoZS5vcmc+
+wo8EExMKABcFAluTTZgCGy8DCwkHAxUKCAIeAQIXgAAKCRDhdQZVe8D/toGwAX4p
+dUR83Riet5rekVu+A207CoGcdPTqndq+LNCAREJrOq1yWfmcf8cb8jOKQnS5TP0B
+f3Mcldr53TrDS0B+Iiq09VYkAQTOjcnNhjvx6GqogYTTF+/9ndQ/FiuzF8TNko3M
+PcelBFuTTZgTCCqGSM49AwEHAgMEKmSVaWqYGODbFhaMCV3n5cx6H4CoaGIaCKBA
+xhpxamIvCvUf3NeD8mLHtm+krxegx+Npt+k9x65b6r2a+dpq0f4JAwiqgr0SnvWC
+7GCT3R1ZQ8g40P3+XWk96kQalXaRQ2QGwyNBZ881FuSxf1jQOkxolXgLSLi6y9cn
+IrFXff1eSRS7isXS6BtznHOYyybZP9anwsAnBBgTCgAPBQJbk02YBQkPCZwAAhsu
+AGoJEOF1BlV7wP+2XyAEGRMKAAYFAluTTZgACgkQpwAX8HS3WvpDtQEAhpJAjrEK
+h7K9Pl6eUUKaIWI1YxzJ4WFeTcalW5xRM3cBALg1uyXQYsZE1zmulTpEKwGulyBX
+as3zifpNqg1Qfnre3a0BgMv+Q0mRVjf8tjnOHHUfqm5q8cGVhp1aBpzao3h/stGH
+SFXbnbbc9dZ9d6A4AJAswwF/X9qlxyCdVVPx/J4isAjtXcXOWNr8lKiC5cnIKi+6
+cEDKrog3NPt2JqW17AZ1r0Dix6UEW5NNmBMIKoZIzj0DAQcCAwQUKaq9zh1Srht7
+PhNpV4i8zwn1YkFAGC4SSb5DdoiBKqA6ab5gHenwAnAB46P1X7jaqY9lBj6Turya
+ll4JvXsU/gkDCGcO793OqawCYCehY/jwoskGywJ0wZW6VFMHd1R5oV7NfPWR98Au
+64FuT03QGyVsiOF9rYtI+uW6+7i/ttE+hTxN7wEKNGcFIzAXqaL4LfLCwCcEGBMK
+AA8FAluTTZgFCQ8JnAACGy4AagkQ4XUGVXvA/7ZfIAQZEwoABgUCW5NNmAAKCRAt
+I2RwROYMI4tzAPoCTGDpVrQQ+cb9MtpjAeb5luwTUqy13kpKhqaAP/+7VgD8D61s
+Bf6+iXKNFKbhK+PsBlHM57USc+DEvU8V+r7MYj2nvwF/Wd7qmTDulfzmJ9ylK60/
+jO98AWhhSS9rnNL8t/3MXxseYdCYuiiXjD7cp20iq7laAYCt6Frj9CalQx0/kMaf
+NtzZua4bthL7WXwOs02vxL1o7Y2jklTXczDEU5hmYWleUaM=
+=HopH
+-----END PGP PRIVATE KEY BLOCK-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/super-secret-passphrase-RSA-2048-v1p0-private.gpg
----------------------------------------------------------------------
diff --git a/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/super-secret-passphrase-RSA-2048-v1p0-private.gpg b/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/super-secret-passphrase-RSA-2048-v1p0-private.gpg
new file mode 100644
index 0000000..35ed45d
--- /dev/null
+++ b/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/super-secret-passphrase-RSA-2048-v1p0-private.gpg
@@ -0,0 +1,99 @@
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: Keybase OpenPGP v1.0.0
+Comment: https://keybase.io/crypto
+
+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
+=DYx9
+-----END PGP PRIVATE KEY BLOCK-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/super-secret-passphrase-RSA-2048-v1p6p1-private.gpg
----------------------------------------------------------------------
diff --git a/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/super-secret-passphrase-RSA-2048-v1p6p1-private.gpg b/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/super-secret-passphrase-RSA-2048-v1p6p1-private.gpg
new file mode 100644
index 0000000..32678f1
--- /dev/null
+++ b/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/super-secret-passphrase-RSA-2048-v1p6p1-private.gpg
@@ -0,0 +1,32 @@
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: BCPG C# v1.6.1.0
+
+lQOsBFuTTP0BCACGwHfOtKlKSNPophndVpK+M3OhxjFT/rKTBAr3zzzVAn1ofdbS
+oi0AysA95P++5mffGryhiNjLzLG8zehtU4/eij0NN1OFOtxILc0wViRqe6Ldikit
+fV6crknwA3q09vTWuzqQDus+lC1qxl36CA6FbnYWNQNt0iLL2rE3doOHAX4rSaIf
+JI19LKlJMsCQKAJhNq65ORg2hnOaycwtybmVhVXSGAw8La1TgbOp2M+loWhNDoFT
+H0tpUPNuNkadv3BqGMb90tG/uQLE9GrxJGOFsgRxertGNXesY1+JofCVJQuxXp1A
+yLTo6tr28oSLFOcfsDMuDAejnokP0QnEinGRABEBAAH/AwMCWVlVFfvOYPxgkUur
+PoNTE+sTyxgSk0go/XzOf/GEwDyR8MF4Z7YUY8ZZuTkmZLAz4pCVzqWjcrEZ6TKs
+wunmrgYkuR2Zon+LIUOAytHLc53zE6HyqZT1DwKox58/nmsPv9rq+8Kbh4phlmz8
+EkpMHZ/OGRCT2bE6eBTOU/eubxwiPx1UVagj5mBuBKkRahJidxQjRY23P3aDYmXm
+Ck+UnJWTe1N8hS9xumb+RzZU0GPmHNk9acP6swgKaC9wpSuAxmQbksgRD8WKIQmi
++o0cdUZXUel01vzk2wXnrAmSGvetk06t4JNNysyMcCS5akwmNiXiyAEAe8isQ9uN
+xP8EvSY7AqxwlO8GMDniIJgBafcVk0LQGtRy4xDY2WLKdYHTqOF7hn4khWcANkww
+vsDS6JiqMy6syaQco+uMRIctKGz/xLAsYT1aGQK1mbbDplKEi4JU7AP3LXIP9HzG
+ZJh+9DZe7O8x8UCcaHEhZGZvOm8TWNHe18s2KTGJJocs6G7PbsTXQa9rcJQI4Gf4
+AkSQzYbfQEKzTCOOSMSAsQJo2AVgEokrDsL4RAegmqQ34GJSUYUynqEQuzgLNtvA
+TTI1chEh+hSiMSTm9UT/EJdFlS0/De5zCnHkKrXU3G2Jc6p3gx9E9NKmQKho0f2B
+N5hMAGgaXTmIqh6Aho4ZAVyd3OKsreKT+9vSkKQCqhF3h6tN3j6YDuY8IlmmBHdp
+EBqIf22KMvbRbW3EsRi64QmQGrjsPanpYEQt8Oupuu7va+v1RAh2ICltOzQQ+Tod
+L4TqvopWeGtZnS7TPh1phPlsxiYGPmCDkIrHv1FZSJSiMekq4yg3JmgVrZyhO8qP
+ZjXZP8Wrqfe1Vr4oeQA1sedYScsDrEVZJWTx8Eh757QVbGdvbGRzdGVpbkBhcGFj
+aGUub3JniQEcBBABAgAGBQJbk0z9AAoJEMHz7u+45tHgZZQH/1BoabnEtIu3E2gZ
+NK0m16eQ00z+6tGy7uJke+PUQus5R98NLt1r+4ipe1aHrG1Hy8Mb0HIKb5anjs3p
+qYJuam3ehgpMpCEHFn3DshzcRodKZwAv6uIudm2zfXc8Kbs1kBI07/cfojhHxMc1
+S0/RR3DKYrQ7u+EOWGPT3h9opLRd5LJyRmzk1BjretwYr34OPdMaU7aNmj8mdg4F
+zcPEgK0Lb8ehd+QaFk/DQOM58xgOFZHEN18/h+2kceI5BMtLISrp6dCE06XCDQo+
+NCa8fraJahFI4JwqozQ8GV+3OOywVGogKwAikTS6vNFoZrDPMbfjwaheM/dsRVLD
+OewF72o=
+=DPAb
+-----END PGP PRIVATE KEY BLOCK-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/super-secret-passphrase-RSA-4096-v2p0p8-private.gpg
----------------------------------------------------------------------
diff --git a/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/super-secret-passphrase-RSA-4096-v2p0p8-private.gpg b/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/super-secret-passphrase-RSA-4096-v2p0p8-private.gpg
new file mode 100644
index 0000000..0ea88bf
--- /dev/null
+++ b/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/super-secret-passphrase-RSA-4096-v2p0p8-private.gpg
@@ -0,0 +1,133 @@
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: OpenPGP v2.0.8
+Comment: https://sela.io/pgp/
+
+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
+e1SPNj2Gc3eQA+d9uKbV1rX8qw==
+=PVOG
+-----END PGP PRIVATE KEY BLOCK-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/super-secret-passphrase-ed25519-gpg4win-3.1.3.gpg
----------------------------------------------------------------------
diff --git a/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/super-secret-passphrase-ed25519-gpg4win-3.1.3.gpg b/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/super-secret-passphrase-ed25519-gpg4win-3.1.3.gpg
new file mode 100644
index 0000000..1f7e0c4
--- /dev/null
+++ b/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/super-secret-passphrase-ed25519-gpg4win-3.1.3.gpg
@@ -0,0 +1,11 @@
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+
+lIYEW5PrKhYJKwYBBAHaRw8BAQdAFEr4U460sxSsPsAlkOWUfwDKKcPKc7IbtGlO
+rRd1XKD+BwMClwoesYdnT6fDwn3XqonNzdO55Ya2MDM3/D1TWE/qIKGBqQ8YjIQm
+ePfZQfnwWcavlC9n8/+kKk2eHM1PNiBMRl/q7k7uA44pFv49Dq0xPrQmTHlvciBH
+b2xkc3RlaW4gPGxnb2xkc3RlaW5AYXBhY2hlLm9yZz6IkAQTFggAOBYhBLD7EWuK
+LE7raoy+1BxCNiSqTDyLBQJbk+sqAhsjBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheA
+AAoJEBxCNiSqTDyLkq0A/ArPLeA5NkQxw8lrVCe7C5sdTHycahZfXK3rBkwCkhLR
+AP9+7CxNd+z8ue5Rymq+AU0B5VntePkyr5Obxv+2T64lDQ==
+=Zirx
+-----END PGP PRIVATE KEY BLOCK-----
l***@apache.org
2018-10-31 05:03:11 UTC
Permalink
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-common/src/main/java/org/apache/sshd/common/SshConstants.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/SshConstants.java b/sshd-common/src/main/java/org/apache/sshd/common/SshConstants.java
index 01e8ae7..ee8ae63 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/SshConstants.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/SshConstants.java
@@ -33,6 +33,8 @@ import org.apache.sshd.common.util.logging.LoggingUtils;
* @author <a href="mailto:***@mina.apache.org">Apache MINA SSHD Project</a>
*/
public final class SshConstants {
+ public static final int DEFAULT_PORT = 22;
+
//
// SSH message identifiers
//

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java b/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java
index aea9f24..b6cab4f 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java
@@ -58,7 +58,6 @@ public final class ConfigFileReaderSupport {
public static final String LISTEN_ADDRESS_CONFIG_PROP = "ListenAddress";
public static final String DEFAULT_BIND_ADDRESS = SshdSocketAddress.IPV4_ANYADDR;
public static final String PORT_CONFIG_PROP = "Port";
- public static final int DEFAULT_PORT = 22;
public static final String KEEP_ALIVE_CONFIG_PROP = "TCPKeepAlive";
public static final boolean DEFAULT_KEEP_ALIVE = true;
public static final String USE_DNS_CONFIG_PROP = "UseDNS";

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashEntryTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashEntryTest.java b/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashEntryTest.java
index 6bf1177..1b76ff0 100644
--- a/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashEntryTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashEntryTest.java
@@ -22,7 +22,7 @@ package org.apache.sshd.client.config.hosts;
import java.util.Arrays;
import java.util.List;

-import org.apache.sshd.common.config.ConfigFileReaderSupport;
+import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
import org.apache.sshd.util.test.JUnitTestSupport;
@@ -64,7 +64,7 @@ public class KnownHostHashEntryTest extends JUnitTestSupport {
return Arrays.asList(
// line generated `ssh ***@localhost hostname` (SSH-2.0-OpenSSH_7.5)
new Object[] {
- "localhost", ConfigFileReaderSupport.DEFAULT_PORT,
+ "localhost", SshConstants.DEFAULT_PORT,
"|1|vLQs+atPgodQmPes21ZaMSgLD0s=|A2K2Ym0ZPtQmD8kB3FVViQvQ7qQ=", "ecdsa-sha2-nistp256",
"AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJTsDTYFSYyRMlOec6JBfC8dEFqHNNWu7n8N0niS1zmHpggX+L4cndxhJPE0ILi9otHO7h0mp0cmqqho2tsX8lc=",
"***@localhost"
@@ -92,7 +92,7 @@ public class KnownHostHashEntryTest extends JUnitTestSupport {

@Test
public void testHostHashMatchOnDefaultPort() {
- Assume.assumeTrue("No-default port used", port == ConfigFileReaderSupport.DEFAULT_PORT);
+ Assume.assumeTrue("No-default port used", port == SshConstants.DEFAULT_PORT);
KnownHostEntry entry = KnownHostEntry.parseKnownHostEntry(line);
assertTrue(entry.isHostMatch(host, 0));
}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java b/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java
index 3ecea7f..72c6e89 100644
--- a/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java
+++ b/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java
@@ -22,7 +22,7 @@ package org.apache.sshd.client.config.hosts;
import java.util.Arrays;
import java.util.Collection;

-import org.apache.sshd.common.config.ConfigFileReaderSupport;
+import org.apache.sshd.common.SshConstants;
import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
import org.apache.sshd.util.test.JUnitTestSupport;
import org.apache.sshd.util.test.NoIoTestCase;
@@ -62,9 +62,9 @@ public class KnownHostHashValueTest extends JUnitTestSupport {
new Object[]{"localhost", 10022,
"|1|qhjoqX12EcnwZO3KNbpoFbxrdYE=|J+voEFzRbRL49TiHV+jbUfaS+kg="},
// line generated `ssh ***@localhost hostname` (SSH-2.0-OpenSSH_7.5)
- new Object[]{"localhost", ConfigFileReaderSupport.DEFAULT_PORT,
+ new Object[]{"localhost", SshConstants.DEFAULT_PORT,
"|1|vLQs+atPgodQmPes21ZaMSgLD0s=|A2K2Ym0ZPtQmD8kB3FVViQvQ7qQ="},
- new Object[]{"192.168.1.61", ConfigFileReaderSupport.DEFAULT_PORT,
+ new Object[]{"192.168.1.61", SshConstants.DEFAULT_PORT,
"|1|F1E1KeoE/eEWhi10WpGv4OdiO6Y=|3988QV0VE8wmZL7suNrYQLITLCg="}
);
}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java b/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
index 84339c3..227a4d5 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java
@@ -63,6 +63,7 @@ import org.apache.sshd.client.session.ClientUserAuthServiceFactory;
import org.apache.sshd.client.session.SessionFactory;
import org.apache.sshd.client.simple.AbstractSimpleClientSessionCreator;
import org.apache.sshd.client.simple.SimpleClient;
+import org.apache.sshd.common.AttributeRepository;
import org.apache.sshd.common.Closeable;
import org.apache.sshd.common.Factory;
import org.apache.sshd.common.NamedFactory;
@@ -441,7 +442,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa

@Override
public ConnectFuture connect(
- String username, String host, int port, SocketAddress localAddress)
+ String username, String host, int port, AttributeRepository context, SocketAddress localAddress)
throws IOException {
HostConfigEntryResolver resolver = getHostConfigEntryResolver();
HostConfigEntry entry = resolver.resolveEffectiveHost(host, port, username);
@@ -464,12 +465,12 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
}
}

- return connect(entry, localAddress);
+ return connect(entry, context, localAddress);
}

@Override
public ConnectFuture connect(
- String username, SocketAddress targetAddress, SocketAddress localAddress)
+ String username, SocketAddress targetAddress, AttributeRepository context, SocketAddress localAddress)
throws IOException {
Objects.requireNonNull(targetAddress, "No target address");
if (targetAddress instanceof InetSocketAddress) {
@@ -485,7 +486,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
log.debug("connect({}@{}:{}) no overrides", username, host, port);
}

- return doConnect(username, targetAddress, localAddress, Collections.emptyList(), true);
+ return doConnect(username, targetAddress, context, localAddress, Collections.emptyList(), true);
} else {
if (log.isDebugEnabled()) {
log.debug("connect({}@{}:{}) effective: {}", username, host, port, entry);
@@ -497,12 +498,14 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
if (log.isDebugEnabled()) {
log.debug("connect({}@{}) not an InetSocketAddress: {}", username, targetAddress, targetAddress.getClass().getName());
}
- return doConnect(username, targetAddress, localAddress, Collections.emptyList(), true);
+ return doConnect(username, targetAddress, context, localAddress, Collections.emptyList(), true);
}
}

@Override
- public ConnectFuture connect(HostConfigEntry hostConfig, SocketAddress localAddress) throws IOException {
+ public ConnectFuture connect(
+ HostConfigEntry hostConfig, AttributeRepository context, SocketAddress localAddress)
+ throws IOException {
Objects.requireNonNull(hostConfig, "No host configuration");
String host = ValidateUtils.checkNotNullAndNotEmpty(hostConfig.getHostName(), "No target host");
int port = hostConfig.getPort();
@@ -510,7 +513,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa

Collection<KeyPair> keys = loadClientIdentities(hostConfig.getIdentities(), IoUtils.EMPTY_LINK_OPTIONS);
return doConnect(hostConfig.getUsername(), new InetSocketAddress(host, port),
- localAddress, keys, !hostConfig.isIdentitiesOnly());
+ context, localAddress, keys, !hostConfig.isIdentitiesOnly());
}

protected List<KeyPair> loadClientIdentities(Collection<String> locations, LinkOption... options) throws IOException {
@@ -556,7 +559,8 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
}

protected ConnectFuture doConnect(
- String username, SocketAddress targetAddress, SocketAddress localAddress,
+ String username, SocketAddress targetAddress,
+ AttributeRepository context, SocketAddress localAddress,
Collection<? extends KeyPair> identities, boolean useDefaultIdentities)
throws IOException {
if (connector == null) {
@@ -565,8 +569,10 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa

ConnectFuture connectFuture = new DefaultConnectFuture(username + "@" + targetAddress, null);
SshFutureListener<IoConnectFuture> listener =
- createConnectCompletionListener(connectFuture, username, targetAddress, identities, useDefaultIdentities);
- connector.connect(targetAddress, localAddress).addListener(listener);
+ createConnectCompletionListener(
+ connectFuture, username, targetAddress, identities, useDefaultIdentities);
+ IoConnectFuture connectingFuture = connector.connect(targetAddress, context, localAddress);
+ connectingFuture.addListener(listener);
return connectFuture;
}

@@ -586,7 +592,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
if (t != null) {
if (log.isDebugEnabled()) {
log.debug("operationComplete({}@{}) failed ({}): {}",
- username, address, t.getClass().getSimpleName(), t.getMessage());
+ username, address, t.getClass().getSimpleName(), t.getMessage());
}
connectFuture.setException(t);
} else {
@@ -595,7 +601,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
onConnectOperationComplete(ioSession, connectFuture, username, address, identities, useDefaultIdentities);
} catch (RuntimeException e) {
log.warn("operationComplete({}@{}) failed ({}) to signal completion of session={}: {}",
- username, address, e.getClass().getSimpleName(), ioSession, e.getMessage());
+ username, address, e.getClass().getSimpleName(), ioSession, e.getMessage());
if (log.isDebugEnabled()) {
log.debug("operationComplete(" + username + "@" + address + ") session=" + ioSession + " completion signal failure details", e);
}
@@ -613,8 +619,9 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
};
}

- protected void onConnectOperationComplete(IoSession ioSession, ConnectFuture connectFuture,
- String username, SocketAddress address, Collection<? extends KeyPair> identities, boolean useDefaultIdentities) {
+ protected void onConnectOperationComplete(
+ IoSession ioSession, ConnectFuture connectFuture, String username,
+ SocketAddress address, Collection<? extends KeyPair> identities, boolean useDefaultIdentities) {
AbstractClientSession session = (AbstractClientSession) AbstractSession.getSession(ioSession);
session.setUsername(username);
session.setConnectAddress(address);
@@ -633,7 +640,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
for (KeyPair kp : identities) {
if (traceEnabled) {
log.trace("onConnectOperationComplete({}) add identity type={}, fingerprint={}",
- session, KeyUtils.getKeyType(kp), KeyUtils.getFingerPrint(kp.getPublic()));
+ session, KeyUtils.getKeyType(kp), KeyUtils.getFingerPrint(kp.getPublic()));
}
session.addPublicKeyIdentity(kp);
}
@@ -676,14 +683,14 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa
if (id instanceof String) {
if (traceEnabled) {
log.trace("setupDefaultSessionIdentities({}) add password fingerprint={}",
- session, KeyUtils.getFingerPrint(id.toString()));
+ session, KeyUtils.getFingerPrint(id.toString()));
}
session.addPasswordIdentity((String) id);
} else if (id instanceof KeyPair) {
KeyPair kp = (KeyPair) id;
if (traceEnabled) {
log.trace("setupDefaultSessionIdentities({}) add identity type={}, fingerprint={}",
- session, KeyUtils.getKeyType(kp), KeyUtils.getFingerPrint(kp.getPublic()));
+ session, KeyUtils.getKeyType(kp), KeyUtils.getFingerPrint(kp.getPublic()));
}
session.addPublicKeyIdentity(kp);
} else {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java b/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
index 2ee30e8..3fac01c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
@@ -40,6 +40,7 @@ import org.apache.sshd.client.channel.ChannelShell;
import org.apache.sshd.client.channel.ChannelSubsystem;
import org.apache.sshd.client.channel.ClientChannel;
import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
+import org.apache.sshd.common.AttributeRepository;
import org.apache.sshd.common.FactoryManager;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.NamedResource;
@@ -74,6 +75,8 @@ import org.apache.sshd.common.util.net.SshdSocketAddress;
public abstract class AbstractClientSession extends AbstractSession implements ClientSession {
private final List<Object> identities = new CopyOnWriteArrayList<>();
private final AuthenticationIdentitiesProvider identitiesProvider;
+ private final AttributeRepository connectionContext;
+
private ServerKeyVerifier serverKeyVerifier;
private UserInteraction userInteraction;
private PasswordIdentityProvider passwordIdentityProvider;
@@ -84,6 +87,13 @@ public abstract class AbstractClientSession extends AbstractSession implements C
protected AbstractClientSession(ClientFactoryManager factoryManager, IoSession ioSession) {
super(false, factoryManager, ioSession);
identitiesProvider = AuthenticationIdentitiesProvider.wrapIdentities(identities);
+ this.connectionContext =
+ (AttributeRepository) ioSession.getAttribute(AttributeRepository.class);
+ }
+
+ @Override
+ public AttributeRepository getConnectionContext() {
+ return connectionContext;
}

@Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
index b016ae3..808586f 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSession.java
@@ -47,6 +47,7 @@ import org.apache.sshd.client.channel.ClientChannelEvent;
import org.apache.sshd.client.future.AuthFuture;
import org.apache.sshd.client.session.forward.DynamicPortForwardingTracker;
import org.apache.sshd.client.session.forward.ExplicitPortForwardingTracker;
+import org.apache.sshd.common.AttributeRepository;
import org.apache.sshd.common.forward.PortForwardingManager;
import org.apache.sshd.common.future.KeyExchangeFuture;
import org.apache.sshd.common.keyprovider.KeyIdentityProvider;
@@ -108,6 +109,12 @@ public interface ClientSession
SocketAddress getConnectAddress();

/**
+ * @return The &quot;context&quot; data provided when
+ * session connection was established - {@code null} if none.
+ */
+ AttributeRepository getConnectionContext();
+
+ /**
* Starts the authentication process.
* User identities will be tried until the server successfully authenticate the user.
* User identities must be provided before calling this method using

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionCreator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionCreator.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionCreator.java
index 4d0a7e8..cfbc570 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionCreator.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionCreator.java
@@ -23,6 +23,7 @@ import java.net.SocketAddress;

import org.apache.sshd.client.config.hosts.HostConfigEntry;
import org.apache.sshd.client.future.ConnectFuture;
+import org.apache.sshd.common.AttributeRepository;

/**
* @author <a href="mailto:***@mina.apache.org">Apache MINA SSHD Project</a>
@@ -40,7 +41,25 @@ public interface ClientSessionCreator {
* @see #connect(HostConfigEntry)
*/
default ConnectFuture connect(String username, String host, int port) throws IOException {
- return connect(username, host, port, null);
+ return connect(username, host, port, (AttributeRepository) null);
+ }
+
+ /**
+ * Resolves the <U>effective</U> {@link HostConfigEntry} and connects to it
+ *
+ * @param username The intended username
+ * @param host The target host name/address - never {@code null}/empty
+ * @param port The target port
+ * @param context An optional &quot;context&quot; to be attached to the established
+ * session if successfully connected
+ * @return A {@link ConnectFuture}
+ * @throws IOException If failed to resolve the effective target or
+ * connect to it
+ */
+ default ConnectFuture connect(
+ String username, String host, int port, AttributeRepository context)
+ throws IOException {
+ return connect(username, host, port, context, null);
}

/**
@@ -56,7 +75,29 @@ public interface ClientSessionCreator {
* connect to it
* @see #connect(HostConfigEntry)
*/
- ConnectFuture connect(String username, String host, int port, SocketAddress localAddress) throws IOException;
+ default ConnectFuture connect(
+ String username, String host, int port, SocketAddress localAddress)
+ throws IOException {
+ return connect(username, host, port, null, localAddress);
+ }
+
+ /**
+ * Resolves the <U>effective</U> {@link HostConfigEntry} and connects to it
+ *
+ * @param username The intended username
+ * @param host The target host name/address - never {@code null}/empty
+ * @param port The target port
+ * @param context An optional &quot;context&quot; to be attached to the established
+ * session if successfully connected
+ * @param localAddress The local address to use - if {@code null} an
+ * automatic ephemeral port and bind address is used
+ * @return A {@link ConnectFuture}
+ * @throws IOException If failed to resolve the effective target or
+ * connect to it
+ */
+ ConnectFuture connect(
+ String username, String host, int port, AttributeRepository context, SocketAddress localAddress)
+ throws IOException;

/**
* Resolves the <U>effective</U> {@link HostConfigEntry} and connects to it
@@ -71,7 +112,26 @@ public interface ClientSessionCreator {
* @see #connect(HostConfigEntry)
*/
default ConnectFuture connect(String username, SocketAddress address) throws IOException {
- return connect(username, address, null);
+ return connect(username, address, (AttributeRepository) null);
+ }
+
+ /**
+ * Resolves the <U>effective</U> {@link HostConfigEntry} and connects to it
+ *
+ * @param username The intended username
+ * @param address The intended {@link SocketAddress} - never {@code null}. If
+ * this is an {@link java.net.InetSocketAddress} then the <U>effective</U> {@link HostConfigEntry}
+ * is resolved and used.
+ * @param context An optional &quot;context&quot; to be attached to the established
+ * session if successfully connected
+ * @return A {@link ConnectFuture}
+ * @throws IOException If failed to resolve the effective target or
+ * connect to it
+ */
+ default ConnectFuture connect(
+ String username, SocketAddress address, AttributeRepository context)
+ throws IOException {
+ return connect(username, address, context, null);
}

/**
@@ -88,7 +148,29 @@ public interface ClientSessionCreator {
* connect to it
* @see #connect(HostConfigEntry)
*/
- ConnectFuture connect(String username, SocketAddress targetAddress, SocketAddress localAddress) throws IOException;
+ default ConnectFuture connect(
+ String username, SocketAddress targetAddress, SocketAddress localAddress)
+ throws IOException {
+ return connect(username, targetAddress, null, localAddress);
+ }
+
+ /**
+ * Resolves the <U>effective</U> {@link HostConfigEntry} and connects to it
+ *
+ * @param username The intended username
+ * @param targetAddress The intended target {@link SocketAddress} - never {@code null}.
+ * If this is an {@link java.net.InetSocketAddress} then the <U>effective</U>
+ * {@link HostConfigEntry} is resolved and used.
+ * @param context An optional &quot;context&quot; to be attached to the established
+ * session if successfully connected
+ * @param localAddress The local address to use - if {@code null} an
+ * automatic ephemeral port and bind address is used
+ * @return A {@link ConnectFuture}
+ * @throws IOException If failed to resolve the effective target or connect to it
+ */
+ ConnectFuture connect(
+ String username, SocketAddress targetAddress, AttributeRepository context, SocketAddress localAddress)
+ throws IOException;

/**
* @param hostConfig The effective {@link HostConfigEntry} to connect to - never {@code null}
@@ -96,15 +178,41 @@ public interface ClientSessionCreator {
* @throws IOException If failed to create the connection future
*/
default ConnectFuture connect(HostConfigEntry hostConfig) throws IOException {
- return connect(hostConfig, null);
+ return connect(hostConfig, (AttributeRepository) null);
+ }
+
+ /**
+ * @param hostConfig The effective {@link HostConfigEntry} to connect to - never {@code null}
+ * @param context An optional &quot;context&quot; to be attached to the established
+ * session if successfully connected
+ * @return A {@link ConnectFuture}
+ * @throws IOException If failed to create the connection future
+ */
+ default ConnectFuture connect(HostConfigEntry hostConfig, AttributeRepository context) throws IOException {
+ return connect(hostConfig, context, null);
+ }
+
+ /**
+ * @param hostConfig The effective {@link HostConfigEntry} to connect to - never {@code null}
+ * @param localAddress The local address to use - if {@code null} an
+ * automatic ephemeral port and bind address is used
+ * @return A {@link ConnectFuture}
+ * @throws IOException If failed to create the connection future
+ */
+ default ConnectFuture connect(HostConfigEntry hostConfig, SocketAddress localAddress) throws IOException {
+ return connect(hostConfig, null, localAddress);
}

/**
* @param hostConfig The effective {@link HostConfigEntry} to connect to - never {@code null}
+ * @param context An optional &quot;context&quot; to be attached to the established
+ * session if successfully connected
* @param localAddress The local address to use - if {@code null} an
* automatic ephemeral port and bind address is used
* @return A {@link ConnectFuture}
* @throws IOException If failed to create the connection future
*/
- ConnectFuture connect(HostConfigEntry hostConfig, SocketAddress localAddress) throws IOException;
+ ConnectFuture connect(
+ HostConfigEntry hostConfig, AttributeRepository context, SocketAddress localAddress)
+ throws IOException;
}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-core/src/main/java/org/apache/sshd/client/simple/AbstractSimpleClientSessionCreator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/simple/AbstractSimpleClientSessionCreator.java b/sshd-core/src/main/java/org/apache/sshd/client/simple/AbstractSimpleClientSessionCreator.java
index 3dd904f..288a693 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/simple/AbstractSimpleClientSessionCreator.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/simple/AbstractSimpleClientSessionCreator.java
@@ -30,6 +30,7 @@ import org.apache.sshd.client.future.AuthFuture;
import org.apache.sshd.client.future.ConnectFuture;
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.client.session.ClientSessionCreator;
+import org.apache.sshd.common.AttributeRepository;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.ValidateUtils;

@@ -136,6 +137,7 @@ public abstract class AbstractSimpleClientSessionCreator extends AbstractSimpleC
* @return The {@link SimpleClient} wrapper. <B>Note:</B> closing the wrapper
* also closes the underlying sessions creator.
*/
+ @SuppressWarnings("checkstyle:anoninnerlength")
public static SimpleClient wrap(ClientSessionCreator creator, Channel channel) {
Objects.requireNonNull(creator, "No sessions creator");
Objects.requireNonNull(channel, "No channel");
@@ -173,6 +175,44 @@ public abstract class AbstractSimpleClientSessionCreator extends AbstractSimpleC
}

@Override
+ public ConnectFuture connect(
+ HostConfigEntry hostConfig, AttributeRepository context, SocketAddress localAddress)
+ throws IOException {
+ return creator.connect(hostConfig, context, localAddress);
+ }
+
+ @Override
+ public ConnectFuture connect(
+ String username, SocketAddress targetAddress, AttributeRepository context, SocketAddress localAddress)
+ throws IOException {
+ return creator.connect(username, targetAddress, context, localAddress);
+ }
+
+ @Override
+ public ConnectFuture connect(
+ String username, String host, int port, AttributeRepository context, SocketAddress localAddress)
+ throws IOException {
+ return creator.connect(username, host, port, context, localAddress);
+ }
+
+ @Override
+ public ConnectFuture connect(HostConfigEntry hostConfig, AttributeRepository context) throws IOException {
+ return creator.connect(hostConfig, context);
+ }
+
+ @Override
+ public ConnectFuture connect(String username, SocketAddress address, AttributeRepository context)
+ throws IOException {
+ return creator.connect(username, address, context);
+ }
+
+ @Override
+ public ConnectFuture connect(String username, String host, int port, AttributeRepository context)
+ throws IOException {
+ return creator.connect(username, host, port, context);
+ }
+
+ @Override
public boolean isOpen() {
return channel.isOpen();
}
@@ -181,6 +221,11 @@ public abstract class AbstractSimpleClientSessionCreator extends AbstractSimpleC
public void close() throws IOException {
channel.close();
}
+
+ @Override
+ public String toString() {
+ return SimpleClient.class.getSimpleName() + "[" + channel + "]";
+ }
};
}
}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-core/src/main/java/org/apache/sshd/client/simple/SimpleClientConfigurator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/simple/SimpleClientConfigurator.java b/sshd-core/src/main/java/org/apache/sshd/client/simple/SimpleClientConfigurator.java
index 31a994b..e8038e1 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/simple/SimpleClientConfigurator.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/simple/SimpleClientConfigurator.java
@@ -19,7 +19,7 @@

package org.apache.sshd.client.simple;

-import org.apache.sshd.common.config.ConfigFileReaderSupport;
+import org.apache.sshd.common.SshConstants;

/**
* @author <a href="mailto:***@mina.apache.org">Apache MINA SSHD Project</a>
@@ -35,7 +35,7 @@ public interface SimpleClientConfigurator {
*/
long DEFAULT_AUTHENTICATION_TIMEOUT = Long.MAX_VALUE; // virtually infinite

- int DEFAULT_PORT = ConfigFileReaderSupport.DEFAULT_PORT;
+ int DEFAULT_PORT = SshConstants.DEFAULT_PORT;

/**
* @return Current connect timeout (msec.) - always positive

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-core/src/main/java/org/apache/sshd/common/io/IoAcceptor.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/io/IoAcceptor.java b/sshd-core/src/main/java/org/apache/sshd/common/io/IoAcceptor.java
index a86b73e..2a0e80d 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/io/IoAcceptor.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/io/IoAcceptor.java
@@ -40,5 +40,4 @@ public interface IoAcceptor extends IoService {
void unbind();

Set<SocketAddress> getBoundAddresses();
-
}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-core/src/main/java/org/apache/sshd/common/io/IoConnector.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/io/IoConnector.java b/sshd-core/src/main/java/org/apache/sshd/common/io/IoConnector.java
index c546d44..193a209 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/io/IoConnector.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/io/IoConnector.java
@@ -20,16 +20,19 @@ package org.apache.sshd.common.io;

import java.net.SocketAddress;

+import org.apache.sshd.common.AttributeRepository;
+
/**
* @author <a href="mailto:***@mina.apache.org">Apache MINA SSHD Project</a>
*/
public interface IoConnector extends IoService {
-
/**
* @param targetAddress The target address to connect to
+ * @param context An optional &quot;context&quot; to be attached to the established
+ * session if successfully connected
* @param localAddress The local address to use - if {@code null} an
* automatic ephemeral port and bind address is used
* @return The {@link IoConnectFuture future} representing the connection request
*/
- IoConnectFuture connect(SocketAddress targetAddress, SocketAddress localAddress);
+ IoConnectFuture connect(SocketAddress targetAddress, AttributeRepository context, SocketAddress localAddress);
}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-core/src/main/java/org/apache/sshd/common/io/IoServiceEventListener.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/io/IoServiceEventListener.java b/sshd-core/src/main/java/org/apache/sshd/common/io/IoServiceEventListener.java
index 53d6611..6bb51d2 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/io/IoServiceEventListener.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/io/IoServiceEventListener.java
@@ -22,6 +22,7 @@ package org.apache.sshd.common.io;
import java.io.IOException;
import java.net.SocketAddress;

+import org.apache.sshd.common.AttributeRepository;
import org.apache.sshd.common.util.SshdEventListener;

/**
@@ -34,10 +35,14 @@ public interface IoServiceEventListener extends SshdEventListener {
*
* @param connector The {@link IoConnector} through which the connection was established
* @param local The local connection endpoint
+ * @param context An optional &quot;context&quot; provided by the user when connection
+ * was requested
* @param remote The remote connection endpoint
* @throws IOException If failed to handle the event - in which case connection will be aborted
*/
- default void connectionEstablished(IoConnector connector, SocketAddress local, SocketAddress remote) throws IOException {
+ default void connectionEstablished(
+ IoConnector connector, SocketAddress local, AttributeRepository context, SocketAddress remote)
+ throws IOException {
// Do nothing
}

@@ -47,14 +52,16 @@ public interface IoServiceEventListener extends SshdEventListener {
*
* @param connector The {@link IoConnector} through which the connection was established
* @param local The local connection endpoint
+ * @param context An optional &quot;context&quot; provided by the user when connection
+ * was requested
* @param remote The remote connection endpoint
* @param reason The reason for aborting - may be an exception thrown by
- * {@link #connectionEstablished(IoConnector, SocketAddress, SocketAddress) connectionEstablished}
+ * {@link #connectionEstablished(IoConnector, SocketAddress, AttributeRepository, SocketAddress) connectionEstablished}
* @throws IOException If failed to handle the event - the exception is logged but does not
* prevent further connections from being accepted
*/
default void abortEstablishedConnection(
- IoConnector connector, SocketAddress local, SocketAddress remote, Throwable reason)
+ IoConnector connector, SocketAddress local, AttributeRepository context, SocketAddress remote, Throwable reason)
throws IOException {
// Do nothing
}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Connector.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Connector.java b/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Connector.java
index e8410e7..61eac91 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Connector.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Connector.java
@@ -23,6 +23,7 @@ import java.net.SocketAddress;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousSocketChannel;

+import org.apache.sshd.common.AttributeRepository;
import org.apache.sshd.common.FactoryManager;
import org.apache.sshd.common.future.DefaultSshFuture;
import org.apache.sshd.common.io.IoConnectFuture;
@@ -44,7 +45,7 @@ public class Nio2Connector extends Nio2Service implements IoConnector {
}

@Override
- public IoConnectFuture connect(SocketAddress address, SocketAddress localAddress) {
+ public IoConnectFuture connect(SocketAddress address, AttributeRepository context, SocketAddress localAddress) {
boolean debugEnabled = log.isDebugEnabled();
if (debugEnabled) {
log.debug("Connecting to {}", address);
@@ -62,7 +63,8 @@ public class Nio2Connector extends Nio2Service implements IoConnector {
}
Nio2CompletionHandler<Void, Object> completionHandler =
ValidateUtils.checkNotNull(
- createConnectionCompletionHandler(future, socket, getFactoryManager(), getIoHandler()),
+ createConnectionCompletionHandler(
+ future, socket, context, getFactoryManager(), getIoHandler()),
"No connection completion handler created for %s",
address);
socket.connect(address, null, completionHandler);
@@ -111,20 +113,24 @@ public class Nio2Connector extends Nio2Service implements IoConnector {
}

protected Nio2CompletionHandler<Void, Object> createConnectionCompletionHandler(
- IoConnectFuture future, AsynchronousSocketChannel socket, FactoryManager manager, IoHandler handler) {
- return new ConnectionCompletionHandler(future, socket, manager, handler);
+ IoConnectFuture future, AsynchronousSocketChannel socket,
+ AttributeRepository context, FactoryManager manager, IoHandler handler) {
+ return new ConnectionCompletionHandler(future, socket, context, manager, handler);
}

protected class ConnectionCompletionHandler extends Nio2CompletionHandler<Void, Object> {
protected final IoConnectFuture future;
protected final AsynchronousSocketChannel socket;
+ protected final AttributeRepository context;
protected final FactoryManager manager;
protected final IoHandler handler;

protected ConnectionCompletionHandler(
- IoConnectFuture future, AsynchronousSocketChannel socket, FactoryManager manager, IoHandler handler) {
+ IoConnectFuture future, AsynchronousSocketChannel socket,
+ AttributeRepository context, FactoryManager manager, IoHandler handler) {
this.future = future;
this.socket = socket;
+ this.context = context;
this.manager = manager;
this.handler = handler;
}
@@ -138,10 +144,14 @@ public class Nio2Connector extends Nio2Service implements IoConnector {
if (listener != null) {
SocketAddress local = socket.getLocalAddress();
SocketAddress remote = socket.getRemoteAddress();
- listener.connectionEstablished(Nio2Connector.this, local, remote);
+ listener.connectionEstablished(Nio2Connector.this, local, context, remote);
}

Nio2Session session = createSession(manager, handler, socket);
+ if (context != null) {
+ session.setAttribute(AttributeRepository.class, context);
+ }
+
handler.sessionCreated(session);
sessionId = session.getId();
sessions.put(sessionId, session);
@@ -162,7 +172,8 @@ public class Nio2Connector extends Nio2Service implements IoConnector {
try {
SocketAddress localAddress = socket.getLocalAddress();
SocketAddress remoteAddress = socket.getRemoteAddress();
- listener.abortEstablishedConnection(Nio2Connector.this, localAddress, remoteAddress, t);
+ listener.abortEstablishedConnection(
+ Nio2Connector.this, localAddress, context, remoteAddress, t);
} catch (Exception e) {
if (debugEnabled) {
log.debug("onCompleted() listener=" + listener + " ignoring abort event exception", e);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/SessionHelper.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/SessionHelper.java b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/SessionHelper.java
index c02c039..25cbf2f 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/SessionHelper.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/SessionHelper.java
@@ -82,6 +82,7 @@ public abstract class SessionHelper extends AbstractKexFactoryManager implements
* The underlying network session
*/
private final IoSession ioSession;
+
/**
* The session specific properties
*/

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-core/src/main/java/org/apache/sshd/server/forward/TcpipServerChannel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/forward/TcpipServerChannel.java b/sshd-core/src/main/java/org/apache/sshd/server/forward/TcpipServerChannel.java
index 1007494..ea0e365 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/forward/TcpipServerChannel.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/forward/TcpipServerChannel.java
@@ -236,7 +236,7 @@ public class TcpipServerChannel extends AbstractServerChannel implements Forward

IoServiceFactory ioServiceFactory = manager.getIoServiceFactory();
connector = ioServiceFactory.createConnector(handler);
- IoConnectFuture future = connector.connect(address.toInetSocketAddress(), getLocalAddress());
+ IoConnectFuture future = connector.connect(address.toInetSocketAddress(), null, getLocalAddress());
future.addListener(future1 -> handleChannelConnectResult(f, future1));
return f;
}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-core/src/test/java/org/apache/sshd/client/session/ClientSessionTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/session/ClientSessionTest.java b/sshd-core/src/test/java/org/apache/sshd/client/session/ClientSessionTest.java
index 12c416d..520c862 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/session/ClientSessionTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/session/ClientSessionTest.java
@@ -24,8 +24,13 @@ import java.nio.charset.StandardCharsets;
import java.rmi.RemoteException;
import java.rmi.ServerException;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;

import org.apache.sshd.client.SshClient;
+import org.apache.sshd.common.AttributeRepository;
+import org.apache.sshd.common.AttributeRepository.AttributeKey;
+import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.session.SessionListener;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.util.test.BaseTestSupport;
import org.apache.sshd.util.test.CommandExecutionHelper;
@@ -41,6 +46,9 @@ import org.junit.runners.MethodSorters;
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class ClientSessionTest extends BaseTestSupport {
+ private static final long CONNECT_TIMEOUT = 7L;
+ private static final long AUTH_TIMEOUT = 5L;
+
private static SshServer sshd;
private static SshClient client;
private static int port;
@@ -97,9 +105,11 @@ public class ClientSessionTest extends BaseTestSupport {
}
});

- try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+ try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
+ .verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
+ .getSession()) {
session.addPasswordIdentity(getCurrentTestName());
- session.auth().verify(5L, TimeUnit.SECONDS);
+ session.auth().verify(AUTH_TIMEOUT, TimeUnit.SECONDS);

// NOTE !!! The LF is only because we are using a buffered reader on the server end to read the command
String actualResponse = session.executeRemoteCommand(expectedCommand + "\n");
@@ -127,9 +137,11 @@ public class ClientSessionTest extends BaseTestSupport {
});

String actualErrorMessage = null;
- try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+ try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
+ .verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
+ .getSession()) {
session.addPasswordIdentity(getCurrentTestName());
- session.auth().verify(5L, TimeUnit.SECONDS);
+ session.auth().verify(AUTH_TIMEOUT, TimeUnit.SECONDS);

// NOTE !!! The LF is only because we are using a buffered reader on the server end to read the command
String response = session.executeRemoteCommand(expectedCommand + "\n");
@@ -175,9 +187,11 @@ public class ClientSessionTest extends BaseTestSupport {
});

String actualErrorMessage = null;
- try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
+ try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
+ .verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
+ .getSession()) {
session.addPasswordIdentity(getCurrentTestName());
- session.auth().verify(5L, TimeUnit.SECONDS);
+ session.auth().verify(AUTH_TIMEOUT, TimeUnit.SECONDS);

// NOTE !!! The LF is only because we are using a buffered reader on the server end to read the command
String response = session.executeRemoteCommand(expectedCommand + "\n");
@@ -197,4 +211,33 @@ public class ClientSessionTest extends BaseTestSupport {

assertEquals("Mismatched captured error code", Integer.toString(expectedErrorCode), actualErrorMessage);
}
+
+ @Test // see SSHD-859
+ public void testConnectionContextPropagation() throws Exception {
+ AttributeRepository expected = AttributeRepository.ofKeyValuePair(
+ new AttributeKey<String>(), getCurrentTestName());
+ AtomicInteger creationCount = new AtomicInteger(0);
+ SessionListener listener = new SessionListener() {
+ @Override
+ public void sessionCreated(Session session) {
+ AttributeRepository actual = ((ClientSession) session).getConnectionContext();
+ assertSame("Mismatched connection context", expected, actual);
+ creationCount.incrementAndGet();
+ }
+ };
+
+ try {
+ client.addSessionListener(listener);
+
+ try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port, expected)
+ .verify(CONNECT_TIMEOUT, TimeUnit.SECONDS)
+ .getSession()) {
+ session.addPasswordIdentity(getCurrentTestName());
+ session.auth().verify(AUTH_TIMEOUT, TimeUnit.SECONDS);
+ assertEquals("Session listener invocation count mismatch", 1, creationCount.getAndSet(0));
+ }
+ } finally {
+ client.removeSessionListener(listener);
+ }
+ }
}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-mina/src/main/java/org/apache/sshd/common/io/mina/MinaConnector.java
----------------------------------------------------------------------
diff --git a/sshd-mina/src/main/java/org/apache/sshd/common/io/mina/MinaConnector.java b/sshd-mina/src/main/java/org/apache/sshd/common/io/mina/MinaConnector.java
index e7229ec..deeb371 100644
--- a/sshd-mina/src/main/java/org/apache/sshd/common/io/mina/MinaConnector.java
+++ b/sshd-mina/src/main/java/org/apache/sshd/common/io/mina/MinaConnector.java
@@ -29,6 +29,7 @@ import org.apache.mina.core.service.IoProcessor;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.transport.socket.nio.NioSession;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
+import org.apache.sshd.common.AttributeRepository;
import org.apache.sshd.common.FactoryManager;
import org.apache.sshd.common.future.DefaultSshFuture;
import org.apache.sshd.common.io.IoConnectFuture;
@@ -79,10 +80,12 @@ public class MinaConnector extends MinaService implements org.apache.sshd.common
IoServiceEventListener listener = getIoServiceEventListener();
SocketAddress local = session.getLocalAddress();
SocketAddress remote = session.getRemoteAddress();
+ AttributeRepository context =
+ (AttributeRepository) session.getAttribute(AttributeRepository.class);
try {
if (listener != null) {
try {
- listener.connectionEstablished(this, local, remote);
+ listener.connectionEstablished(this, local, context, remote);
} catch (Exception e) {
session.closeNow();
throw e;
@@ -93,7 +96,7 @@ public class MinaConnector extends MinaService implements org.apache.sshd.common
} catch (Exception e) {
if (listener != null) {
try {
- listener.abortEstablishedConnection(this, local, remote, e);
+ listener.abortEstablishedConnection(this, local, context, remote, e);
} catch (Exception exc) {
if (log.isDebugEnabled()) {
log.debug("sessionCreated(" + session + ") listener=" + listener + " ignoring abort event exception", exc);
@@ -106,7 +109,7 @@ public class MinaConnector extends MinaService implements org.apache.sshd.common
}

@Override
- public IoConnectFuture connect(SocketAddress address, SocketAddress localAddress) {
+ public IoConnectFuture connect(SocketAddress address, AttributeRepository context, SocketAddress localAddress) {
class Future extends DefaultSshFuture<IoConnectFuture> implements IoConnectFuture {
Future(Object lock) {
super(address, lock);
@@ -131,6 +134,10 @@ public class MinaConnector extends MinaService implements org.apache.sshd.common

@Override
public void setSession(org.apache.sshd.common.io.IoSession session) {
+ if (context != null) {
+ session.setAttribute(AttributeRepository.class, context);
+ }
+
setValue(session);
}

@@ -142,7 +149,13 @@ public class MinaConnector extends MinaService implements org.apache.sshd.common

IoConnectFuture future = new Future(null);
IoConnector connector = getConnector();
- ConnectFuture connectFuture = connector.connect(address, localAddress);
+ ConnectFuture connectFuture = connector.connect(
+ address, localAddress,
+ (s, f) -> {
+ if (context != null) {
+ s.setAttribute(AttributeRepository.class, context);
+ }
+ });
connectFuture.addListener((IoFutureListener<ConnectFuture>) cf -> {
Throwable t = cf.getException();
if (t != null) {
@@ -152,6 +165,9 @@ public class MinaConnector extends MinaService implements org.apache.sshd.common
} else {
IoSession ioSession = cf.getSession();
org.apache.sshd.common.io.IoSession sshSession = getSession(ioSession);
+ if (context != null) {
+ sshSession.setAttribute(AttributeRepository.class, context);
+ }
future.setSession(sshSession);
}
});

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoConnector.java
----------------------------------------------------------------------
diff --git a/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoConnector.java b/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoConnector.java
index 9230803..1c7bdf9 100644
--- a/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoConnector.java
+++ b/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoConnector.java
@@ -21,6 +21,7 @@ package org.apache.sshd.netty;

import java.net.SocketAddress;

+import org.apache.sshd.common.AttributeRepository;
import org.apache.sshd.common.future.DefaultSshFuture;
import org.apache.sshd.common.io.IoConnectFuture;
import org.apache.sshd.common.io.IoConnector;
@@ -63,10 +64,13 @@ public class NettyIoConnector extends NettyIoService implements IoConnector {
IoServiceEventListener listener = getIoServiceEventListener();
SocketAddress local = ch.localAddress();
SocketAddress remote = ch.remoteAddress();
+ AttributeRepository context = ch.hasAttr(CONTEXT_KEY)
+ ? ch.attr(CONTEXT_KEY).get()
+ : null;
try {
if (listener != null) {
try {
- listener.connectionEstablished(NettyIoConnector.this, local, remote);
+ listener.connectionEstablished(NettyIoConnector.this, local, context, remote);
} catch (Exception e) {
ch.close();
throw e;
@@ -75,13 +79,17 @@ public class NettyIoConnector extends NettyIoService implements IoConnector {

@SuppressWarnings("resource")
NettyIoSession session = new NettyIoSession(NettyIoConnector.this, handler);
+ if (context != null) {
+ session.setAttribute(AttributeRepository.class, context);
+ }
+
ChannelPipeline p = ch.pipeline();
p.addLast(new LoggingHandler(LogLevel.INFO)); // TODO make this configurable
p.addLast(session.adapter);
} catch (Exception e) {
if (listener != null) {
try {
- listener.abortEstablishedConnection(NettyIoConnector.this, local, remote, e);
+ listener.abortEstablishedConnection(NettyIoConnector.this, local, context, remote, e);
} catch (Exception exc) {
if (log.isDebugEnabled()) {
log.debug("initChannel(" + ch + ") listener=" + listener + " ignoring abort event exception", exc);
@@ -96,7 +104,7 @@ public class NettyIoConnector extends NettyIoService implements IoConnector {
}

@Override
- public IoConnectFuture connect(SocketAddress address, SocketAddress localAddress) {
+ public IoConnectFuture connect(SocketAddress address, AttributeRepository context, SocketAddress localAddress) {
boolean debugEnabled = log.isDebugEnabled();
if (debugEnabled) {
log.debug("Connecting to {}", address);
@@ -111,6 +119,10 @@ public class NettyIoConnector extends NettyIoService implements IoConnector {
}
Channel channel = chf.channel();
channel.attr(CONNECT_FUTURE_KEY).set(future);
+ if (context != null) {
+ channel.attr(CONTEXT_KEY).set(context);
+ }
+
chf.addListener(cf -> {
Throwable t = chf.cause();
if (t != null) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoService.java
----------------------------------------------------------------------
diff --git a/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoService.java b/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoService.java
index ea2ca8f..4e36da7 100644
--- a/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoService.java
+++ b/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoService.java
@@ -24,6 +24,7 @@ import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

+import org.apache.sshd.common.AttributeRepository;
import org.apache.sshd.common.io.IoConnectFuture;
import org.apache.sshd.common.io.IoHandler;
import org.apache.sshd.common.io.IoService;
@@ -42,6 +43,8 @@ public abstract class NettyIoService extends AbstractCloseable implements IoServ

public static final AttributeKey<IoConnectFuture> CONNECT_FUTURE_KEY =
AttributeKey.valueOf(IoConnectFuture.class.getName());
+ public static final AttributeKey<AttributeRepository> CONTEXT_KEY =
+ AttributeKey.valueOf(AttributeRepository.class.getName());

protected final AtomicLong sessionSeq = new AtomicLong();
protected final Map<Long, IoSession> sessions = new ConcurrentHashMap<>();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java
index d4db7c7..3c171d0 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemProvider.java
@@ -73,8 +73,8 @@ import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.client.subsystem.sftp.SftpClient.Attributes;
import org.apache.sshd.common.PropertyResolver;
import org.apache.sshd.common.PropertyResolverUtils;
+import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.SshException;
-import org.apache.sshd.common.config.ConfigFileReaderSupport;
import org.apache.sshd.common.io.IoSession;
import org.apache.sshd.common.subsystem.sftp.SftpConstants;
import org.apache.sshd.common.subsystem.sftp.SftpException;
@@ -181,7 +181,7 @@ public class SftpFileSystemProvider extends FileSystemProvider {
String host = ValidateUtils.checkNotNullAndNotEmpty(uri.getHost(), "Host not provided");
int port = uri.getPort();
if (port <= 0) {
- port = ConfigFileReaderSupport.DEFAULT_PORT;
+ port = SshConstants.DEFAULT_PORT;
}

String userInfo = ValidateUtils.checkNotNullAndNotEmpty(uri.getUserInfo(), "UserInfo not provided");
@@ -1220,13 +1220,13 @@ public class SftpFileSystemProvider extends FileSystemProvider {
InetSocketAddress inetAddr = (InetSocketAddress) addr;
return getFileSystemIdentifier(inetAddr.getHostString(), inetAddr.getPort(), username);
} else {
- return getFileSystemIdentifier(addr.toString(), ConfigFileReaderSupport.DEFAULT_PORT, username);
+ return getFileSystemIdentifier(addr.toString(), SshConstants.DEFAULT_PORT, username);
}
}

public static String getFileSystemIdentifier(String host, int port, String username) {
return GenericUtils.trimToEmpty(host) + ':'
- + ((port <= 0) ? ConfigFileReaderSupport.DEFAULT_PORT : port) + ':'
+ + ((port <= 0) ? SshConstants.DEFAULT_PORT : port) + ':'
+ GenericUtils.trimToEmpty(username);
}


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-spring-sftp/src/main/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactory.java
----------------------------------------------------------------------
diff --git a/sshd-spring-sftp/src/main/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactory.java b/sshd-spring-sftp/src/main/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactory.java
index 7dc2eb0..dfa5855 100644
--- a/sshd-spring-sftp/src/main/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactory.java
+++ b/sshd-spring-sftp/src/main/java/org/apache/sshd/spring/integration/sftp/ApacheSshdSftpSessionFactory.java
@@ -37,7 +37,7 @@ import org.apache.sshd.client.subsystem.sftp.SftpClient.DirEntry;
import org.apache.sshd.client.subsystem.sftp.SftpClientFactory;
import org.apache.sshd.client.subsystem.sftp.SftpVersionSelector;
import org.apache.sshd.common.PropertyResolverUtils;
-import org.apache.sshd.common.config.ConfigFileReaderSupport;
+import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.config.keys.FilePasswordProvider;
import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.config.keys.loader.pem.PEMResourceParserUtils;
@@ -72,7 +72,7 @@ public class ApacheSshdSftpSessionFactory
private final AtomicReference<ClientSession> sharedSessionHolder = new AtomicReference<>();

private volatile String hostValue;
- private volatile int portValue = ConfigFileReaderSupport.DEFAULT_PORT;
+ private volatile int portValue = SshConstants.DEFAULT_PORT;
private volatile String userValue;
private volatile String passwordValue;
private volatile Resource privateKey;
l***@apache.org
2018-10-31 05:03:10 UTC
Permalink
[SSHD-757] Added initial support for reading PGP private key files


Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/259ee8c7
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/259ee8c7
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/259ee8c7

Branch: refs/heads/master
Commit: 259ee8c7fffd7355d08905d3bb69ab4ed31a6e0a
Parents: 403402f
Author: Lyor Goldstein <***@gmail.com>
Authored: Fri Sep 7 18:28:49 2018 +0300
Committer: Lyor Goldstein <***@apache.org>
Committed: Wed Oct 31 07:02:56 2018 +0200

----------------------------------------------------------------------
README.md | 38 ++
assembly/pom.xml | 5 +
assembly/src/main/components/modules.xml | 6 +-
assembly/src/main/legal/licenses/jpgpj.txt | 21 +
assembly/src/main/legal/notices.xml | 17 +
pom.xml | 1 +
.../common/config/keys/KeyEntryResolver.java | 15 +-
.../loader/AbstractKeyPairResourceParser.java | 5 +-
.../keys/loader/KeyPairResourceLoader.java | 35 +-
.../keys/loader/KeyPairResourceParser.java | 21 +-
.../openssh/OpenSSHKeyPairResourceParser.java | 28 +-
.../pem/AbstractPEMResourceKeyPairParser.java | 7 +-
.../loader/pem/DSSPEMResourceKeyPairParser.java | 2 +-
.../pem/ECDSAPEMResourceKeyPairParser.java | 4 +-
.../pem/PKCS8PEMResourceKeyPairParser.java | 6 +-
.../loader/pem/RSAPEMResourceKeyPairParser.java | 2 +-
.../BouncyCastleKeyPairResourceParser.java | 7 +-
sshd-openpgp/pom.xml | 104 +++++
.../openpgp/PGPKeyPairResourceParser.java | 445 +++++++++++++++++++
sshd-openpgp/src/main/resources/.gitignore | 0
.../openpgp/PGPKeyPairResourceParserTest.java | 186 ++++++++
.../src/test/resources/log4j.properties | 38 ++
.../loader/openpgp/DSA-2048-gpg4win-3.1.3.asc | 25 ++
.../keys/loader/openpgp/EC-348-v1p0-public.asc | 27 ++
.../loader/openpgp/RSA-2048-v1p0-public.asc | 56 +++
.../loader/openpgp/RSA-2048-v1p6p1-public.asc | 18 +
.../loader/openpgp/RSA-4096-vp2p0p8-public.asc | 76 ++++
.../loader/openpgp/ed25519-gpg4win-3.1.3.asc | 9 +
...secret-passphrase-DSA-2048-gpg4win-3.1.3.gpg | 27 ++
...er-secret-passphrase-EC-384-v1p0-private.gpg | 32 ++
...-secret-passphrase-RSA-2048-v1p0-private.gpg | 99 +++++
...ecret-passphrase-RSA-2048-v1p6p1-private.gpg | 32 ++
...ecret-passphrase-RSA-4096-v2p0p8-private.gpg | 133 ++++++
...-secret-passphrase-ed25519-gpg4win-3.1.3.gpg | 11 +
34 files changed, 1482 insertions(+), 56 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index e739438..c5edc53 100644
--- a/README.md
+++ b/README.md
@@ -187,6 +187,8 @@ The code contains support for reading _ed25519_ [OpenSSH formatted private keys]

* *sshd-putty* - contains code that can parse [PUTTY](https://www.putty.org/) key files.

+* *sshd-openpgp* - contains code that can parse [OpenPGP](https://www.openpgp.org/) key files (with some limitations - see relevant section)
+
* *sshd-cli* - contains simple templates for command-line client/server - used to provide look-and-feel similar to the Linux *ssh/sshd* commands.

* *sshd-contrib* - **experimental** code that is currently under review and may find its way into one of the other artifacts
@@ -1849,6 +1851,42 @@ be included as an extra dependency:
</dependency>
```

+### [OpenPGP](https://www.openpgp.org/)
+
+The code contains the _sshd-openpgp_ module that enables using _OpenPGP_ private key files as identity providers.
+
+```xml
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-openpgp</artifactId>
+ <version>...same version as the rest of the artifacts...</version>
+ </dependency>
+```
+
+The [support](https://issues.apache.org/jira/browse/SSHD-757) for it is currently still in its infancy, and therefore
+this feature should be considered **experimental** for the time being. However, within its limitations it supports
+
+* RSA keys
+* DSA keys
+* ECDSA keys
+
+(*) For now `ed25519` keys are not supported by this module.
+
+The code reads **all** the available key pairs in the key file without any distinction between encryption, decryption,
+authentication or signature ones.
+
+This code relies on the [jpgpgj](https://github.com/justinludwig/jpgpj) support module
+
+```xml
+ <dependency>
+ <groupId>org.c02e.jpgpj</groupId>
+ <artifactId>jpgpj</artifactId>
+ <version>0.5</version>
+ </dependency>
+```
+
+(which in turn automatically uses _Bouncycastle_ - so if one does not want _Bouncycastle_ one cannot use this module).
+
## Useful extra components in _sshd-contrib_

* `InteractivePasswordIdentityProvider` - helps implement a `PasswordIdentityProvider` by delegating calls

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/assembly/pom.xml
----------------------------------------------------------------------
diff --git a/assembly/pom.xml b/assembly/pom.xml
index 9d65f2d..f38d050 100644
--- a/assembly/pom.xml
+++ b/assembly/pom.xml
@@ -100,6 +100,11 @@
</dependency>
<dependency>
<groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-openpgp</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
<artifactId>sshd-spring-sftp</artifactId>
<version>${project.version}</version>
</dependency>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/assembly/src/main/components/modules.xml
----------------------------------------------------------------------
diff --git a/assembly/src/main/components/modules.xml b/assembly/src/main/components/modules.xml
index 461155b..3b34d7b 100644
--- a/assembly/src/main/components/modules.xml
+++ b/assembly/src/main/components/modules.xml
@@ -24,6 +24,7 @@
<exclude>org.apache.sshd:sshd-ldap</exclude>
<exclude>org.apache.sshd:sshd-contrib</exclude>
<exclude>org.apache.sshd:sshd-putty</exclude>
+ <exclude>org.apache.sshd:sshd-openpgp</exclude>
<exclude>org.apache.sshd:sshd-spring-sftp</exclude>
<exclude>org.springframework:*</exclude>
<exclude>org.springframework.retry:*</exclude>
@@ -34,6 +35,7 @@
<exclude>org.eclipse.jgit:*</exclude>
<exclude>com.jcraft:*</exclude>
<exclude>com.googlecode.javaewah:JavaEWAH</exclude>
+ <exclude>org.c02e.jpgpj:jpgpj</exclude>
</excludes>
<outputDirectory>lib</outputDirectory>
<useProjectArtifact>false</useProjectArtifact>
@@ -46,6 +48,7 @@
<include>org.apache.sshd:sshd-contrib</include>
<include>org.apache.sshd:sshd-putty</include>
<include>org.apache.sshd:sshd-spring-sftp</include>
+ <include>org.apache.sshd:sshd-openpgp</include>
</includes>
<outputDirectory>extras</outputDirectory>
<useProjectArtifact>false</useProjectArtifact>
@@ -60,7 +63,8 @@
<include>org.reactivestreams:*</include>
<include>commons-io:commons-io</include>
<include>com.jcraft:*</include>
- <include>com.googlecode.javaewah:JavaEWAH</include>
+ <include>com.googlecode.javaewah:JavaEWAH</include>
+ <include>org.c02e.jpgpj:jpgpj</include>
</includes>
<outputDirectory>dependencies</outputDirectory>
<useProjectArtifact>false</useProjectArtifact>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/assembly/src/main/legal/licenses/jpgpj.txt
----------------------------------------------------------------------
diff --git a/assembly/src/main/legal/licenses/jpgpj.txt b/assembly/src/main/legal/licenses/jpgpj.txt
new file mode 100644
index 0000000..f47901d
--- /dev/null
+++ b/assembly/src/main/legal/licenses/jpgpj.txt
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2016 Justin Ludwig
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/assembly/src/main/legal/notices.xml
----------------------------------------------------------------------
diff --git a/assembly/src/main/legal/notices.xml b/assembly/src/main/legal/notices.xml
index e03833a..48235b3 100644
--- a/assembly/src/main/legal/notices.xml
+++ b/assembly/src/main/legal/notices.xml
@@ -171,4 +171,21 @@
</licenses>
</project>
</supplement>
+ <supplement>
+ <project>
+ <groupId>org.c02e.jpgpj</groupId>
+ <artifactId>jpgpj</artifactId>
+ <name>jpjpj</name>
+ <organization>
+ <name>jpgpj</name>
+ <url>https://github.com/justinludwig/jpgpj</url>
+ </organization>
+ <licenses>
+ <license>
+ <name>MIT License</name>
+ <url>https://raw.githubusercontent.com/justinludwig/jpgpj/master/LICENSE</url>
+ </license>
+ </licenses>
+ </project>
+ </supplement>
</supplementalDataModels>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index a67ae3b..5bb5762 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1324,6 +1324,7 @@
<modules>
<module>sshd-common</module>
<module>sshd-putty</module>
+ <module>sshd-openpgp</module>
<module>sshd-core</module>
<module>sshd-mina</module>
<module>sshd-netty</module>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyEntryResolver.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyEntryResolver.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyEntryResolver.java
index 2dd52d1..8b6dcc8 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyEntryResolver.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyEntryResolver.java
@@ -41,7 +41,8 @@ import org.apache.sshd.common.util.io.IoUtils;
* @param <PRV> Type of {@link PrivateKey}
* @author <a href="mailto:***@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public interface KeyEntryResolver<PUB extends PublicKey, PRV extends PrivateKey> extends IdentityResourceLoader<PUB, PRV> {
+public interface KeyEntryResolver<PUB extends PublicKey, PRV extends PrivateKey>
+ extends IdentityResourceLoader<PUB, PRV> {
/**
* @param keySize Key size in bits
* @return A {@link KeyPair} with the specified key size
@@ -72,10 +73,12 @@ public interface KeyEntryResolver<PUB extends PublicKey, PRV extends PrivateKey>
if (pubOriginal != null) {
Class<?> orgType = pubOriginal.getClass();
if (!pubExpected.isAssignableFrom(orgType)) {
- throw new InvalidKeyException("Mismatched public key types: expected=" + pubExpected.getSimpleName() + ", actual=" + orgType.getSimpleName());
+ throw new InvalidKeyException(
+ "Mismatched public key types: expected=" + pubExpected.getSimpleName() + ", actual=" + orgType.getSimpleName());
}

- pubCloned = clonePublicKey(pubExpected.cast(pubOriginal));
+ PUB castPub = pubExpected.cast(pubOriginal);
+ pubCloned = clonePublicKey(castPub);
}

PRV prvCloned = null;
@@ -84,10 +87,12 @@ public interface KeyEntryResolver<PUB extends PublicKey, PRV extends PrivateKey>
if (prvOriginal != null) {
Class<?> orgType = prvOriginal.getClass();
if (!prvExpected.isAssignableFrom(orgType)) {
- throw new InvalidKeyException("Mismatched private key types: expected=" + prvExpected.getSimpleName() + ", actual=" + orgType.getSimpleName());
+ throw new InvalidKeyException(
+ "Mismatched private key types: expected=" + prvExpected.getSimpleName() + ", actual=" + orgType.getSimpleName());
}

- prvCloned = clonePrivateKey(prvExpected.cast(prvOriginal));
+ PRV castPrv = prvExpected.cast(prvOriginal);
+ prvCloned = clonePrivateKey(castPrv);
}

return new KeyPair(pubCloned, prvCloned);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractKeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractKeyPairResourceParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractKeyPairResourceParser.java
index a83bf68..e9ad24e 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractKeyPairResourceParser.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractKeyPairResourceParser.java
@@ -78,7 +78,8 @@ public abstract class AbstractKeyPairResourceParser extends AbstractLoggingBean
}

@Override
- public boolean canExtractKeyPairs(String resourceKey, List<String> lines) throws IOException, GeneralSecurityException {
+ public boolean canExtractKeyPairs(String resourceKey, List<String> lines)
+ throws IOException, GeneralSecurityException {
return KeyPairResourceParser.containsMarkerLine(lines, getBeginners());
}

@@ -136,7 +137,7 @@ public abstract class AbstractKeyPairResourceParser extends AbstractLoggingBean
*/
public Collection<KeyPair> extractKeyPairs(
String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, List<String> lines)
- throws IOException, GeneralSecurityException {
+ throws IOException, GeneralSecurityException {
return extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, KeyPairResourceParser.extractDataBytes(lines));
}


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceLoader.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceLoader.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceLoader.java
index 659870f..00c53cf 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceLoader.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceLoader.java
@@ -59,13 +59,15 @@ public interface KeyPairResourceLoader {
*/
KeyPairResourceLoader EMPTY = (resourceKey, passwordProvider, lines) -> Collections.emptyList();

- default Collection<KeyPair> loadKeyPairs(Path path, FilePasswordProvider passwordProvider, OpenOption... options)
- throws IOException, GeneralSecurityException {
+ default Collection<KeyPair> loadKeyPairs(
+ Path path, FilePasswordProvider passwordProvider, OpenOption... options)
+ throws IOException, GeneralSecurityException {
return loadKeyPairs(path, passwordProvider, StandardCharsets.UTF_8, options);
}

- default Collection<KeyPair> loadKeyPairs(Path path, FilePasswordProvider passwordProvider, Charset cs, OpenOption... options)
- throws IOException, GeneralSecurityException {
+ default Collection<KeyPair> loadKeyPairs(
+ Path path, FilePasswordProvider passwordProvider, Charset cs, OpenOption... options)
+ throws IOException, GeneralSecurityException {
try (InputStream stream = Files.newInputStream(path, options)) {
return loadKeyPairs(path.toString(), passwordProvider, stream, cs);
}
@@ -83,35 +85,40 @@ public interface KeyPairResourceLoader {
}
}

- default Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, String data)
- throws IOException, GeneralSecurityException {
+ default Collection<KeyPair> loadKeyPairs(
+ String resourceKey, FilePasswordProvider passwordProvider, String data)
+ throws IOException, GeneralSecurityException {
try (Reader reader = new StringReader((data == null) ? "" : data)) {
return loadKeyPairs(resourceKey, passwordProvider, reader);
}
}

- default Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, InputStream stream)
- throws IOException, GeneralSecurityException {
+ default Collection<KeyPair> loadKeyPairs(
+ String resourceKey, FilePasswordProvider passwordProvider, InputStream stream)
+ throws IOException, GeneralSecurityException {
return loadKeyPairs(resourceKey, passwordProvider, stream, StandardCharsets.UTF_8);
}

- default Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, InputStream stream, Charset cs)
- throws IOException, GeneralSecurityException {
+ default Collection<KeyPair> loadKeyPairs(
+ String resourceKey, FilePasswordProvider passwordProvider, InputStream stream, Charset cs)
+ throws IOException, GeneralSecurityException {
try (Reader reader = new InputStreamReader(
Objects.requireNonNull(stream, "No stream instance"), Objects.requireNonNull(cs, "No charset"))) {
return loadKeyPairs(resourceKey, passwordProvider, reader);
}
}

- default Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, Reader r)
- throws IOException, GeneralSecurityException {
+ default Collection<KeyPair> loadKeyPairs(
+ String resourceKey, FilePasswordProvider passwordProvider, Reader r)
+ throws IOException, GeneralSecurityException {
try (BufferedReader br = new BufferedReader(Objects.requireNonNull(r, "No reader instance"), IoUtils.DEFAULT_COPY_SIZE)) {
return loadKeyPairs(resourceKey, passwordProvider, br);
}
}

- default Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, BufferedReader r)
- throws IOException, GeneralSecurityException {
+ default Collection<KeyPair> loadKeyPairs(
+ String resourceKey, FilePasswordProvider passwordProvider, BufferedReader r)
+ throws IOException, GeneralSecurityException {
return loadKeyPairs(resourceKey, passwordProvider, IoUtils.readAllLines(r));
}


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceParser.java
index 80fc2c5..f7e336a 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceParser.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/KeyPairResourceParser.java
@@ -44,13 +44,15 @@ public interface KeyPairResourceParser extends KeyPairResourceLoader {
*/
KeyPairResourceParser EMPTY = new KeyPairResourceParser() {
@Override
- public Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
- throws IOException, GeneralSecurityException {
+ public Collection<KeyPair> loadKeyPairs(
+ String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
+ throws IOException, GeneralSecurityException {
return Collections.emptyList();
}

@Override
- public boolean canExtractKeyPairs(String resourceKey, List<String> lines) throws IOException, GeneralSecurityException {
+ public boolean canExtractKeyPairs(String resourceKey, List<String> lines)
+ throws IOException, GeneralSecurityException {
return false;
}

@@ -69,7 +71,7 @@ public interface KeyPairResourceParser extends KeyPairResourceLoader {
* the possibility to extract the key pairs
*/
boolean canExtractKeyPairs(String resourceKey, List<String> lines)
- throws IOException, GeneralSecurityException;
+ throws IOException, GeneralSecurityException;

/**
* Converts the lines assumed to contain BASE-64 encoded data into
@@ -94,7 +96,8 @@ public interface KeyPairResourceParser extends KeyPairResourceLoader {
}

static boolean containsMarkerLine(List<String> lines, String marker) {
- return containsMarkerLine(lines, Collections.singletonList(ValidateUtils.checkNotNullAndNotEmpty(marker, "No marker")));
+ return containsMarkerLine(
+ lines, Collections.singletonList(ValidateUtils.checkNotNullAndNotEmpty(marker, "No marker")));
}

static boolean containsMarkerLine(List<String> lines, List<String> markers) {
@@ -149,8 +152,9 @@ public interface KeyPairResourceParser extends KeyPairResourceLoader {
ValidateUtils.checkNotNullAndNotEmpty(parsers, "No parsers to aggregate");
return new KeyPairResourceParser() {
@Override
- public Collection<KeyPair> loadKeyPairs(String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
- throws IOException, GeneralSecurityException {
+ public Collection<KeyPair> loadKeyPairs(
+ String resourceKey, FilePasswordProvider passwordProvider, List<String> lines)
+ throws IOException, GeneralSecurityException {
Collection<KeyPair> keyPairs = Collections.emptyList();
for (KeyPairResourceParser p : parsers) {
if (!p.canExtractKeyPairs(resourceKey, lines)) {
@@ -173,7 +177,8 @@ public interface KeyPairResourceParser extends KeyPairResourceLoader {
}

@Override
- public boolean canExtractKeyPairs(String resourceKey, List<String> lines) throws IOException, GeneralSecurityException {
+ public boolean canExtractKeyPairs(String resourceKey, List<String> lines)
+ throws IOException, GeneralSecurityException {
for (KeyPairResourceParser p : parsers) {
if (p.canExtractKeyPairs(resourceKey, lines)) {
return true;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java
index 9f309ea..d531021 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/openssh/OpenSSHKeyPairResourceParser.java
@@ -120,7 +120,7 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser
byte[] kdfOptions = KeyEntryResolver.readRLEBytes(stream, MAX_KDF_OPTIONS_SIZE);
if (debugEnabled) {
log.debug("extractKeyPairs({}) KDF={}, options={}",
- resourceKey, kdfName, BufferUtils.toHex(':', kdfOptions));
+ resourceKey, kdfName, BufferUtils.toHex(':', kdfOptions));
}

int numKeys = KeyEntryResolver.decodeInt(stream);
@@ -139,7 +139,7 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser
ValidateUtils.checkNotNull(pubKey, "Empty public key #%d in %s", index, resourceKey);
if (traceEnabled) {
log.trace("extractKeyPairs({}) read public key #{}: {} {}",
- resourceKey, index, KeyUtils.getKeyType(pubKey), KeyUtils.getFingerPrint(pubKey));
+ resourceKey, index, KeyUtils.getKeyType(pubKey), KeyUtils.getFingerPrint(pubKey));
}
publicKeys.add(pubKey);
}
@@ -168,7 +168,7 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser
protected List<KeyPair> readPrivateKeys(
String resourceKey, OpenSSHParserContext context, Collection<? extends PublicKey> publicKeys,
FilePasswordProvider passwordProvider, InputStream stream)
- throws IOException, GeneralSecurityException {
+ throws IOException, GeneralSecurityException {
if (GenericUtils.isEmpty(publicKeys)) {
return Collections.emptyList();
}
@@ -178,7 +178,7 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser
int check2 = KeyEntryResolver.decodeInt(stream);
if (traceEnabled) {
log.trace("readPrivateKeys({}) check1=0x{}, check2=0x{}",
- resourceKey, Integer.toHexString(check1), Integer.toHexString(check2));
+ resourceKey, Integer.toHexString(check1), Integer.toHexString(check2));
}

List<KeyPair> keyPairs = new ArrayList<>(publicKeys.size());
@@ -187,7 +187,7 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser
int keyIndex = keyPairs.size() + 1;
if (traceEnabled) {
log.trace("extractKeyPairs({}) read private key #{}: {}",
- resourceKey, keyIndex, pubType);
+ resourceKey, keyIndex, pubType);
}

Map.Entry<PrivateKey, String> prvData = readPrivateKey(resourceKey, context, pubType, passwordProvider, stream);
@@ -196,12 +196,12 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser

String prvType = KeyUtils.getKeyType(prvKey);
ValidateUtils.checkTrue(Objects.equals(pubType, prvType),
- "Mismatched public (%s) vs. private (%s) key type #%d in %s",
- pubType, prvType, keyIndex, resourceKey);
+ "Mismatched public (%s) vs. private (%s) key type #%d in %s",
+ pubType, prvType, keyIndex, resourceKey);

if (traceEnabled) {
log.trace("extractKeyPairs({}) add private key #{}: {} {}",
- resourceKey, keyIndex, prvType, prvData.getValue());
+ resourceKey, keyIndex, prvType, prvData.getValue());
}
keyPairs.add(new KeyPair(pubKey, prvKey));
}
@@ -211,7 +211,7 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser

protected SimpleImmutableEntry<PrivateKey, String> readPrivateKey(
String resourceKey, OpenSSHParserContext context, String keyType, FilePasswordProvider passwordProvider, InputStream stream)
- throws IOException, GeneralSecurityException {
+ throws IOException, GeneralSecurityException {
String prvType = KeyEntryResolver.decodeString(stream, MAX_KEY_TYPE_NAME_LENGTH);
if (!Objects.equals(keyType, prvType)) {
throw new StreamCorruptedException("Mismatched private key type: "
@@ -255,7 +255,7 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser
/**
* @param decoder The decoder to register
* @throws IllegalArgumentException if no decoder or not key type or no
- * supported names for the decoder
+ * supported names for the decoder
* @see PrivateKeyEntryDecoder#getPublicKeyType()
* @see PrivateKeyEntryDecoder#getSupportedTypeNames()
*/
@@ -269,7 +269,8 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser
BY_KEY_CLASS_DECODERS_MAP.put(prvType, decoder);
}

- Collection<String> names = ValidateUtils.checkNotNullAndNotEmpty(decoder.getSupportedTypeNames(), "No supported key type");
+ Collection<String> names =
+ ValidateUtils.checkNotNullAndNotEmpty(decoder.getSupportedTypeNames(), "No supported key type");
synchronized (BY_KEY_TYPE_DECODERS_MAP) {
for (String n : names) {
PrivateKeyEntryDecoder<?, ?> prev = BY_KEY_TYPE_DECODERS_MAP.put(n, decoder);
@@ -283,7 +284,7 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser

/**
* @param keyType The {@code OpenSSH} key type string - e.g., {@code ssh-rsa, ssh-dss}
- * - ignored if {@code null}/empty
+ * - ignored if {@code null}/empty
* @return The registered {@link PrivateKeyEntryDecoder} or {code null} if not found
*/
public static PrivateKeyEntryDecoder<?, ?> getPrivateKeyEntryDecoder(String keyType) {
@@ -331,8 +332,7 @@ public class OpenSSHKeyPairResourceParser extends AbstractKeyPairResourceParser
}

/**
- * @param keyType The key {@link Class} - ignored if {@code null} or not a {@link Key}
- * compatible type
+ * @param keyType The key {@link Class} - ignored if {@code null} or not a {@link Key} compatible type
* @return The registered {@link PrivateKeyEntryDecoder} or {code null} if no match found
*/
public static PrivateKeyEntryDecoder<?, ?> getPrivateKeyEntryDecoder(Class<?> keyType) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/AbstractPEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/AbstractPEMResourceKeyPairParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/AbstractPEMResourceKeyPairParser.java
index 6778610..1528790 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/AbstractPEMResourceKeyPairParser.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/AbstractPEMResourceKeyPairParser.java
@@ -54,7 +54,8 @@ public abstract class AbstractPEMResourceKeyPairParser
private final String algo;
private final String algId;

- protected AbstractPEMResourceKeyPairParser(String algo, String algId, List<String> beginners, List<String> enders) {
+ protected AbstractPEMResourceKeyPairParser(
+ String algo, String algId, List<String> beginners, List<String> enders) {
super(beginners, enders);
this.algo = ValidateUtils.checkNotNullAndNotEmpty(algo, "No encryption algorithm provided");
this.algId = ValidateUtils.checkNotNullAndNotEmpty(algId, "No algorithm identifier provided");
@@ -173,7 +174,9 @@ public abstract class AbstractPEMResourceKeyPairParser
return super.extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, dataLines);
}

- protected byte[] applyPrivateKeyCipher(byte[] bytes, PrivateKeyEncryptionContext encContext, boolean encryptIt) throws GeneralSecurityException {
+ protected byte[] applyPrivateKeyCipher(
+ byte[] bytes, PrivateKeyEncryptionContext encContext, boolean encryptIt)
+ throws GeneralSecurityException {
String cipherName = encContext.getCipherName();
PrivateKeyObfuscator o = encContext.resolvePrivateKeyObfuscator();
if (o == null) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java
index 5347495..c68a815 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/DSSPEMResourceKeyPairParser.java
@@ -69,7 +69,7 @@ public class DSSPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairParse
@Override
public Collection<KeyPair> extractKeyPairs(
String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
- throws IOException, GeneralSecurityException {
+ throws IOException, GeneralSecurityException {
KeyPair kp = decodeDSSKeyPair(SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM), stream, false);
return Collections.singletonList(kp);
}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
index a2075e4..768c30f 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/ECDSAPEMResourceKeyPairParser.java
@@ -73,7 +73,7 @@ public class ECDSAPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairPar
@Override
public Collection<KeyPair> extractKeyPairs(
String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
- throws IOException, GeneralSecurityException {
+ throws IOException, GeneralSecurityException {
Map.Entry<ECPublicKeySpec, ECPrivateKeySpec> spec = decodeECPrivateKeySpec(stream, false);
if (!SecurityUtils.isECCSupported()) {
throw new NoSuchProviderException("ECC not supported");
@@ -163,7 +163,7 @@ public class ECDSAPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairPar

// TODO make sure params object tag is 0xA0

- final List<Integer> curveOID;
+ List<Integer> curveOID;
try (DERParser paramsParser = paramsObject.createParser()) {
ASN1Object namedCurve = paramsParser.readObject();
if (namedCurve == null) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java
index cce1fc8..1967719 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/PKCS8PEMResourceKeyPairParser.java
@@ -66,7 +66,7 @@ public class PKCS8PEMResourceKeyPairParser extends AbstractPEMResourceKeyPairPar
@Override
public Collection<KeyPair> extractKeyPairs(
String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
- throws IOException, GeneralSecurityException {
+ throws IOException, GeneralSecurityException {
// Save the data before getting the algorithm OID since we will need it
byte[] encBytes = IoUtils.toByteArray(stream);
List<Integer> oidAlgorithm = getPKCS8AlgorithmIdentifier(encBytes);
@@ -79,14 +79,14 @@ public class PKCS8PEMResourceKeyPairParser extends AbstractPEMResourceKeyPairPar

public static PrivateKey decodePEMPrivateKeyPKCS8(
List<Integer> oidAlgorithm, byte[] keyBytes, FilePasswordProvider passwordProvider)
- throws GeneralSecurityException {
+ throws GeneralSecurityException {
ValidateUtils.checkNotNullAndNotEmpty(oidAlgorithm, "No PKCS8 algorithm OID");
return decodePEMPrivateKeyPKCS8(GenericUtils.join(oidAlgorithm, '.'), keyBytes, passwordProvider);
}

public static PrivateKey decodePEMPrivateKeyPKCS8(
String oid, byte[] keyBytes, FilePasswordProvider passwordProvider)
- throws GeneralSecurityException {
+ throws GeneralSecurityException {
KeyPairPEMResourceParser parser =
PEMResourceParserUtils.getPEMResourceParserByOid(
ValidateUtils.checkNotNullAndNotEmpty(oid, "No PKCS8 algorithm OID"));

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java
index e7698f9..988c210 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/pem/RSAPEMResourceKeyPairParser.java
@@ -70,7 +70,7 @@ public class RSAPEMResourceKeyPairParser extends AbstractPEMResourceKeyPairParse
@Override
public Collection<KeyPair> extractKeyPairs(
String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
- throws IOException, GeneralSecurityException {
+ throws IOException, GeneralSecurityException {
KeyPair kp = decodeRSAKeyPair(SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM), stream, false);
return Collections.singletonList(kp);
}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java
index c2da507..1433de0 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/bouncycastle/BouncyCastleKeyPairResourceParser.java
@@ -77,7 +77,7 @@ public class BouncyCastleKeyPairResourceParser extends AbstractKeyPairResourcePa
@Override
public Collection<KeyPair> extractKeyPairs(
String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, List<String> lines)
- throws IOException, GeneralSecurityException {
+ throws IOException, GeneralSecurityException {
StringBuilder writer = new StringBuilder(beginMarker.length() + endMarker.length() + lines.size() * 80);
writer.append(beginMarker).append(IoUtils.EOL);
lines.forEach(l -> writer.append(l).append(IoUtils.EOL));
@@ -93,7 +93,7 @@ public class BouncyCastleKeyPairResourceParser extends AbstractKeyPairResourcePa
@Override
public Collection<KeyPair> extractKeyPairs(
String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
- throws IOException, GeneralSecurityException {
+ throws IOException, GeneralSecurityException {
KeyPair kp = loadKeyPair(resourceKey, stream, passwordProvider);
return (kp == null) ? Collections.emptyList() : Collections.singletonList(kp);
}
@@ -103,7 +103,8 @@ public class BouncyCastleKeyPairResourceParser extends AbstractKeyPairResourcePa
try (PEMParser r = new PEMParser(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
Object o = r.readObject();

- SecurityProviderRegistrar registrar = SecurityUtils.getRegisteredProvider(SecurityUtils.BOUNCY_CASTLE);
+ SecurityProviderRegistrar registrar =
+ SecurityUtils.getRegisteredProvider(SecurityUtils.BOUNCY_CASTLE);
if (registrar == null) {
throw new NoSuchProviderException(SecurityUtils.BOUNCY_CASTLE + " registrar not available");
}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-openpgp/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-openpgp/pom.xml b/sshd-openpgp/pom.xml
new file mode 100644
index 0000000..0616662
--- /dev/null
+++ b/sshd-openpgp/pom.xml
@@ -0,0 +1,104 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+
+ <!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd</artifactId>
+ <version>2.1.1-SNAPSHOT</version>
+ <relativePath>..</relativePath>
+ </parent>
+
+ <artifactId>sshd-openpgp</artifactId>
+ <name>Apache Mina SSHD :: OpenPGP key files support utilities</name>
+ <packaging>jar</packaging>
+ <inceptionYear>2018</inceptionYear>
+
+ <properties>
+ <projectRoot>${project.basedir}/..</projectRoot>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-common</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpg-jdk15on</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk15on</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.c02e.jpgpj</groupId>
+ <artifactId>jpgpj</artifactId>
+ <version>0.5</version>
+ </dependency>
+
+ <!-- test dependencies -->
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-common</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>jcl-over-slf4j</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <redirectTestOutputToFile>true</redirectTestOutputToFile>
+ <reportsDirectory>${project.build.directory}/surefire-reports-putty</reportsDirectory>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-openpgp/src/main/java/org/apache/sshd/common/config/keys/loader/openpgp/PGPKeyPairResourceParser.java
----------------------------------------------------------------------
diff --git a/sshd-openpgp/src/main/java/org/apache/sshd/common/config/keys/loader/openpgp/PGPKeyPairResourceParser.java b/sshd-openpgp/src/main/java/org/apache/sshd/common/config/keys/loader/openpgp/PGPKeyPairResourceParser.java
new file mode 100644
index 0000000..8bfb8e4
--- /dev/null
+++ b/sshd-openpgp/src/main/java/org/apache/sshd/common/config/keys/loader/openpgp/PGPKeyPairResourceParser.java
@@ -0,0 +1,445 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.loader.openpgp;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StreamCorruptedException;
+import java.math.BigInteger;
+import java.net.ProtocolException;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.DSAParams;
+import java.security.interfaces.DSAPrivateKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.sshd.common.cipher.ECCurves;
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.FilePasswordProvider.ResourceDecodeResult;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.loader.AbstractKeyPairResourceParser;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.bcpg.BCPGKey;
+import org.bouncycastle.bcpg.DSAPublicBCPGKey;
+import org.bouncycastle.bcpg.DSASecretBCPGKey;
+import org.bouncycastle.bcpg.ECDSAPublicBCPGKey;
+import org.bouncycastle.bcpg.ECPublicBCPGKey;
+import org.bouncycastle.bcpg.ECSecretBCPGKey;
+import org.bouncycastle.bcpg.EdDSAPublicBCPGKey;
+import org.bouncycastle.bcpg.EdSecretBCPGKey;
+import org.bouncycastle.bcpg.PublicKeyPacket;
+import org.bouncycastle.bcpg.RSAPublicBCPGKey;
+import org.bouncycastle.bcpg.RSASecretBCPGKey;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPPrivateKey;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.c02e.jpgpj.Key;
+import org.c02e.jpgpj.Subkey;
+
+/**
+ * @author <a href="mailto:***@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class PGPKeyPairResourceParser extends AbstractKeyPairResourceParser {
+ public static final String BEGIN_MARKER = "BEGIN PGP PRIVATE KEY BLOCK";
+ public static final List<String> BEGINNERS =
+ Collections.unmodifiableList(Collections.singletonList(BEGIN_MARKER));
+
+ public static final String END_MARKER = "END PGP PRIVATE KEY BLOCK";
+ public static final List<String> ENDERS =
+ Collections.unmodifiableList(Collections.singletonList(END_MARKER));
+
+ public static final PGPKeyPairResourceParser INSTANCE = new PGPKeyPairResourceParser();
+
+ public PGPKeyPairResourceParser() {
+ super(BEGINNERS, ENDERS);
+ }
+
+ @Override
+ public Collection<KeyPair> extractKeyPairs(
+ String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, List<String> lines)
+ throws IOException, GeneralSecurityException {
+ // We need to re-construct the original data - including start/end markers
+ String eol = System.lineSeparator();
+ int numLines = GenericUtils.size(lines);
+ StringBuilder sb = new StringBuilder(
+ beginMarker.length() + endMarker.length() + 4 + numLines * 80)
+ .append(beginMarker);
+ if (numLines > 0) {
+ for (String l : lines) {
+ sb.append(eol).append(l);
+ }
+ }
+ sb.append(eol).append(endMarker).append(eol);
+
+ String keyData = sb.toString();
+ byte[] dataBytes = keyData.getBytes(StandardCharsets.US_ASCII);
+ return extractKeyPairs(resourceKey, beginMarker, endMarker, passwordProvider, dataBytes);
+ }
+
+ @Override
+ public Collection<KeyPair> extractKeyPairs(
+ String resourceKey, String beginMarker, String endMarker, FilePasswordProvider passwordProvider, InputStream stream)
+ throws IOException, GeneralSecurityException {
+ for (int retryCount = 1;; retryCount++) {
+ String password = (passwordProvider == null) ? null : passwordProvider.getPassword(resourceKey);
+ Collection<KeyPair> keys;
+ try {
+ if (retryCount > 1) {
+ stream.reset();
+ }
+
+ Key key = new Key(stream, password);
+ if (GenericUtils.isEmpty(password)) {
+ key.setNoPassphrase(true);
+ } else {
+ key.setPassphrase(password);
+ }
+
+ keys = extractKeyPairs(resourceKey, key.getSubkeys());
+ } catch (IOException | GeneralSecurityException | PGPException | RuntimeException e) {
+ ResourceDecodeResult result = (passwordProvider != null)
+ ? passwordProvider.handleDecodeAttemptResult(resourceKey, password, e)
+ : ResourceDecodeResult.TERMINATE;
+ if (result == null) {
+ result = ResourceDecodeResult.TERMINATE;
+ }
+
+ switch (result) {
+ case TERMINATE:
+ if (e instanceof PGPException) {
+ throw new StreamCorruptedException(
+ "Failed (" + e.getClass().getSimpleName() + ")"
+ + " to decode " + resourceKey + ": " + e.getMessage());
+ } else if (e instanceof IOException) {
+ throw (IOException) e;
+ } else if (e instanceof GeneralSecurityException) {
+ throw (GeneralSecurityException) e;
+ } else {
+ throw (RuntimeException) e;
+ }
+ case RETRY:
+ continue;
+ case IGNORE:
+ return Collections.emptyList();
+ default:
+ throw new ProtocolException("Unsupported decode attempt result (" + result + ") for " + resourceKey);
+ }
+ }
+
+ if (passwordProvider != null) {
+ passwordProvider.handleDecodeAttemptResult(resourceKey, password, null);
+ }
+ return keys;
+ }
+ }
+
+ public List<KeyPair> extractKeyPairs(String resourceKey, Collection<? extends Subkey> subKeys)
+ throws IOException, GeneralSecurityException {
+ if (GenericUtils.isEmpty(subKeys)) {
+ return Collections.emptyList();
+ }
+
+ List<KeyPair> kpList = new ArrayList<>(subKeys.size());
+ boolean debugEnabled = log.isDebugEnabled();
+ for (Subkey sk : subKeys) {
+ PublicKey pubKey;
+ try {
+ pubKey = extractPublicKey(resourceKey, sk);
+ if (pubKey == null) {
+ if (debugEnabled) {
+ log.debug("extractKeyPairs({}) no public key extracted from {}", resourceKey, sk);
+ }
+ continue;
+ }
+ } catch (IOException | GeneralSecurityException | RuntimeException | Error e) {
+ log.error("extractKeyPairs({}) failed ({}) to extract public key of {}: {}",
+ resourceKey, e.getClass().getSimpleName(), sk, e.getMessage());
+ throw e;
+ }
+
+ PrivateKey prvKey;
+ try {
+ prvKey = extractPrivateKey(resourceKey, sk, pubKey);
+ if (prvKey == null) {
+ if (debugEnabled) {
+ log.debug("extractKeyPairs({}) no private key extracted from {}", resourceKey, sk);
+ }
+ continue;
+ }
+ } catch (IOException | GeneralSecurityException | RuntimeException | Error e) {
+ log.error("extractKeyPairs({}) failed ({}) to extract private key of {}: {}",
+ resourceKey, e.getClass().getSimpleName(), sk, e.getMessage());
+ throw e;
+ } catch (PGPException e) {
+ log.error("extractKeyPairs({}) failed ({}) to parse private key of {}: {}",
+ resourceKey, e.getClass().getSimpleName(), sk, e.getMessage());
+ throw new StreamCorruptedException("Failed to parse " + resourceKey + " sub-key=" + sk + ": " + e.getMessage());
+ }
+
+ KeyPair kp = new KeyPair(pubKey, prvKey);
+ KeyPair prev = kpList.isEmpty()
+ ? null
+ : kpList.stream()
+ .filter(e -> KeyUtils.compareKeyPairs(e, kp))
+ .findFirst()
+ .orElse(null);
+ if (prev != null) {
+ if (debugEnabled) {
+ log.debug("extractKeyPairs({}) skip duplicate sub-key={}", resourceKey, sk);
+ }
+ continue;
+ }
+
+ kpList.add(kp);
+ }
+
+ return kpList;
+ }
+
+ public PublicKey extractPublicKey(String resourceKey, Subkey sk) throws IOException, GeneralSecurityException {
+ if (sk == null) {
+ return null;
+ }
+
+ PGPPublicKey pgpKey = Objects.requireNonNull(sk.getPublicKey(), "Missing sub-key public key");
+ PublicKeyPacket pgpPacket = Objects.requireNonNull(pgpKey.getPublicKeyPacket(), "Missing public key packet");
+ BCPGKey bcKey = Objects.requireNonNull(pgpPacket.getKey(), "Missing BC key");
+ if (bcKey instanceof RSAPublicBCPGKey) {
+ return extractRSAPublicKey(resourceKey, (RSAPublicBCPGKey) bcKey);
+ } else if (bcKey instanceof ECPublicBCPGKey) {
+ return extractECPublicKey(resourceKey, (ECPublicBCPGKey) bcKey);
+ } else if (bcKey instanceof DSAPublicBCPGKey) {
+ return extractDSSPublicKey(resourceKey, (DSAPublicBCPGKey) bcKey);
+ } else {
+ throw new NoSuchAlgorithmException("Unsupported BC public key type: " + bcKey.getClass().getSimpleName());
+ }
+ }
+
+ public RSAPublicKey extractRSAPublicKey(String resourceKey, RSAPublicBCPGKey bcKey) throws GeneralSecurityException {
+ if (bcKey == null) {
+ return null;
+ }
+
+ BigInteger e = bcKey.getPublicExponent();
+ BigInteger n = bcKey.getModulus();
+ return generatePublicKey(KeyUtils.RSA_ALGORITHM, RSAPublicKey.class, new RSAPublicKeySpec(n, e));
+ }
+
+ public PublicKey extractECPublicKey(String resourceKey, ECPublicBCPGKey bcKey) throws GeneralSecurityException {
+ if (bcKey == null) {
+ return null;
+ } else if (bcKey instanceof EdDSAPublicBCPGKey) {
+ return extractEdDSAPublicKey(resourceKey, (EdDSAPublicBCPGKey) bcKey);
+ } else if (bcKey instanceof ECDSAPublicBCPGKey) {
+ return extractECDSAPublicKey(resourceKey, (ECDSAPublicBCPGKey) bcKey);
+ } else {
+ throw new NoSuchAlgorithmException("Unsupported EC public key type: " + bcKey.getClass().getSimpleName());
+ }
+ }
+
+ public ECPublicKey extractECDSAPublicKey(String resourceKey, ECDSAPublicBCPGKey bcKey) throws GeneralSecurityException {
+ if (bcKey == null) {
+ return null;
+ }
+
+ ASN1ObjectIdentifier asnId = bcKey.getCurveOID();
+ String oid = asnId.getId();
+ ECCurves curve = ECCurves.fromOID(oid);
+ if (curve == null) {
+ throw new InvalidKeySpecException("Not an EC curve OID: " + oid);
+ }
+
+ if (!SecurityUtils.isECCSupported()) {
+ throw new NoSuchProviderException("ECC not supported");
+ }
+
+ BigInteger encPoint = bcKey.getEncodedPoint();
+ byte[] octets = encPoint.toByteArray();
+ ECPoint w;
+ try {
+ w = ECCurves.octetStringToEcPoint(octets);
+ if (w == null) {
+ throw new InvalidKeySpecException("No ECPoint generated for curve=" + curve.getName()
+ + " from octets=" + BufferUtils.toHex(':', octets));
+ }
+ } catch (RuntimeException e) {
+ throw new InvalidKeySpecException("Failed (" + e.getClass().getSimpleName() + ")"
+ + " to generate ECPoint for curve=" + curve.getName()
+ + " from octets=" + BufferUtils.toHex(':', octets)
+ + ": " + e.getMessage());
+ }
+
+ ECParameterSpec paramSpec = curve.getParameters();
+ return generatePublicKey(KeyUtils.EC_ALGORITHM, ECPublicKey.class, new ECPublicKeySpec(w, paramSpec));
+ }
+
+ public PublicKey extractEdDSAPublicKey(String resourceKey, EdDSAPublicBCPGKey bcKey) throws GeneralSecurityException {
+ if (bcKey == null) {
+ return null;
+ }
+
+ if (!SecurityUtils.isEDDSACurveSupported()) {
+ throw new NoSuchProviderException("EdDSA not supported");
+ }
+
+ throw new NoSuchAlgorithmException("Unsupported EdDSA public key type: " + bcKey.getClass().getSimpleName());
+ }
+
+ public DSAPublicKey extractDSSPublicKey(String resourceKey, DSAPublicBCPGKey bcKey) throws GeneralSecurityException {
+ if (bcKey == null) {
+ return null;
+ }
+
+ BigInteger p = bcKey.getP();
+ BigInteger q = bcKey.getQ();
+ BigInteger g = bcKey.getG();
+ BigInteger y = bcKey.getY();
+ return generatePublicKey(KeyUtils.DSS_ALGORITHM, DSAPublicKey.class, new DSAPublicKeySpec(y, p, q, g));
+ }
+
+ protected <K extends PublicKey> K generatePublicKey(String algorithm, Class<K> keyType, KeySpec keySpec)
+ throws GeneralSecurityException {
+ KeyFactory factory = getKeyFactory(algorithm);
+ PublicKey pubKey = factory.generatePublic(keySpec);
+ return keyType.cast(pubKey);
+ }
+
+ public PrivateKey extractPrivateKey(String resourceKey, Subkey sk, PublicKey pubKey)
+ throws IOException, GeneralSecurityException, PGPException {
+ if (sk == null) {
+ return null;
+ }
+
+ PGPPrivateKey pgpKey = Objects.requireNonNull(sk.getPrivateKey(), "Missing sub-key private key");
+ BCPGKey bcKey = Objects.requireNonNull(pgpKey.getPrivateKeyDataPacket(), "Missing BC key");
+ if (bcKey instanceof RSASecretBCPGKey) {
+ return extractRSAPrivateKey(resourceKey, (RSAPublicKey) pubKey, (RSASecretBCPGKey) bcKey);
+ } else if (bcKey instanceof ECSecretBCPGKey) {
+ return extractECDSAPrivateKey(resourceKey, (ECPublicKey) pubKey, (ECSecretBCPGKey) bcKey);
+ } else if (bcKey instanceof EdSecretBCPGKey) {
+ return extractEdDSAPrivateKey(resourceKey, pubKey, (EdSecretBCPGKey) bcKey);
+ } else if (bcKey instanceof DSASecretBCPGKey) {
+ return extractDSSPrivateKey(resourceKey, (DSAPublicKey) pubKey, (DSASecretBCPGKey) bcKey);
+ } else {
+ throw new NoSuchAlgorithmException("Unsupported BC public key type: " + bcKey.getClass().getSimpleName());
+ }
+ }
+
+ public ECPrivateKey extractECDSAPrivateKey(String resourceKey, ECPublicKey pubKey, ECSecretBCPGKey bcKey)
+ throws GeneralSecurityException {
+ if (bcKey == null) {
+ return null;
+ }
+
+ if (!SecurityUtils.isECCSupported()) {
+ throw new NoSuchProviderException("ECC not supported");
+ }
+
+ ECParameterSpec params = pubKey.getParams();
+ BigInteger x = bcKey.getX();
+ return generatePrivateKey(KeyUtils.EC_ALGORITHM, ECPrivateKey.class, new ECPrivateKeySpec(x, params));
+ }
+
+ public PrivateKey extractEdDSAPrivateKey(String resourceKey, PublicKey pubKey, EdSecretBCPGKey bcKey)
+ throws GeneralSecurityException {
+ if (bcKey == null) {
+ return null;
+ }
+
+ if (!SecurityUtils.isEDDSACurveSupported()) {
+ throw new NoSuchProviderException("EdDSA not supported");
+ }
+
+ throw new NoSuchAlgorithmException("Unsupported EdDSA private key type: " + bcKey.getClass().getSimpleName());
+ }
+
+ public RSAPrivateKey extractRSAPrivateKey(String resourceKey, RSAPublicKey pubKey, RSASecretBCPGKey bcKey)
+ throws GeneralSecurityException {
+ if (bcKey == null) {
+ return null;
+ }
+
+ return generatePrivateKey(KeyUtils.RSA_ALGORITHM, RSAPrivateKey.class,
+ new RSAPrivateCrtKeySpec(
+ bcKey.getModulus(),
+ pubKey.getPublicExponent(),
+ bcKey.getPrivateExponent(),
+ bcKey.getPrimeP(),
+ bcKey.getPrimeQ(),
+ bcKey.getPrimeExponentP(),
+ bcKey.getPrimeExponentQ(),
+ bcKey.getCrtCoefficient()));
+ }
+
+ public DSAPrivateKey extractDSSPrivateKey(String resourceKey, DSAPublicKey pubKey, DSASecretBCPGKey bcKey)
+ throws GeneralSecurityException {
+ if (bcKey == null) {
+ return null;
+ }
+
+ DSAParams params = pubKey.getParams();
+ if (params == null) {
+ throw new InvalidKeyException("Missing parameters in public key");
+ }
+
+ return generatePrivateKey(KeyUtils.DSS_ALGORITHM, DSAPrivateKey.class,
+ new DSAPrivateKeySpec(bcKey.getX(), params.getP(), params.getQ(), params.getG()));
+ }
+
+ protected <K extends PrivateKey> K generatePrivateKey(String algorithm, Class<K> keyType, KeySpec keySpec)
+ throws GeneralSecurityException {
+ KeyFactory factory = getKeyFactory(algorithm);
+ PrivateKey prvKey = factory.generatePrivate(keySpec);
+ return keyType.cast(prvKey);
+ }
+
+ protected KeyFactory getKeyFactory(String algorithm) throws GeneralSecurityException {
+ return SecurityUtils.getKeyFactory(algorithm);
+ }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-openpgp/src/main/resources/.gitignore
----------------------------------------------------------------------
diff --git a/sshd-openpgp/src/main/resources/.gitignore b/sshd-openpgp/src/main/resources/.gitignore
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-openpgp/src/test/java/org/apache/sshd/common/config/keys/loader/openpgp/PGPKeyPairResourceParserTest.java
----------------------------------------------------------------------
diff --git a/sshd-openpgp/src/test/java/org/apache/sshd/common/config/keys/loader/openpgp/PGPKeyPairResourceParserTest.java b/sshd-openpgp/src/test/java/org/apache/sshd/common/config/keys/loader/openpgp/PGPKeyPairResourceParserTest.java
new file mode 100644
index 0000000..3a121d9
--- /dev/null
+++ b/sshd-openpgp/src/test/java/org/apache/sshd/common/config/keys/loader/openpgp/PGPKeyPairResourceParserTest.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.config.keys.loader.openpgp;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.common.config.keys.FilePasswordProvider;
+import org.apache.sshd.common.config.keys.FilePasswordProvider.ResourceDecodeResult;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * @author <a href="mailto:***@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class PGPKeyPairResourceParserTest extends JUnitTestSupport {
+ public static final String PASSWORD = "super secret passphrase";
+
+ private final String resourceName;
+ private final ResourceDecodeResult result;
+ private final FilePasswordProvider passwordProvider;
+ private final AtomicInteger retriesCount = new AtomicInteger(0);
+ private final int maxRetries = 3;
+
+ public PGPKeyPairResourceParserTest(String resourceName, ResourceDecodeResult result, String password) {
+ this.resourceName = resourceName;
+ this.result = result;
+ this.passwordProvider = new FilePasswordProvider() {
+
+ @Override
+ @SuppressWarnings("synthetic-access")
+ public String getPassword(String resourceKey) throws IOException {
+ switch (result) {
+ case IGNORE:
+ case TERMINATE:
+ return "qweryuiop123456!@#$%^";
+ case RETRY: {
+ int count = retriesCount.incrementAndGet();
+ if (count == maxRetries) {
+ return PASSWORD;
+ } else {
+ return "retry #" + count;
+ }
+ }
+ default:
+ throw new UnsupportedOperationException("Unknown decode result type: " + result);
+ }
+ }
+
+ @Override
+ @SuppressWarnings("synthetic-access")
+ public ResourceDecodeResult handleDecodeAttemptResult(
+ String resourceKey, String password, Exception err)
+ throws IOException, GeneralSecurityException {
+ if (err == null) {
+ return null;
+ }
+
+ if (result == ResourceDecodeResult.RETRY) {
+ if (retriesCount.get() >= maxRetries) {
+ return ResourceDecodeResult.TERMINATE;
+ }
+ }
+
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return FilePasswordProvider.class.getSimpleName() + "[" + result + "]";
+ }
+ };
+ }
+
+ @Parameters(name = "{0} / {1}")
+ public static List<Object[]> parameters() {
+ return Collections.unmodifiableList(new ArrayList<Object[]>() {
+ // Not serializing it
+ private static final long serialVersionUID = 1L;
+
+ {
+ for (ResourceDecodeResult result : ResourceDecodeResult.values()) {
+ add(new Object[] {"super-secret-passphrase-RSA-2048-v1p0-private.gpg", result, PASSWORD});
+ add(new Object[] {"super-secret-passphrase-RSA-2048-v1p6p1-private.gpg", result, PASSWORD});
+ add(new Object[] {"super-secret-passphrase-RSA-4096-v2p0p8-private.gpg", result, PASSWORD});
+ add(new Object[] {"super-secret-passphrase-DSA-2048-gpg4win-3.1.3.gpg", result, PASSWORD});
+ add(new Object[] {"super-secret-passphrase-EC-384-v1p0-private.gpg", result, PASSWORD});
+ // TODO add(new Object[] {"super-secret-passphrase-ed25519-gpg4win-3.1.3.gpg, result", PASSWORD});
+ }
+ }
+ });
+ }
+
+ @Test
+ public void testDecodePrivateKeyPair() throws IOException, GeneralSecurityException {
+ InputStream stream = getClass().getResourceAsStream(resourceName);
+ assertNotNull("Missing " + resourceName, stream);
+
+ Collection<KeyPair> keys;
+ try {
+ keys = PGPKeyPairResourceParser.INSTANCE.loadKeyPairs(resourceName, passwordProvider, stream);
+ } catch (Exception e) {
+ if (result != ResourceDecodeResult.TERMINATE) {
+ fail("Mismatched result mode for " + e.getClass().getSimpleName() + "[" + e.getMessage() + "]");
+ }
+ return;
+ } finally {
+ stream.close();
+ }
+
+ switch (result) {
+ case IGNORE:
+ assertTrue("Unexpected keys recovered", GenericUtils.isEmpty(keys));
+ return;
+
+ case RETRY:
+ assertFalse("No keys recovered", GenericUtils.isEmpty(keys));
+ break;
+
+ case TERMINATE: // fall through...
+ default:
+ fail("Unexpected return value on request=" + result + ": " + keys);
+ }
+
+ for (KeyPair kp : keys) {
+ PublicKey pubKey = kp.getPublic();
+ PrivateKey prvKey = kp.getPrivate();
+ assertNotNull("No public key for private=" + prvKey, pubKey);
+ assertNotNull("No private key for public=" + pubKey, prvKey);
+
+ String pubType = KeyUtils.getKeyType(pubKey);
+ String prvType = KeyUtils.getKeyType(prvKey);
+ assertEquals("Mismatched public/private key types", pubType, prvType);
+
+ int pubSize = KeyUtils.getKeySize(pubKey);
+ int prvSize = KeyUtils.getKeySize(prvKey);
+ assertEquals("Mismatched public/private key size", pubSize, prvSize);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[" + resourceName + "/" + result + "]";
+ }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-openpgp/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/sshd-openpgp/src/test/resources/log4j.properties b/sshd-openpgp/src/test/resources/log4j.properties
new file mode 100644
index 0000000..e3f24df
--- /dev/null
+++ b/sshd-openpgp/src/test/resources/log4j.properties
@@ -0,0 +1,38 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+#
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+
+#
+# The logging properties used during tests..
+#
+log4j.rootLogger=INFO, stdout, logfile
+#log4j.logger.org.apache.sshd=TRACE
+#log4j.logger.org.apache.sshd.common.channel.Window=DEBUG
+
+# CONSOLE appender
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
+
+# File appender
+log4j.appender.logfile=org.apache.log4j.FileAppender
+log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
+log4j.appender.logfile.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
+log4j.appender.logfile.file=target/sshd-pgp-tests.log
+log4j.appender.logfile.append=true

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/DSA-2048-gpg4win-3.1.3.asc
----------------------------------------------------------------------
diff --git a/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/DSA-2048-gpg4win-3.1.3.asc b/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/DSA-2048-gpg4win-3.1.3.asc
new file mode 100644
index 0000000..cd12896
--- /dev/null
+++ b/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/DSA-2048-gpg4win-3.1.3.asc
@@ -0,0 +1,25 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQMuBFuT5zARCADAPwVpS4++wgCKgYNq/J0afO3lAsBKGBdRIPKNS4aFpEZNxVxF
+QbeazCE9qKh360hYy8bO0JeJeto59ZdZ1DxPmLm7RSpkvvDLcZzgEqa0HTPH4WaZ
+5xANi1p1SgCs6BHKcuPVj42BPNMmxGSXVW7/dsr8GKOAq45nXllwJrUC3Vs9AEtz
+nSCjudw5os03OhPj31+EYnGnFvQSgZIpWVyDyjapd0fmnYGKg3G+cKuw4KZ6zR9G
+LY5/hu6QPmilBwQFhmJhnd5vSQTFiLpLXvHgfdQ6CtMPng4njydBKCTL7a/qP5t1
+c2t3Tc1JGmN0vq1lH2/weug12icfI2VARgX/AQCL2NCPIv0b5XtvHd/YEijPkapL
+AfxwCA0K55/lqjFMlwf+KB9MY1xl0r0FBx+x+1xa/pvgBXjQwh1g+mvxWY1f2YZo
+VIGWBRD+b1m2MJKgDZFgJ4bAhUWmll96+7Scxy7fO1PBWKdxiXBfwxjYgTipaxOB
+oDYEVwt/ZEm7oI1NXyZMIRNkINwz1L5AvZg9zAXVAhaxJvwpWJU7YK42QMzJkaF8
+AIlfjbrh7g4ordGj6OH/pzxB8fTYmM79hg7oN1syXdxiUX8h37tnvBehlCOyD68k
+UoEmP8EEaeukHSUXbSObHNyPAJmEjzkpEfSPNGeuU3VaLeca5Ofw5tBRgzAyjj9j
+7VG2yfn34ox2t4CfCS+Lrn1yvLuhd/AUgHfC7T0+Zwf/R/Pq7JFIT/nWgZQIf4TC
+7Up6fj1QoOy8qwQ2oh6ugCtSGO6pxCcebjZntg18NUQwMBW1h+gASi3vAWhOcsEh
+cRr5ZQE3wgxSfIvNiFNJmxjBqQztN1Ur+eXSPUX3eS+xiTyKGIYoAv1p3vSkrGd/
+8pb5rq0i4xaOFrAgMQhe/vby0KRULZpYjog4lzDbLHDcg+qyOusJ1wsKYWhGlJe3
+edb75xE9lpT/jEyLFkFU9KOhtw4kpbU3/KpaZBTPbTv2cZqaYrtve4j7Qi3rBsJq
+uURZlAWCeJjtmRlVEtKOzCd+ba4Afmy+pBkngy7qb/Ms2HFO4QeAjvMQROgJRo9I
+rLQmTHlvciBHb2xkc3RlaW4gPGxnb2xkc3RlaW5AYXBhY2hlLm9yZz6IkAQTEQgA
+OBYhBEiwzOjxhisPStdn64wXeSM70ePABQJbk+cwAhsjBQsJCAcCBhUKCQgLAgQW
+AgMBAh4BAheAAAoJEIwXeSM70ePAL/0BAIqWxD6K6llI9NWK5jXTppBeHfEGC182
+Y9EAkbxGLtAMAP0RYt41Cuhj6uodcZGooW/2/pO3fbb6ZeGDAN5o80bBgQ==
+=DtpZ
+-----END PGP PUBLIC KEY BLOCK-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/EC-348-v1p0-public.asc
----------------------------------------------------------------------
diff --git a/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/EC-348-v1p0-public.asc b/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/EC-348-v1p0-public.asc
new file mode 100644
index 0000000..683a6a9
--- /dev/null
+++ b/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/EC-348-v1p0-public.asc
@@ -0,0 +1,27 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: Keybase OpenPGP v1.0.0
+Comment: https://keybase.io/crypto
+
+xm8EW5NNmBMFK4EEACIDAwT0MDx4JzYywOIpKKCGMKkrSVT4AeTOpQHhrIWRmK9R
+bJhYSsUj3G2qT4z6Rgw7MPs7ltyCkujfNxxet0RKvYZhbc9XH/F9qOSssHX9fC6v
+xL4/JX2hrz7ONgEX/0C4UhXNSUx5b3IgR29sZHN0ZWluIChBcGFjaGUgU1NIRCBF
+Q0MgMzQ4IFBHUCB0ZXN0IGtleSkgPGxnb2xkc3RlaW5AYXBhY2hlLm9yZz7CjwQT
+EwoAFwUCW5NNmAIbLwMLCQcDFQoIAh4BAheAAAoJEOF1BlV7wP+2gbABfil1RHzd
+GJ63mt6RW74DbTsKgZx09Oqd2r4s0IBEQms6rXJZ+Zx/xxvyM4pCdLlM/QF/cxyV
+2vndOsNLQH4iKrT1ViQBBM6Nyc2GO/HoaqiBhNMX7/2d1D8WK7MXxM2Sjcw9zlIE
+W5NNmBMIKoZIzj0DAQcCAwQqZJVpapgY4NsWFowJXeflzHofgKhoYhoIoEDGGnFq
+Yi8K9R/c14PyYse2b6SvF6DH42m36T3HrlvqvZr52mrRwsAnBBgTCgAPBQJbk02Y
+BQkPCZwAAhsuAGoJEOF1BlV7wP+2XyAEGRMKAAYFAluTTZgACgkQpwAX8HS3WvpD
+tQEAhpJAjrEKh7K9Pl6eUUKaIWI1YxzJ4WFeTcalW5xRM3cBALg1uyXQYsZE1zmu
+lTpEKwGulyBXas3zifpNqg1Qfnre3a0BgMv+Q0mRVjf8tjnOHHUfqm5q8cGVhp1a
+Bpzao3h/stGHSFXbnbbc9dZ9d6A4AJAswwF/X9qlxyCdVVPx/J4isAjtXcXOWNr8
+lKiC5cnIKi+6cEDKrog3NPt2JqW17AZ1r0DizlIEW5NNmBMIKoZIzj0DAQcCAwQU
+Kaq9zh1Srht7PhNpV4i8zwn1YkFAGC4SSb5DdoiBKqA6ab5gHenwAnAB46P1X7ja
+qY9lBj6Turyall4JvXsUwsAnBBgTCgAPBQJbk02YBQkPCZwAAhsuAGoJEOF1BlV7
+wP+2XyAEGRMKAAYFAluTTZgACgkQLSNkcETmDCOLcwD6Akxg6Va0EPnG/TLaYwHm
++ZbsE1Kstd5KSoamgD//u1YA/A+tbAX+volyjRSm4Svj7AZRzOe1EnPgxL1PFfq+
+zGI9p78Bf1ne6pkw7pX85ifcpSutP4zvfAFoYUkva5zS/Lf9zF8bHmHQmLool4w+
+3KdtIqu5WgGAreha4/QmpUMdP5DGnzbc2bmuG7YS+1l8DrNNr8S9aO2No5JU13Mw
+xFOYZmFpXlGj
+=30E1
+-----END PGP PUBLIC KEY BLOCK-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/RSA-2048-v1p0-public.asc
----------------------------------------------------------------------
diff --git a/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/RSA-2048-v1p0-public.asc b/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/RSA-2048-v1p0-public.asc
new file mode 100644
index 0000000..cb3449f
--- /dev/null
+++ b/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/RSA-2048-v1p0-public.asc
@@ -0,0 +1,56 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: Keybase OpenPGP v1.0.0
+Comment: https://keybase.io/crypto
+
+xsBNBFuTTBMBCAC1e2+g9f9fsqq5FAne4LdeRn7iCwUJZwQ7QEIG1638Bs8nDI2I
+W4QwNi1lGecyl4o3E1w2lDKzfoKfdkDkKZxnHATFxQIHLsw6ZNV8WO+MLDlSPEJQ
+AXlEU1ywX+LCUKsYep9SlktEWmrm04kmmwktq4TbCM44MMm2qKTFs5DG5xSB3k0/
+WxTPfpoccSV/ye8FeP97qxNZpqRUT/g+5Y/1c/3foKOEHaQYwK81YyKo1nPQRUuz
+R4IIQUQ+vWreO6sar3bSr7l8my2xYBGHMPdKIE4Wf6AzSFxK3R6oYqMWCA++DgIc
+zUW0eamnojzm6ZYYDToZZ+Y4z3Tx7tJ+iOerABEBAAHNQ0x5b3IgR29sZHN0ZWlu
+IChBcGFjaGUgU1NIRCB0ZXN0IGtleSAoUlNBKSkgPGxnb2xkc3RlaW5AYXBhY2hl
+Lm9yZz7CwG0EEwEKABcFAluTTBMCGy8DCwkHAxUKCAIeAQIXgAAKCRBLFjWLWij8
+aLA0B/9qfYp4Y0aiLSIioiHOH8Fwt4tTLHbqzXOjoduXmpg7rWa2LjSyaecnNIhP
+fNv7g/1TLvbSQiDU857Er8rYRzVP+JR4CepH5l0TVij62xn4hwSOsGGsaU2Z81b0
+6Mt/QkZjtJ12SgCLTaAKUftg8Gc6qqq24FMgmLqpL9R8zY0gCxPkU2A3xkL1kMj4
+uw1IMjofVj/bJPIAaasd0RXkxN4i/kuUXOkzUzTcUJnsWmZHYxvwEp5D58IH59Ef
+n9bT5iFiDdHG+jPF9ca+7fsBapdl/ujmihDeNKmWHGJ9zd8/MOv0+4VeldZt0HwI
+o2py9OjSXVZB3upwQw8nz4RxiiIbzsBNBFuTTBMBCAC02EDW7LQCZjiCnIK92a8A
+hdMEZ1EI7VCLuSyHXpnk3p0oegm4NDr4bHsenG3C4ZkjX4drGe/E87Jxo28PQYKS
+/lGm+ZAQ7royuVLVf7pYvU4qvuVCi9IoYS8eBTqCl0k5urMMggknbvapATMtv1Ar
+gsNtUPE94J9tGKWrSoDm1713R49K1cdhHcdgxW5TsxHclFxC8PcLKYSGUHQwv+tk
+p11nyhYGlrEODucGEye0m4MHnhbehG7OsX+54qa2F8Vgks2MUXnIEixR5qK8MwnQ
+eBH6kwV7dKphiY4kX2tww867FmM4gYOn27luk0ZDvHhfFbPL/TVY5HTGVdDlQVj9
+ABEBAAHCwYQEGAEKAA8FAluTTBMFCQ8JnAACGy4BKQkQSxY1i1oo/GjAXSAEGQEK
+AAYFAluTTBMACgkQGtDEWdb9eQw3WggAk8jNpLiQAj7+IOWsum3k9xiM2iBzYYQU
+PS5OjGBykKe5o37HRMdpIACS2f/4EXMG6U9+WfYYvGKlcse8IUy6PeyI35qhT8OC
+1RMaRDphXDoGzFnirYT+wmgvUUfez+/YEqwatDgsc4bL7iGS/WMQ4oqNNVjvZRKF
+OIPVFPefqICsxit2xMhsTDmxlqVCCLozhJSCg65J7qSayEgm40OKZf6tpFU/hkpR
+QfCSDkx3HXHPStr80/tJdcrUSd0/iKwnXD4mdnnZc7vizisF3uI1bcppa0vtWDgP
+/YeZJUefr4DWyjH/jNcxT1NHiOiTZ1InFjk2yxKCTaVteZ0rDdmGDaYXB/4gAB1z
+pGv3gBgRlJoJ1qqu4ZeHk5MVx5zqlIAfSraoUW2YnqkskJUbztTrAgkaWW0gt5hb
++dXgwmLkxwXCER68WL2/RkLm94xMDUgoh+f9rNbllqHYEee2K6M2RcjizMtL0SqR
+hCeoQUN/T4NfcVOEWpdjOhqDm6uh1/X6riWYMZfIeRHAQCHVSZxeBSzVw2QAxJ+m
+kGFVM2+GBOGZLBzl+WCYxknN4VxLLZBh1BIw5wOy9vYQEG5LYX5ACJ3DaKShZ5Aw
+wZ/28nR8hDx2/ntbhUiG/pDSoyqEs6ltV2ygfC9U7avV3k6E8h9+xMHsDL0gAn9F
+YrD1ewXofWShCEinzsBNBFuTTBMBCAC9Et6iaxY/Qy7ZpmbM0TBBrDtOopYZ0hPs
+1kV5bJOmk8/B2+pbPitL0w0wvkYUKtayfY9mBRABDloaODJH/BhoCeImc2K9UmyM
+7XzY+B/yIM1V98MxkGF69dlnp+v0pFgJ1KDMkadj26/nlsZt8n1gA4zKMPE+dB3U
+PcmPFDgRVv5xeF/2BLy0DZVhemUuGexTXkPKdhLS6joPJWAvJFvwkqrRvV4/Kqa0
+Bix12q4Q+31OhaLhsN/tGq7HeLizpoWsXEVE7KpjUJToo1VMdsMA0u+w83SkfRsK
+TNYDgqNPdlgI+lHX7HpJx4cqrYgmPjm830oCuTpJm5YgFM/CVUalABEBAAHCwYQE
+GAEKAA8FAluTTBMFCQ8JnAACGy4BKQkQSxY1i1oo/GjAXSAEGQEKAAYFAluTTBMA
+CgkQu+enUOSGX8IW6gf/UrnVmAO7Xdy49TiCAJoFdwl5FL5FbKT0x+/mUqLdgyto
+wgWHa1SkfND5j3WBRSqM8AFLHFIY7EKk+loRWdcPpXvTpC5+G3iba8Ps0DkxedHc
+8iYTu8DJ0EZIvY3h31ffRe+yxaYwgs095ftJhWVsZ28DMbriMmjbfpO9u5Asr1xU
+gljPscAKN8b3JEmvjGedP9qDEPYN5tpcQKzQcusWV0tT8fd/4aWNo0E/APpkKeOW
+6Fkij6wwA7aJSDunx7UMI8vYkwgztk9Nn/Ucc8u6Yu8l/nB7/nbZsLyZWqOl26eb
+/4oqZCRX263d0Zkgg4E+2SxcKRbOGRqQFBNFk8Esgoq8CACXFeSYHUPTp4TpuduM
+bm/J+Wm4gkeednUNwHuVmVmW/f1+2yc9gx/Gu6w6n9D5HPsNx6iusvCZuZMV+KdQ
+fq5Sdv4wme8sS64i8nyiARn4njTO9GmB3rDgQis79KhIdXPposb952772YqhFCLD
+IfaK4+Ee6sxDnmQ3Guf1MQzTMqyBK25EaUQYDMB9Xda9E0oyCb8Onii+qbzqdvmC
+bCZjnm3usHIGlTsDEobSz+MDbLIbAfqe6JOvNFUWM8US7smeRQj/anX3dCrWWu/T
+nj/8FJ5nJcDksq/BncINFrHQX0ALUHHp+FvHlOFFAhkdf9zEMR27rnIZQZr1WvBV
+yqQD
+=csQY
+-----END PGP PUBLIC KEY BLOCK-----

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/259ee8c7/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/RSA-2048-v1p6p1-public.asc
----------------------------------------------------------------------
diff --git a/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/RSA-2048-v1p6p1-public.asc b/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/RSA-2048-v1p6p1-public.asc
new file mode 100644
index 0000000..db72826
--- /dev/null
+++ b/sshd-openpgp/src/test/resources/org/apache/sshd/common/config/keys/loader/openpgp/RSA-2048-v1p6p1-public.asc
@@ -0,0 +1,18 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG C# v1.6.1.0
+
+mQENBFuTTP0BCACGwHfOtKlKSNPophndVpK+M3OhxjFT/rKTBAr3zzzVAn1ofdbS
+oi0AysA95P++5mffGryhiNjLzLG8zehtU4/eij0NN1OFOtxILc0wViRqe6Ldikit
+fV6crknwA3q09vTWuzqQDus+lC1qxl36CA6FbnYWNQNt0iLL2rE3doOHAX4rSaIf
+JI19LKlJMsCQKAJhNq65ORg2hnOaycwtybmVhVXSGAw8La1TgbOp2M+loWhNDoFT
+H0tpUPNuNkadv3BqGMb90tG/uQLE9GrxJGOFsgRxertGNXesY1+JofCVJQuxXp1A
+yLTo6tr28oSLFOcfsDMuDAejnokP0QnEinGRABEBAAG0FWxnb2xkc3RlaW5AYXBh
+Y2hlLm9yZ4kBHAQQAQIABgUCW5NM/QAKCRDB8+7vuObR4GWUB/9QaGm5xLSLtxNo
+GTStJtenkNNM/urRsu7iZHvj1ELrOUffDS7da/uIqXtWh6xtR8vDG9ByCm+Wp47N
+6amCbmpt3oYKTKQhBxZ9w7Ic3EaHSmcAL+riLnZts313PCm7NZASNO/3H6I4R8TH
+NUtP0UdwymK0O7vhDlhj094faKS0XeSyckZs5NQY63rcGK9+Dj3TGlO2jZo/JnYO
+Bc3DxICtC2/HoXfkGhZPw0DjOfMYDhWRxDdfP4ftpHHiOQTLSyEq6enQhNOlwg0K
+PjQmvH62iWoRSOCcKqM0PBlftzjssFRqICsAIpE0urzRaGawzzG348GoXjP3bEVS
+wznsBe9q
+=wQIr
+-----END PGP PUBLIC KEY BLOCK-----
l***@apache.org
2018-10-31 05:03:13 UTC
Permalink
[SSHD-859] Moved read-only access of AttributeStore to AttributeRepository


Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/ba008331
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/ba008331
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/ba008331

Branch: refs/heads/master
Commit: ba008331bdb92afb85e3027c006cc3569ddefad2
Parents: 4e12d7e
Author: Lyor Goldstein <***@apache.org>
Authored: Tue Oct 30 12:07:29 2018 +0200
Committer: Lyor Goldstein <***@apache.org>
Committed: Wed Oct 31 07:02:56 2018 +0200

----------------------------------------------------------------------
.../apache/sshd/common/AttributeRepository.java | 108 +++++++++++++++++++
.../org/apache/sshd/common/AttributeStore.java | 42 +-------
.../org/apache/sshd/common/FactoryManager.java | 4 +-
.../sshd/common/channel/AbstractChannel.java | 17 ++-
.../org/apache/sshd/common/channel/Channel.java | 5 +-
.../common/helpers/AbstractFactoryManager.java | 18 +++-
.../org/apache/sshd/common/io/IoService.java | 1 -
.../sshd/common/io/nio2/Nio2Acceptor.java | 16 ++-
.../sshd/common/io/nio2/Nio2Connector.java | 23 ++--
.../apache/sshd/common/io/nio2/Nio2Service.java | 11 +-
.../apache/sshd/common/io/nio2/Nio2Session.java | 75 ++++++++-----
.../org/apache/sshd/common/session/Session.java | 5 +-
.../common/session/helpers/SessionHelper.java | 18 +++-
.../pubkey/CachingPublicKeyAuthenticator.java | 7 +-
.../apache/sshd/common/AttributeStoreTest.java | 4 +-
.../apache/sshd/common/io/mina/MinaService.java | 12 +--
.../apache/sshd/common/io/mina/MinaSession.java | 28 +++--
.../org/apache/sshd/netty/NettyIoConnector.java | 1 -
.../org/apache/sshd/netty/NettyIoService.java | 3 +-
.../org/apache/sshd/netty/NettyIoSession.java | 6 +-
20 files changed, 267 insertions(+), 137 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ba008331/sshd-common/src/main/java/org/apache/sshd/common/AttributeRepository.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/AttributeRepository.java b/sshd-common/src/main/java/org/apache/sshd/common/AttributeRepository.java
new file mode 100644
index 0000000..f754e87
--- /dev/null
+++ b/sshd-common/src/main/java/org/apache/sshd/common/AttributeRepository.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.GenericUtils;
+
+/**
+ * @author <a href="mailto:***@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface AttributeRepository {
+ /**
+ * <P>
+ * Type safe key for storage of user attributes. Typically it is used as a static
+ * variable that is shared between the producer and the consumer. To further
+ * restrict access the setting or getting it from the store one can add static
+ * {@code get/set methods} e.g:
+ * </P>
+ *
+ * <pre>
+ * public static final AttributeKey&lt;MyValue&gt; MY_KEY = new AttributeKey&lt;MyValue&gt;();
+ *
+ * public static MyValue getMyValue(Session s) {
+ * return s.getAttribute(MY_KEY);
+ * }
+ *
+ * public static void setMyValue(Session s, MyValue value) {
+ * s.setAttribute(MY_KEY, value);
+ * }
+ * </pre>
+ *
+ * @param <T> type of value stored in the attribute.
+ * @author <a href="mailto:***@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+ // CHECKSTYLE:OFF
+ class AttributeKey<T> {
+ public AttributeKey() {
+ super();
+ }
+ }
+ // CHECKSTYLE:ON
+
+ /**
+ * Returns the value of the user-defined attribute.
+ *
+ * @param <T> The generic attribute type
+ * @param key The key of the attribute; must not be {@code null}.
+ * @return {@code null} if there is no value associated with the specified key
+ */
+ <T> T getAttribute(AttributeKey<T> key);
+
+ /**
+ * @return A {@link Collection} <u>snapshot</u> of all the currently registered
+ * attributes in the repository
+ */
+ Collection<AttributeKey<?>> attributeKeys();
+
+ static <A> AttributeRepository ofKeyValuePair(AttributeKey<A> key, A value) {
+ Objects.requireNonNull(key, "No key provided");
+ Objects.requireNonNull(value, "No value provided");
+ return ofAttributesMap(Collections.singletonMap(key, value));
+ }
+
+ static AttributeRepository ofAttributesMap(Map<AttributeKey<?>, ?> attributes) {
+ return new AttributeRepository() {
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T getAttribute(AttributeKey<T> key) {
+ Objects.requireNonNull(key, "No key provided");
+ return GenericUtils.isEmpty(attributes) ? null : (T) attributes.get(key);
+ }
+
+ @Override
+ public Collection<AttributeKey<?>> attributeKeys() {
+ return GenericUtils.isEmpty(attributes)
+ ? Collections.emptySet()
+ : new HashSet<>(attributes.keySet());
+ }
+
+ @Override
+ public String toString() {
+ return AttributeRepository.class.getSimpleName() + "[" + attributes + "]";
+ }
+ };
+ }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ba008331/sshd-common/src/main/java/org/apache/sshd/common/AttributeStore.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/AttributeStore.java b/sshd-common/src/main/java/org/apache/sshd/common/AttributeStore.java
index feccc11..cc5665a 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/AttributeStore.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/AttributeStore.java
@@ -27,47 +27,7 @@ import java.util.function.Function;
*
* @author <a href="mailto:***@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public interface AttributeStore {
- /**
- * <P>
- * Type safe key for storage of user attributes. Typically it is used as a static
- * variable that is shared between the producer and the consumer. To further
- * restrict access the setting or getting it from the store one can add static
- * {@code get/set methods} e.g:
- * </P>
- *
- * <pre>
- * public static final AttributeKey&lt;MyValue&gt; MY_KEY = new AttributeKey&lt;MyValue&gt;();
- *
- * public static MyValue getMyValue(Session s) {
- * return s.getAttribute(MY_KEY);
- * }
- *
- * public static void setMyValue(Session s, MyValue value) {
- * s.setAttribute(MY_KEY, value);
- * }
- * </pre>
- *
- * @param <T> type of value stored in the attribute.
- * @author <a href="mailto:***@mina.apache.org">Apache MINA SSHD Project</a>
- */
- // CHECKSTYLE:OFF
- class AttributeKey<T> {
- public AttributeKey() {
- super();
- }
- }
- // CHECKSTYLE:ON
-
- /**
- * Returns the value of the user-defined attribute.
- *
- * @param <T> The generic attribute type
- * @param key The key of the attribute; must not be {@code null}.
- * @return {@code null} if there is no value associated with the specified key
- */
- <T> T getAttribute(AttributeKey<T> key);
-
+public interface AttributeStore extends AttributeRepository {
/**
* If the specified key is not already associated with a value (or is mapped
* to {@code null}), attempts to compute its value using the given mapping

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ba008331/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
index cc04275..18ee70a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
@@ -469,7 +469,7 @@ public interface FactoryManager
List<RequestHandler<ConnectionService>> getGlobalRequestHandlers();

@Override
- default <T> T resolveAttribute(AttributeKey<T> key) {
+ default <T> T resolveAttribute(AttributeRepository.AttributeKey<T> key) {
return resolveAttribute(this, key);
}

@@ -479,7 +479,7 @@ public interface FactoryManager
* @param key The attribute key - never {@code null}
* @return Associated value - {@code null} if not found
*/
- static <T> T resolveAttribute(FactoryManager manager, AttributeKey<T> key) {
+ static <T> T resolveAttribute(FactoryManager manager, AttributeRepository.AttributeKey<T> key) {
Objects.requireNonNull(key, "No key");
return (manager == null) ? null : manager.getAttribute(key);
}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ba008331/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java b/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
index d213d72..d8918a0 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
@@ -23,6 +23,7 @@ import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -35,6 +36,7 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.function.IntUnaryOperator;

+import org.apache.sshd.common.AttributeRepository;
import org.apache.sshd.common.Closeable;
import org.apache.sshd.common.FactoryManager;
import org.apache.sshd.common.PropertyResolver;
@@ -107,7 +109,7 @@ public abstract class AbstractChannel
*/
private final Map<String, Date> pendingRequests = new ConcurrentHashMap<>();
private final Map<String, Object> properties = new ConcurrentHashMap<>();
- private final Map<AttributeKey<?>, Object> attributes = new ConcurrentHashMap<>();
+ private final Map<AttributeRepository.AttributeKey<?>, Object> attributes = new ConcurrentHashMap<>();

protected AbstractChannel(boolean client) {
this("", client);
@@ -949,20 +951,25 @@ public abstract class AbstractChannel

@Override
@SuppressWarnings("unchecked")
- public <T> T getAttribute(AttributeKey<T> key) {
+ public <T> T getAttribute(AttributeRepository.AttributeKey<T> key) {
return (T) attributes.get(Objects.requireNonNull(key, "No key"));
}

@Override
+ public Collection<AttributeKey<?>> attributeKeys() {
+ return attributes.isEmpty() ? Collections.emptySet() : new HashSet<>(attributes.keySet());
+ }
+
+ @Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public <T> T computeAttributeIfAbsent(
- AttributeKey<T> key, Function<? super AttributeKey<T>, ? extends T> resolver) {
+ AttributeRepository.AttributeKey<T> key, Function<? super AttributeRepository.AttributeKey<T>, ? extends T> resolver) {
return (T) attributes.computeIfAbsent(Objects.requireNonNull(key, "No key"), (Function) resolver);
}

@Override
@SuppressWarnings("unchecked")
- public <T> T setAttribute(AttributeKey<T> key, T value) {
+ public <T> T setAttribute(AttributeRepository.AttributeKey<T> key, T value) {
return (T) attributes.put(
Objects.requireNonNull(key, "No key"),
Objects.requireNonNull(value, "No value"));
@@ -970,7 +977,7 @@ public abstract class AbstractChannel

@Override
@SuppressWarnings("unchecked")
- public <T> T removeAttribute(AttributeKey<T> key) {
+ public <T> T removeAttribute(AttributeRepository.AttributeKey<T> key) {
return (T) attributes.remove(Objects.requireNonNull(key, "No key"));
}


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ba008331/sshd-core/src/main/java/org/apache/sshd/common/channel/Channel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/channel/Channel.java b/sshd-core/src/main/java/org/apache/sshd/common/channel/Channel.java
index 099c095..258642e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/channel/Channel.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/channel/Channel.java
@@ -24,6 +24,7 @@ import java.util.List;
import java.util.Objects;

import org.apache.sshd.client.future.OpenFuture;
+import org.apache.sshd.common.AttributeRepository;
import org.apache.sshd.common.AttributeStore;
import org.apache.sshd.common.Closeable;
import org.apache.sshd.common.PropertyResolver;
@@ -214,7 +215,7 @@ public interface Channel
void handleOpenFailure(Buffer buffer) throws IOException;

@Override
- default <T> T resolveAttribute(AttributeKey<T> key) {
+ default <T> T resolveAttribute(AttributeRepository.AttributeKey<T> key) {
return resolveAttribute(this, key);
}

@@ -228,7 +229,7 @@ public interface Channel
* @see #getSession()
* @see Session#resolveAttribute(Session, AttributeKey)
*/
- static <T> T resolveAttribute(Channel channel, AttributeKey<T> key) {
+ static <T> T resolveAttribute(Channel channel, AttributeRepository.AttributeKey<T> key) {
Objects.requireNonNull(key, "No key");
if (channel == null) {
return null;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ba008331/sshd-core/src/main/java/org/apache/sshd/common/helpers/AbstractFactoryManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/helpers/AbstractFactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/common/helpers/AbstractFactoryManager.java
index 7b9a68e..4d717a2 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/helpers/AbstractFactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/helpers/AbstractFactoryManager.java
@@ -19,6 +19,8 @@
package org.apache.sshd.common.helpers;

import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -30,6 +32,7 @@ import java.util.concurrent.TimeUnit;
import java.util.function.Function;

import org.apache.sshd.agent.SshAgentFactory;
+import org.apache.sshd.common.AttributeRepository;
import org.apache.sshd.common.Factory;
import org.apache.sshd.common.FactoryManager;
import org.apache.sshd.common.NamedFactory;
@@ -88,7 +91,7 @@ public abstract class AbstractFactoryManager extends AbstractKexFactoryManager i
protected final PortForwardingEventListener tunnelListenerProxy;

private final Map<String, Object> properties = new ConcurrentHashMap<>();
- private final Map<AttributeKey<?>, Object> attributes = new ConcurrentHashMap<>();
+ private final Map<AttributeRepository.AttributeKey<?>, Object> attributes = new ConcurrentHashMap<>();
private PropertyResolver parentResolver = SyspropsMapWrapper.SYSPROPS_RESOLVER;
private ReservedSessionMessagesHandler reservedSessionMessagesHandler;
private ChannelStreamPacketWriterResolver channelStreamPacketWriterResolver;
@@ -146,20 +149,25 @@ public abstract class AbstractFactoryManager extends AbstractKexFactoryManager i

@Override
@SuppressWarnings("unchecked")
- public <T> T getAttribute(AttributeKey<T> key) {
+ public <T> T getAttribute(AttributeRepository.AttributeKey<T> key) {
return (T) attributes.get(Objects.requireNonNull(key, "No key"));
}

@Override
+ public Collection<AttributeKey<?>> attributeKeys() {
+ return attributes.isEmpty() ? Collections.emptySet() : new HashSet<>(attributes.keySet());
+ }
+
+ @Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public <T> T computeAttributeIfAbsent(
- AttributeKey<T> key, Function<? super AttributeKey<T>, ? extends T> resolver) {
+ AttributeRepository.AttributeKey<T> key, Function<? super AttributeRepository.AttributeKey<T>, ? extends T> resolver) {
return (T) attributes.computeIfAbsent(Objects.requireNonNull(key, "No key"), (Function) resolver);
}

@Override
@SuppressWarnings("unchecked")
- public <T> T setAttribute(AttributeKey<T> key, T value) {
+ public <T> T setAttribute(AttributeRepository.AttributeKey<T> key, T value) {
return (T) attributes.put(
Objects.requireNonNull(key, "No key"),
Objects.requireNonNull(value, "No value"));
@@ -167,7 +175,7 @@ public abstract class AbstractFactoryManager extends AbstractKexFactoryManager i

@Override
@SuppressWarnings("unchecked")
- public <T> T removeAttribute(AttributeKey<T> key) {
+ public <T> T removeAttribute(AttributeRepository.AttributeKey<T> key) {
return (T) attributes.remove(Objects.requireNonNull(key, "No key"));
}


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ba008331/sshd-core/src/main/java/org/apache/sshd/common/io/IoService.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/io/IoService.java b/sshd-core/src/main/java/org/apache/sshd/common/io/IoService.java
index 453e625..4a8ce58 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/io/IoService.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/io/IoService.java
@@ -40,5 +40,4 @@ public interface IoService extends Closeable, IoServiceEventListenerManager {
* @return the sessions. An empty collection if there's no session.
*/
Map<Long, IoSession> getManagedSessions();
-
}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ba008331/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Acceptor.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Acceptor.java b/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Acceptor.java
index 0c90ce3..0dcfdb8 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Acceptor.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Acceptor.java
@@ -74,12 +74,14 @@ public class Nio2Acceptor extends Nio2Service implements IoAcceptor {
}

protected AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(
- SocketAddress address, AsynchronousChannelGroup group) throws IOException {
+ SocketAddress address, AsynchronousChannelGroup group)
+ throws IOException {
return AsynchronousServerSocketChannel.open(group);
}

protected CompletionHandler<AsynchronousSocketChannel, ? super SocketAddress> createSocketCompletionHandler(
- Map<SocketAddress, AsynchronousServerSocketChannel> channelsMap, AsynchronousServerSocketChannel socket) throws IOException {
+ Map<SocketAddress, AsynchronousServerSocketChannel> channelsMap, AsynchronousServerSocketChannel socket)
+ throws IOException {
return new AcceptCompletionHandler(socket);
}

@@ -112,7 +114,7 @@ public class Nio2Acceptor extends Nio2Service implements IoAcceptor {
channel.close();
} catch (IOException e) {
log.warn("unbind({}) {} while unbinding channel: {}",
- address, e.getClass().getSimpleName(), e.getMessage());
+ address, e.getClass().getSimpleName(), e.getMessage());
if (debugEnabled) {
log.debug("unbind(" + address + ") failure details", e);
}
@@ -208,7 +210,9 @@ public class Nio2Acceptor extends Nio2Service implements IoAcceptor {
// Create a session
IoHandler handler = getIoHandler();
setSocketOptions(result);
- session = Objects.requireNonNull(createSession(Nio2Acceptor.this, address, result, handler), "No NIO2 session created");
+ session = Objects.requireNonNull(
+ createSession(Nio2Acceptor.this, address, result, handler),
+ "No NIO2 session created");
sessionId = session.getId();
handler.sessionCreated(session);
sessions.put(sessionId, session);
@@ -264,7 +268,9 @@ public class Nio2Acceptor extends Nio2Service implements IoAcceptor {
}
}

- protected Nio2Session createSession(Nio2Acceptor acceptor, SocketAddress address, AsynchronousSocketChannel channel, IoHandler handler) throws Throwable {
+ protected Nio2Session createSession(
+ Nio2Acceptor acceptor, SocketAddress address, AsynchronousSocketChannel channel, IoHandler handler)
+ throws Throwable {
if (log.isTraceEnabled()) {
log.trace("createNio2Session({}) address={}", acceptor, address);
}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ba008331/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Connector.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Connector.java b/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Connector.java
index 061795d..e8410e7 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Connector.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Connector.java
@@ -61,15 +61,16 @@ public class Nio2Connector extends Nio2Service implements IoConnector {
socket.bind(localAddress);
}
Nio2CompletionHandler<Void, Object> completionHandler =
- ValidateUtils.checkNotNull(createConnectionCompletionHandler(future, socket, getFactoryManager(), getIoHandler()),
- "No connection completion handler created for %s",
- address);
+ ValidateUtils.checkNotNull(
+ createConnectionCompletionHandler(future, socket, getFactoryManager(), getIoHandler()),
+ "No connection completion handler created for %s",
+ address);
socket.connect(address, null, completionHandler);
} catch (Throwable exc) {
Throwable t = GenericUtils.peelException(exc);
if (debugEnabled) {
log.debug("connect({}) failed ({}) to schedule connection: {}",
- address, t.getClass().getSimpleName(), t.getMessage());
+ address, t.getClass().getSimpleName(), t.getMessage());
}
if (log.isTraceEnabled()) {
log.trace("connect(" + address + ") connection failure details", t);
@@ -82,7 +83,7 @@ public class Nio2Connector extends Nio2Service implements IoConnector {
} catch (IOException err) {
if (debugEnabled) {
log.debug("connect({}) - failed ({}) to close socket: {}",
- address, err.getClass().getSimpleName(), err.getMessage());
+ address, err.getClass().getSimpleName(), err.getMessage());
}
}

@@ -93,7 +94,7 @@ public class Nio2Connector extends Nio2Service implements IoConnector {
} catch (IOException err) {
if (debugEnabled) {
log.debug("connect({}) - failed ({}) to close channel: {}",
- address, err.getClass().getSimpleName(), err.getMessage());
+ address, err.getClass().getSimpleName(), err.getMessage());
}
}

@@ -170,7 +171,8 @@ public class Nio2Connector extends Nio2Service implements IoConnector {
}

if (debugEnabled) {
- log.debug("onCompleted - failed {} to start session: {}", t.getClass().getSimpleName(), t.getMessage());
+ log.debug("onCompleted - failed {} to start session: {}",
+ t.getClass().getSimpleName(), t.getMessage());
}
if (log.isTraceEnabled()) {
log.trace("onCompleted - session creation failure details", t);
@@ -180,7 +182,8 @@ public class Nio2Connector extends Nio2Service implements IoConnector {
socket.close();
} catch (IOException err) {
if (debugEnabled) {
- log.debug("onCompleted - failed {} to close socket: {}", err.getClass().getSimpleName(), err.getMessage());
+ log.debug("onCompleted - failed {} to close socket: {}",
+ err.getClass().getSimpleName(), err.getMessage());
}
}

@@ -195,7 +198,9 @@ public class Nio2Connector extends Nio2Service implements IoConnector {
}
}

- protected Nio2Session createSession(FactoryManager manager, IoHandler handler, AsynchronousSocketChannel socket) throws Throwable {
+ protected Nio2Session createSession(
+ FactoryManager manager, IoHandler handler, AsynchronousSocketChannel socket)
+ throws Throwable {
return new Nio2Session(this, manager, handler, socket);
}


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ba008331/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Service.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Service.java b/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Service.java
index b340ec8..066df84 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Service.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Service.java
@@ -115,7 +115,8 @@ public abstract class Nio2Service extends AbstractInnerCloseable implements IoSe
}
} catch (IOException e) {
if (log.isDebugEnabled()) {
- log.debug("dispose({}) {} while stopping service: {}", this, e.getClass().getSimpleName(), e.getMessage());
+ log.debug("dispose({}) {} while stopping service: {}",
+ this, e.getClass().getSimpleName(), e.getMessage());
}

if (log.isTraceEnabled()) {
@@ -126,7 +127,9 @@ public abstract class Nio2Service extends AbstractInnerCloseable implements IoSe

@Override
protected Closeable getInnerCloseable() {
- return builder().parallel(toString(), sessions.values()).build();
+ return builder()
+ .parallel(toString(), sessions.values())
+ .build();
}

@Override
@@ -165,7 +168,9 @@ public abstract class Nio2Service extends AbstractInnerCloseable implements IoSe
return socket;
}

- protected <T> boolean setOption(NetworkChannel socket, String property, SocketOption<T> option, T defaultValue) throws IOException {
+ protected <T> boolean setOption(
+ NetworkChannel socket, String property, SocketOption<T> option, T defaultValue)
+ throws IOException {
PropertyResolver manager = getFactoryManager();
String valStr = manager.getString(property);
T val = defaultValue;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ba008331/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Session.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Session.java b/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Session.java
index 9a9ef4a..a49e132 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Session.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/io/nio2/Nio2Session.java
@@ -64,7 +64,8 @@ public class Nio2Session extends AbstractCloseable implements IoSession {
private final Queue<Nio2DefaultIoWriteFuture> writes = new LinkedTransferQueue<>();
private final AtomicReference<Nio2DefaultIoWriteFuture> currentWrite = new AtomicReference<>();

- public Nio2Session(Nio2Service service, FactoryManager manager, IoHandler handler, AsynchronousSocketChannel socket) throws IOException {
+ public Nio2Session(Nio2Service service, FactoryManager manager, IoHandler handler, AsynchronousSocketChannel socket)
+ throws IOException {
this.service = Objects.requireNonNull(service, "No service instance");
this.manager = Objects.requireNonNull(manager, "No factory manager");
this.ioHandler = Objects.requireNonNull(handler, "No IoHandler");
@@ -135,7 +136,7 @@ public class Nio2Session extends AbstractCloseable implements IoSession {
} catch (IOException e) {
if (debugEnabled) {
log.debug("suspend({}) failed {{}) to shutdown input: {}",
- this, e.getClass().getSimpleName(), e.getMessage());
+ this, e.getClass().getSimpleName(), e.getMessage());
}
}

@@ -144,7 +145,7 @@ public class Nio2Session extends AbstractCloseable implements IoSession {
} catch (IOException e) {
if (debugEnabled) {
log.debug("suspend({}) failed {{}) to shutdown output: {}",
- this, e.getClass().getSimpleName(), e.getMessage());
+ this, e.getClass().getSimpleName(), e.getMessage());
}
}
}
@@ -156,7 +157,8 @@ public class Nio2Session extends AbstractCloseable implements IoSession {
}

ByteBuffer buf = ByteBuffer.wrap(buffer.array(), buffer.rpos(), buffer.available());
- Nio2DefaultIoWriteFuture future = new Nio2DefaultIoWriteFuture(getRemoteAddress(), null, buf);
+ Nio2DefaultIoWriteFuture future =
+ new Nio2DefaultIoWriteFuture(getRemoteAddress(), null, buf);
if (isClosing()) {
Throwable exc = new ClosedChannelException();
future.setException(exc);
@@ -179,14 +181,14 @@ public class Nio2Session extends AbstractCloseable implements IoSession {
try {
if (log.isDebugEnabled()) {
log.debug("exceptionCaught({}) caught {}[{}] - calling handler",
- this, exc.getClass().getSimpleName(), exc.getMessage());
+ this, exc.getClass().getSimpleName(), exc.getMessage());
}
handler.exceptionCaught(this, exc);
} catch (Throwable e) {
Throwable t = GenericUtils.peelException(e);
if (log.isDebugEnabled()) {
log.debug("exceptionCaught({}) Exception handler threw {}, closing the session: {}",
- this, t.getClass().getSimpleName(), t.getMessage());
+ this, t.getClass().getSimpleName(), t.getMessage());
}

if (log.isTraceEnabled()) {
@@ -201,15 +203,18 @@ public class Nio2Session extends AbstractCloseable implements IoSession {
@Override
protected CloseFuture doCloseGracefully() {
Object closeId = toString();
- return builder().when(closeId, writes).run(closeId, () -> {
- try {
- AsynchronousSocketChannel socket = getSocket();
- socket.shutdownOutput();
- } catch (IOException e) {
- log.info("doCloseGracefully({}) {} while shutting down output: {}",
- this, e.getClass().getSimpleName(), e.getMessage());
- }
- }).build().close(false);
+ return builder()
+ .when(closeId, writes)
+ .run(closeId, () -> {
+ try {
+ AsynchronousSocketChannel socket = getSocket();
+ socket.shutdownOutput();
+ } catch (IOException e) {
+ log.info("doCloseGracefully({}) {} while shutting down output: {}",
+ this, e.getClass().getSimpleName(), e.getMessage());
+ }
+ }).build()
+ .close(false);
}

@Override
@@ -231,7 +236,8 @@ public class Nio2Session extends AbstractCloseable implements IoSession {
if (debugEnabled) {
log.debug("doCloseImmediately({}) signal write abort for future={}", this, future);
}
- future.setException(new WriteAbortedException("Write request aborted due to immediate session close", null));
+ future.setException(
+ new WriteAbortedException("Write request aborted due to immediate session close", null));
}
} else {
break;
@@ -252,7 +258,7 @@ public class Nio2Session extends AbstractCloseable implements IoSession {

} catch (IOException e) {
log.info("doCloseImmediately({}) {} caught while closing socket={}: {}",
- this, e.getClass().getSimpleName(), socket, e.getMessage());
+ this, e.getClass().getSimpleName(), socket, e.getMessage());
if (debugEnabled) {
log.debug("doCloseImmediately(" + this + ") socket=" + socket + " close failure details", e);
}
@@ -267,7 +273,7 @@ public class Nio2Session extends AbstractCloseable implements IoSession {
} catch (Throwable e) {
if (debugEnabled) {
log.debug("doCloseImmediately({}) {} while calling IoHandler#sessionClosed: {}",
- this, e.getClass().getSimpleName(), e.getMessage());
+ this, e.getClass().getSimpleName(), e.getMessage());
}

if (log.isTraceEnabled()) {
@@ -307,11 +313,14 @@ public class Nio2Session extends AbstractCloseable implements IoSession {

protected void doReadCycle(ByteBuffer buffer, Readable bufReader) {
Nio2CompletionHandler<Integer, Object> completion =
- Objects.requireNonNull(createReadCycleCompletionHandler(buffer, bufReader), "No completion handler created");
+ Objects.requireNonNull(
+ createReadCycleCompletionHandler(buffer, bufReader),
+ "No completion handler created");
doReadCycle(buffer, completion);
}

- protected Nio2CompletionHandler<Integer, Object> createReadCycleCompletionHandler(ByteBuffer buffer, Readable bufReader) {
+ protected Nio2CompletionHandler<Integer, Object> createReadCycleCompletionHandler(
+ ByteBuffer buffer, Readable bufReader) {
return new Nio2CompletionHandler<Integer, Object>() {
@Override
protected void onCompleted(Integer result, Object attachment) {
@@ -326,7 +335,8 @@ public class Nio2Session extends AbstractCloseable implements IoSession {
}

protected void handleReadCycleCompletion(
- ByteBuffer buffer, Readable bufReader, Nio2CompletionHandler<Integer, Object> completionHandler, Integer result, Object attachment) {
+ ByteBuffer buffer, Readable bufReader, Nio2CompletionHandler<Integer, Object> completionHandler,
+ Integer result, Object attachment) {
try {
boolean debugEnabled = log.isDebugEnabled();
if (result >= 0) {
@@ -348,7 +358,8 @@ public class Nio2Session extends AbstractCloseable implements IoSession {
}
} else {
if (debugEnabled) {
- log.debug("handleReadCycleCompletion({}) Socket has been disconnected (result={}), closing IoSession now", this, result);
+ log.debug("handleReadCycleCompletion({}) Socket has been disconnected (result={}), closing IoSession now",
+ this, result);
}
close(true);
}
@@ -363,7 +374,8 @@ public class Nio2Session extends AbstractCloseable implements IoSession {

protected void doReadCycle(ByteBuffer buffer, Nio2CompletionHandler<Integer, Object> completion) {
AsynchronousSocketChannel socket = getSocket();
- long readTimeout = manager.getLongProperty(FactoryManager.NIO2_READ_TIMEOUT, FactoryManager.DEFAULT_NIO2_READ_TIMEOUT);
+ long readTimeout = manager.getLongProperty(
+ FactoryManager.NIO2_READ_TIMEOUT, FactoryManager.DEFAULT_NIO2_READ_TIMEOUT);
socket.read(buffer, readTimeout, TimeUnit.MILLISECONDS, null, completion);
}

@@ -381,8 +393,9 @@ public class Nio2Session extends AbstractCloseable implements IoSession {
AsynchronousSocketChannel socket = getSocket();
ByteBuffer buffer = future.getBuffer();
Nio2CompletionHandler<Integer, Object> handler =
- Objects.requireNonNull(createWriteCycleCompletionHandler(future, socket, buffer),
- "No write cycle completion handler created");
+ Objects.requireNonNull(
+ createWriteCycleCompletionHandler(future, socket, buffer),
+ "No write cycle completion handler created");
doWriteCycle(buffer, handler);
} catch (Throwable e) {
future.setWritten();
@@ -397,7 +410,8 @@ public class Nio2Session extends AbstractCloseable implements IoSession {

protected void doWriteCycle(ByteBuffer buffer, Nio2CompletionHandler<Integer, Object> completion) {
AsynchronousSocketChannel socket = getSocket();
- long writeTimeout = manager.getLongProperty(FactoryManager.NIO2_MIN_WRITE_TIMEOUT, FactoryManager.DEFAULT_NIO2_MIN_WRITE_TIMEOUT);
+ long writeTimeout = manager.getLongProperty(
+ FactoryManager.NIO2_MIN_WRITE_TIMEOUT, FactoryManager.DEFAULT_NIO2_MIN_WRITE_TIMEOUT);
socket.write(buffer, writeTimeout, TimeUnit.MILLISECONDS, null, completion);
}

@@ -449,7 +463,7 @@ public class Nio2Session extends AbstractCloseable implements IoSession {
ByteBuffer buffer, int writeLen, Throwable exc, Object attachment) {
if (log.isDebugEnabled()) {
log.debug("handleWriteCycleFailure({}) failed ({}) to write {} bytes: {}",
- this, exc.getClass().getSimpleName(), writeLen, exc.getMessage());
+ this, exc.getClass().getSimpleName(), writeLen, exc.getMessage());
}

boolean traceEnabled = log.isTraceEnabled();
@@ -465,7 +479,7 @@ public class Nio2Session extends AbstractCloseable implements IoSession {
} catch (RuntimeException e) {
if (traceEnabled) {
log.trace("handleWriteCycleFailure({}) failed ({}) to finish writing: {}",
- this, e.getClass().getSimpleName(), e.getMessage());
+ this, e.getClass().getSimpleName(), e.getMessage());
}
}
}
@@ -478,6 +492,9 @@ public class Nio2Session extends AbstractCloseable implements IoSession {

@Override
public String toString() {
- return getClass().getSimpleName() + "[local=" + getLocalAddress() + ", remote=" + getRemoteAddress() + "]";
+ return getClass().getSimpleName()
+ + "[local=" + getLocalAddress()
+ + ", remote=" + getRemoteAddress()
+ + "]";
}
}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ba008331/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java b/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
index 6886a39..06516da 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
@@ -22,6 +22,7 @@ import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

+import org.apache.sshd.common.AttributeRepository;
import org.apache.sshd.common.AttributeStore;
import org.apache.sshd.common.Closeable;
import org.apache.sshd.common.FactoryManager;
@@ -345,7 +346,7 @@ public interface Session
void startService(String name) throws Exception;

@Override
- default <T> T resolveAttribute(AttributeKey<T> key) {
+ default <T> T resolveAttribute(AttributeRepository.AttributeKey<T> key) {
return resolveAttribute(this, key);
}

@@ -369,7 +370,7 @@ public interface Session
* @see Session#getFactoryManager()
* @see FactoryManager#resolveAttribute(FactoryManager, AttributeKey)
*/
- static <T> T resolveAttribute(Session session, AttributeKey<T> key) {
+ static <T> T resolveAttribute(Session session, AttributeRepository.AttributeKey<T> key) {
Objects.requireNonNull(key, "No key");
if (session == null) {
return null;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ba008331/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/SessionHelper.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/SessionHelper.java b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/SessionHelper.java
index f4d4f30..c02c039 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/SessionHelper.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/SessionHelper.java
@@ -23,8 +23,10 @@ import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
@@ -37,6 +39,7 @@ import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;

+import org.apache.sshd.common.AttributeRepository;
import org.apache.sshd.common.FactoryManager;
import org.apache.sshd.common.NamedResource;
import org.apache.sshd.common.PropertyResolver;
@@ -86,7 +89,7 @@ public abstract class SessionHelper extends AbstractKexFactoryManager implements
/**
* Session specific attributes
*/
- private final Map<AttributeKey<?>, Object> attributes = new ConcurrentHashMap<>();
+ private final Map<AttributeRepository.AttributeKey<?>, Object> attributes = new ConcurrentHashMap<>();

// Session timeout measurements
private long authTimeoutStart = System.currentTimeMillis();
@@ -145,20 +148,25 @@ public abstract class SessionHelper extends AbstractKexFactoryManager implements

@Override
@SuppressWarnings("unchecked")
- public <T> T getAttribute(AttributeKey<T> key) {
+ public <T> T getAttribute(AttributeRepository.AttributeKey<T> key) {
return (T) attributes.get(Objects.requireNonNull(key, "No key"));
}

@Override
+ public Collection<AttributeKey<?>> attributeKeys() {
+ return attributes.isEmpty() ? Collections.emptySet() : new HashSet<>(attributes.keySet());
+ }
+
+ @Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public <T> T computeAttributeIfAbsent(
- AttributeKey<T> key, Function<? super AttributeKey<T>, ? extends T> resolver) {
+ AttributeRepository.AttributeKey<T> key, Function<? super AttributeRepository.AttributeKey<T>, ? extends T> resolver) {
return (T) attributes.computeIfAbsent(Objects.requireNonNull(key, "No key"), (Function) resolver);
}

@Override
@SuppressWarnings("unchecked")
- public <T> T setAttribute(AttributeKey<T> key, T value) {
+ public <T> T setAttribute(AttributeRepository.AttributeKey<T> key, T value) {
return (T) attributes.put(
Objects.requireNonNull(key, "No key"),
Objects.requireNonNull(value, "No value"));
@@ -166,7 +174,7 @@ public abstract class SessionHelper extends AbstractKexFactoryManager implements

@Override
@SuppressWarnings("unchecked")
- public <T> T removeAttribute(AttributeKey<T> key) {
+ public <T> T removeAttribute(AttributeRepository.AttributeKey<T> key) {
return (T) attributes.remove(Objects.requireNonNull(key, "No key"));
}


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ba008331/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/CachingPublicKeyAuthenticator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/CachingPublicKeyAuthenticator.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/CachingPublicKeyAuthenticator.java
index 411a579..79cac90 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/CachingPublicKeyAuthenticator.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/CachingPublicKeyAuthenticator.java
@@ -23,7 +23,8 @@ import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

-import org.apache.sshd.common.AttributeStore.AttributeKey;
+import org.apache.sshd.common.AttributeRepository;
+import org.apache.sshd.common.AttributeRepository.AttributeKey;
import org.apache.sshd.common.RuntimeSshException;
import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.util.logging.AbstractLoggingBean;
@@ -37,9 +38,9 @@ import org.apache.sshd.server.session.ServerSession;
*/
public class CachingPublicKeyAuthenticator extends AbstractLoggingBean implements PublickeyAuthenticator {
/**
- * The {@link AttributeKey} used to store the cached authentication results on the session instance
+ * The {@link AttributeRepository.AttributeKey} used to store the cached authentication results on the session instance
*/
- public static final AttributeKey<Map<PublicKey, Boolean>> CACHE_ATTRIBUTE = new AttributeKey<>();
+ public static final AttributeRepository.AttributeKey<Map<PublicKey, Boolean>> CACHE_ATTRIBUTE = new AttributeRepository.AttributeKey<>();

protected final PublickeyAuthenticator authenticator;


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ba008331/sshd-core/src/test/java/org/apache/sshd/common/AttributeStoreTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/AttributeStoreTest.java b/sshd-core/src/test/java/org/apache/sshd/common/AttributeStoreTest.java
index e19265e..741045e 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/AttributeStoreTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/AttributeStoreTest.java
@@ -21,7 +21,7 @@ package org.apache.sshd.common;

import java.util.concurrent.atomic.AtomicInteger;

-import org.apache.sshd.common.AttributeStore.AttributeKey;
+import org.apache.sshd.common.AttributeRepository.AttributeKey;
import org.apache.sshd.common.channel.Channel;
import org.apache.sshd.common.session.Session;
import org.apache.sshd.util.test.BaseTestSupport;
@@ -39,7 +39,7 @@ import org.mockito.Mockito;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Category({ NoIoTestCase.class })
public class AttributeStoreTest extends BaseTestSupport {
- private static final AttributeKey<String> KEY = new AttributeKey<>();
+ private static final AttributeRepository.AttributeKey<String> KEY = new AttributeRepository.AttributeKey<>();

public AttributeStoreTest() {
super();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ba008331/sshd-mina/src/main/java/org/apache/sshd/common/io/mina/MinaService.java
----------------------------------------------------------------------
diff --git a/sshd-mina/src/main/java/org/apache/sshd/common/io/mina/MinaService.java b/sshd-mina/src/main/java/org/apache/sshd/common/io/mina/MinaService.java
index b6edacf..7139a5e 100644
--- a/sshd-mina/src/main/java/org/apache/sshd/common/io/mina/MinaService.java
+++ b/sshd-mina/src/main/java/org/apache/sshd/common/io/mina/MinaService.java
@@ -18,10 +18,9 @@
*/
package org.apache.sshd.common.io.mina;

-import java.util.Comparator;
+import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
-import java.util.TreeMap;

import org.apache.mina.core.RuntimeIoException;
import org.apache.mina.core.buffer.IoBuffer;
@@ -87,8 +86,8 @@ public abstract class MinaService extends AbstractCloseable implements org.apach
public Map<Long, org.apache.sshd.common.io.IoSession> getManagedSessions() {
IoService ioService = getIoService();
Map<Long, IoSession> managedMap = ioService.getManagedSessions();
- Map<Long, IoSession> mina = new TreeMap<>(managedMap);
- Map<Long, org.apache.sshd.common.io.IoSession> sessions = new TreeMap<>(Comparator.naturalOrder());
+ Map<Long, IoSession> mina = new HashMap<>(managedMap);
+ Map<Long, org.apache.sshd.common.io.IoSession> sessions = new HashMap<>(mina.size());
for (Long id : mina.keySet()) {
// Avoid possible NPE if the MinaSession hasn't been created yet
IoSession minaSession = mina.get(id);
@@ -209,10 +208,11 @@ public abstract class MinaService extends AbstractCloseable implements org.apach
}
}

- protected void handleConfigurationError(SocketSessionConfig config, String propName, Object propValue, RuntimeIoException t) {
+ protected void handleConfigurationError(
+ SocketSessionConfig config, String propName, Object propValue, RuntimeIoException t) {
Throwable e = GenericUtils.resolveExceptionCause(t);
log.warn("handleConfigurationError({}={}) failed ({}) to configure: {}",
- propName, propValue, e.getClass().getSimpleName(), e.getMessage());
+ propName, propValue, e.getClass().getSimpleName(), e.getMessage());
}

protected Integer getInteger(String property) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ba008331/sshd-mina/src/main/java/org/apache/sshd/common/io/mina/MinaSession.java
----------------------------------------------------------------------
diff --git a/sshd-mina/src/main/java/org/apache/sshd/common/io/mina/MinaSession.java b/sshd-mina/src/main/java/org/apache/sshd/common/io/mina/MinaSession.java
index 8a8ba07..0fa11db 100644
--- a/sshd-mina/src/main/java/org/apache/sshd/common/io/mina/MinaSession.java
+++ b/sshd-mina/src/main/java/org/apache/sshd/common/io/mina/MinaSession.java
@@ -99,7 +99,8 @@ public class MinaSession extends AbstractInnerCloseable implements IoSession {
protected Closeable getInnerCloseable() {
return new IoBaseCloseable() {
@SuppressWarnings("synthetic-access")
- private final DefaultCloseFuture future = new DefaultCloseFuture(MinaSession.this.toString(), lock);
+ private final DefaultCloseFuture future =
+ new DefaultCloseFuture(MinaSession.this.toString(), lock);

@SuppressWarnings("synthetic-access")
@Override
@@ -126,7 +127,8 @@ public class MinaSession extends AbstractInnerCloseable implements IoSession {
@SuppressWarnings("synthetic-access")
@Override
public org.apache.sshd.common.future.CloseFuture close(boolean immediately) {
- org.apache.mina.core.future.CloseFuture cf = immediately ? session.closeNow() : session.closeOnFlush();
+ org.apache.mina.core.future.CloseFuture cf =
+ immediately ? session.closeNow() : session.closeOnFlush();
cf.addListener(f -> future.setValue(Boolean.TRUE));
return future;
}
@@ -151,14 +153,15 @@ public class MinaSession extends AbstractInnerCloseable implements IoSession {
// NOTE !!! data buffer may NOT be re-used when method returns - at least until IoWriteFuture is signalled
public IoWriteFuture write(IoBuffer buffer) {
Future future = new Future(sessionWriteId, null);
- session.write(buffer).addListener((IoFutureListener<WriteFuture>) cf -> {
- Throwable t = cf.getException();
- if (t != null) {
- future.setException(t);
- } else {
- future.setWritten();
- }
- });
+ session.write(buffer)
+ .addListener((IoFutureListener<WriteFuture>) cf -> {
+ Throwable t = cf.getException();
+ if (t != null) {
+ future.setException(t);
+ } else {
+ future.setWritten();
+ }
+ });
return future;
}

@@ -183,6 +186,9 @@ public class MinaSession extends AbstractInnerCloseable implements IoSession {

@Override
public String toString() {
- return getClass().getSimpleName() + "[local=" + session.getLocalAddress() + ", remote=" + session.getRemoteAddress() + "]";
+ return getClass().getSimpleName()
+ + "[local=" + session.getLocalAddress()
+ + ", remote=" + session.getRemoteAddress()
+ + "]";
}
}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ba008331/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoConnector.java
----------------------------------------------------------------------
diff --git a/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoConnector.java b/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoConnector.java
index 6d90490..9230803 100644
--- a/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoConnector.java
+++ b/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoConnector.java
@@ -47,7 +47,6 @@ import io.netty.util.concurrent.GlobalEventExecutor;
* @author <a href="mailto:***@mina.apache.org">Apache MINA SSHD Project</a>
*/
public class NettyIoConnector extends NettyIoService implements IoConnector {
-
protected final Bootstrap bootstrap = new Bootstrap();

public NettyIoConnector(NettyIoServiceFactory factory, IoHandler handler) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ba008331/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoService.java
----------------------------------------------------------------------
diff --git a/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoService.java b/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoService.java
index a62326f..ea2ca8f 100644
--- a/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoService.java
+++ b/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoService.java
@@ -40,7 +40,8 @@ import io.netty.util.AttributeKey;
*/
public abstract class NettyIoService extends AbstractCloseable implements IoService {

- public static final AttributeKey<IoConnectFuture> CONNECT_FUTURE_KEY = AttributeKey.valueOf(IoConnectFuture.class.getName());
+ public static final AttributeKey<IoConnectFuture> CONNECT_FUTURE_KEY =
+ AttributeKey.valueOf(IoConnectFuture.class.getName());

protected final AtomicLong sessionSeq = new AtomicLong();
protected final Map<Long, IoSession> sessions = new ConcurrentHashMap<>();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/ba008331/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoSession.java
----------------------------------------------------------------------
diff --git a/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoSession.java b/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoSession.java
index f5864df..9cdeceb 100644
--- a/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoSession.java
+++ b/sshd-netty/src/main/java/org/apache/sshd/netty/NettyIoSession.java
@@ -51,7 +51,6 @@ import io.netty.util.Attribute;
* @author <a href="mailto:***@mina.apache.org">Apache MINA SSHD Project</a>
*/
public class NettyIoSession extends AbstractCloseable implements IoSession {
-
protected final Map<Object, Object> attributes = new HashMap<>();
protected final NettyIoService service;
protected final IoHandler handler;
@@ -163,7 +162,8 @@ public class NettyIoSession extends AbstractCloseable implements IoSession {
remoteAddr = channel.remoteAddress();
handler.sessionCreated(NettyIoSession.this);

- Attribute<IoConnectFuture> connectFuture = channel.attr(NettyIoService.CONNECT_FUTURE_KEY);
+ Attribute<IoConnectFuture> connectFuture =
+ channel.attr(NettyIoService.CONNECT_FUTURE_KEY);
IoConnectFuture future = connectFuture.get();
if (future != null) {
future.setSession(NettyIoSession.this);
@@ -186,7 +186,6 @@ public class NettyIoSession extends AbstractCloseable implements IoSession {
}

protected static class DefaultIoWriteFuture extends AbstractIoWriteFuture {
-
public DefaultIoWriteFuture(Object id, Object lock) {
super(id, lock);
}
@@ -196,7 +195,6 @@ public class NettyIoSession extends AbstractCloseable implements IoSession {
* Simple netty adapter to use as a bridge.
*/
protected class Adapter extends ChannelInboundHandlerAdapter {
-
public Adapter() {
super();
}
l***@apache.org
2018-10-31 05:03:12 UTC
Permalink
[SSHD-859] Provide client session connection context that is propagated to the SSH session


Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/403402f5
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/403402f5
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/403402f5

Branch: refs/heads/master
Commit: 403402f58a3b5b2893ac016c1703000494ea7e0b
Parents: ba00833
Author: Lyor Goldstein <***@apache.org>
Authored: Tue Oct 30 12:20:59 2018 +0200
Committer: Lyor Goldstein <***@apache.org>
Committed: Wed Oct 31 07:02:56 2018 +0200

----------------------------------------------------------------------
CHANGES.md | 26 ++
README.md | 407 ++++++++++++++-----
pom.xml | 1 +
.../java/org/apache/sshd/cli/CliSupport.java | 5 +-
.../sshd/cli/client/SshClientCliSupport.java | 3 +-
.../apache/sshd/cli/client/SshKeyScanMain.java | 4 +-
.../apache/sshd/cli/server/SshFsMounter.java | 4 +-
.../client/config/hosts/KnownHostHashValue.java | 6 +-
.../java/org/apache/sshd/common/Closeable.java | 8 +-
.../org/apache/sshd/common/SshConstants.java | 2 +
.../common/config/ConfigFileReaderSupport.java | 1 -
.../config/hosts/KnownHostHashEntryTest.java | 6 +-
.../config/hosts/KnownHostHashValueTest.java | 6 +-
.../java/org/apache/sshd/client/SshClient.java | 41 +-
.../client/session/AbstractClientSession.java | 10 +
.../sshd/client/session/ClientSession.java | 7 +
.../client/session/ClientSessionCreator.java | 120 +++++-
.../AbstractSimpleClientSessionCreator.java | 45 ++
.../client/simple/SimpleClientConfigurator.java | 4 +-
.../org/apache/sshd/common/io/IoAcceptor.java | 1 -
.../org/apache/sshd/common/io/IoConnector.java | 7 +-
.../sshd/common/io/IoServiceEventListener.java | 13 +-
.../sshd/common/io/nio2/Nio2Connector.java | 25 +-
.../common/session/helpers/SessionHelper.java | 1 +
.../sshd/server/forward/TcpipServerChannel.java | 2 +-
.../sshd/client/session/ClientSessionTest.java | 55 ++-
.../sshd/common/io/mina/MinaConnector.java | 24 +-
.../org/apache/sshd/netty/NettyIoConnector.java | 18 +-
.../org/apache/sshd/netty/NettyIoService.java | 3 +
.../subsystem/sftp/SftpFileSystemProvider.java | 8 +-
.../sftp/ApacheSshdSftpSessionFactory.java | 4 +-
31 files changed, 679 insertions(+), 188 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/CHANGES.md
----------------------------------------------------------------------
diff --git a/CHANGES.md b/CHANGES.md
new file mode 100644
index 0000000..f518b79
--- /dev/null
+++ b/CHANGES.md
@@ -0,0 +1,26 @@
+# Since version 2.1.0
+
+## Major code re-factoring
+
+* `AttributeKey` moved from `AttributeStore` to `AttributeRepository`
+
+* `getAttribute` moved from `AttributeStore` to `AttributeRepository` + added
+`attributeKeys` method.
+
+* `DEFAULT_PORT` moved from `SshConfigFileReader` to `SshConstants`
+
+* Added new `sessionDisconnect` method to `SessionListener`
+
+* `ReservedSessionMessagesHandler#handleUnimplementedMessage` has an extra `cmd` argument
+and is called both for `SSH_MSG_UNIMPLEMENTED` and also for any unexpected/unrecognized command
+encountered during the session message processing loop.
+
+* `ClientSessionCreator` has extra `connect` methods with an `AttributeRepository`
+connection context argument
+
+* `connectionEstablished` and `abortEstablishedConnection` methods of `IoServiceEventListener`
+accept also an `AttributeRepository` connection context argument (propagated from the
+`ClientSessionCreator#connect` invocation).
+
+* `FilePasswordProvider` has an extra method (`handleDecodeAttemptResult`) that enables
+user to try and repeat an encrypted private key decoding using a different password.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index dd17a91..e739438 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,10 @@
![Apache MINA SSHD](Loading Image... "Apache MINA SSHD")
# Apache MINA SSHD

-Apache SSHD is a 100% pure java library to support the SSH protocols on both the client and server side. This library can leverage [Apache MINA](http://mina.apache.org), a scalable and high performance asynchronous IO library. SSHD does not really aim at being a replacement for the SSH client or SSH server from Unix operating systems, but rather provides support for Java based applications requiring SSH support.
+Apache SSHD is a 100% pure java library to support the SSH protocols on both the client and server side. This library can
+leverage [Apache MINA](http://mina.apache.org), a scalable and high performance asynchronous IO library. SSHD does not really
+aim at being a replacement for the SSH client or SSH server from Unix operating systems, but rather provides support for Java
+based applications requiring SSH support.

# Core requirements

@@ -11,7 +14,8 @@ Apache SSHD is a 100% pure java library to support the SSH protocols on both the
* [Slf4j](http://www.slf4j.org/)


-The code only requires the core abstract [slf4j-api](https://mvnrepository.com/artifact/org.slf4j/slf4j-api) module. The actual implementation of the logging API can be selected from the many existing adaptors.
+The code only requires the core abstract [slf4j-api](https://mvnrepository.com/artifact/org.slf4j/slf4j-api) module. The actual
+implementation of the logging API can be selected from the many existing adaptors.

# Optional dependencies

@@ -19,19 +23,25 @@ The code only requires the core abstract [slf4j-api](https://mvnrepository.com/a
## [Bouncy Castle](https://www.bouncycastle.org/)


-Required mainly for writing keys to PEM files or for special keys/ciphers/etc. that are not part of the standard [Java Cryptography Extension](https://en.wikipedia.org/wiki/Java_Cryptography_Extension). See [Java Cryptography Architecture (JCA) Reference Guide](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html) for key classes and explanations as to how _Bouncy Castle_ is plugged in (other security providers).
+Required mainly for writing keys to PEM files or for special keys/ciphers/etc. that are not part of the standard
+[Java Cryptography Extension](https://en.wikipedia.org/wiki/Java_Cryptography_Extension). See
+[Java Cryptography Architecture (JCA) Reference Guide](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html)
+for key classes and explanations as to how _Bouncy Castle_ is plugged in (other security providers).

-**Caveat**: If _Bouncy Castle_ modules are registered, then the code will use its implementation of the ciphers, keys, signatures, etc. rather than the default JCE provided in the JVM.
+**Caveat**: If _Bouncy Castle_ modules are registered, then the code will use its implementation of the ciphers,
+keys, signatures, etc. rather than the default JCE provided in the JVM.

**Note:**

- The security provider can also be registered for keys/ciphers/etc. that are already supported by the standard JCE as a **replacement** for them.


- - The _BouncyCastle_ code can also be used to load keys from PEM files instead or in parallel with the built-in code that already parses the standard PEM formats for the default JCE supported key types.
+ - The _BouncyCastle_ code can also be used to load keys from PEM files instead or in parallel with the built-in code that
+already parses the standard PEM formats for the default JCE supported key types.


-- One can use the `BouncyCastleKeyPairResourceParser` to load standard PEM files instead of the core one - either directly or via `SecurityUtils#setKeyPairResourceParser` for **global** usage - even without registering or enabling the provider.
+- One can use the `BouncyCastleKeyPairResourceParser` to load standard PEM files instead of the core one - either directly
+or via `SecurityUtils#setKeyPairResourceParser` for **global** usage - even without registering or enabling the provider.


- The required _Maven_ module(s) are defined as `optional` so must be added as an **explicit** dependency in order to be included in the classpath:
@@ -138,7 +148,10 @@ before starting the client/server. Therefore, the default selection process desc
## [ed25519-java](https://github.com/str4d/ed25519-java)


-Required for supporting [ssh-ed25519](https://tools.ietf.org/html/draft-bjh21-ssh-ed25519-02) keys and [ed25519-sha-512](https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-02) signatures. **Note:** the required Maven module(s) are defined as `optional` so must be added as an **explicit** dependency in order to be included in the classpath:
+Required for supporting [ssh-ed25519](https://tools.ietf.org/html/draft-bjh21-ssh-ed25519-02) keys
+and [ed25519-sha-512](https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-02) signatures. **Note:**
+the required Maven module(s) are defined as `optional` so must be added as an **explicit** dependency in
+order to be included in the classpath:


```xml
@@ -181,7 +194,9 @@ The code contains support for reading _ed25519_ [OpenSSH formatted private keys]

# Set up an SSH client in 5 minutes

-SSHD is designed to easily allow setting up and using an SSH client in a few simple steps. The client needs to be configured and then started before it can be used to connect to an SSH server. There are a few simple steps for creating a client instance - for more details refer to the `SshClient` class.
+SSHD is designed to easily allow setting up and using an SSH client in a few simple steps. The client needs to be configured
+and then started before it can be used to connect to an SSH server. There are a few simple steps for creating a client
+instance - for more details refer to the `SshClient` class.

## Creating an instance of the `SshClient` class

@@ -193,7 +208,10 @@ This is simply done by calling

```

-The call will create an instance with a default configuration suitable for most use cases - including ciphers, compression, MACs, key exchanges, signatures, etc... If your code requires some special configuration, you can look at the code for `setupDefaultClient` and `checkConfig` as a reference for available options and configure the SSH client the way you need.
+The call will create an instance with a default configuration suitable for most use cases - including ciphers,
+compression, MACs, key exchanges, signatures, etc... If your code requires some special configuration, one can
+look at the code for `setupDefaultClient` and `checkConfig` as a reference for available options and configure
+the SSH client the way you need.

## Set up client side security

@@ -201,15 +219,21 @@ The SSH client contains some security related configuration that one needs to co

### `ServerKeyVerifier`

-`client.setServerKeyVerifier(...);` sets up the server key verifier. As part of the SSH connection initialization protocol, the server proves its "identity" by presenting a public key. The client can examine the key (e.g., present it to the user via some UI) and decide whether to trust the server and continue with the connection setup. By default the client is initialized with an `AcceptAllServerKeyVerifier` that simply logs a warning that an un-verified server key was accepted. There are other out-of-the-box verifiers available in the code:
+`client.setServerKeyVerifier(...);` sets up the server key verifier. As part of the SSH connection initialization
+protocol, the server proves its "identity" by presenting a public key. The client can examine the key (e.g., present
+it to the user via some UI) and decide whether to trust the server and continue with the connection setup. By default
+the client is initialized with an `AcceptAllServerKeyVerifier` that simply logs a warning that an un-verified server
+key was accepted. There are other out-of-the-box verifiers available in the code:

-* `RejectAllServerKeyVerifier` - rejects all server key - usually used in tests or as a fallback verifier if none of it predecesors validated the server key
+* `RejectAllServerKeyVerifier` - rejects all server key - usually used in tests or as a fallback verifier if none
+of it predecesors validated the server key


* `RequiredServerKeyVerifier` - accepts only **one** specific server key (similar to certificate pinning for SSL)


-* `KnownHostsServerKeyVerifier` - uses the [known_hosts](https://en.wikibooks.org/wiki/OpenSSH/Client_Configuration_Files#Public_Keys_from_other_Hosts_.E2.80.93_.7E.2F.ssh.2Fknown_hosts) file to validate the server key. One can use this class + some existing code to **update** the file when new servers are detected and their keys are accepted.
+* `KnownHostsServerKeyVerifier` - uses the [known_hosts](https://en.wikibooks.org/wiki/OpenSSH/Client_Configuration_Files#Public_Keys_from_other_Hosts_.E2.80.93_.7E.2F.ssh.2Fknown_hosts)
+file to validate the server key. One can use this class + some existing code to **update** the file when new servers are detected and their keys are accepted.


Of course, one can implement the verifier in whatever other manner is suitable for the specific code needs.
@@ -243,7 +267,11 @@ in [RFC 4253 section 4.2](https://tools.ietf.org/html/rfc4253#section-4.2).

## Using the `SshClient` to connect to a server

-Once the `SshClient` instance is properly configured it needs to be `start()`-ed in order to connect to a server. **Note:** one can use a single `SshClient` instance to connnect to multiple servers as well as modifying the default configuration (ciphers, MACs, keys, etc.) on a per-session manner (see more in the *Advanced usage* section). Furthermore, one can change almost any configured `SshClient` parameter - although its influence on currently established sessions depends on the actual changed configuration. Here is how a typical usage would look like
+Once the `SshClient` instance is properly configured it needs to be `start()`-ed in order to connect to a server.
+**Note:** one can use a single `SshClient` instance to connnect to multiple servers as well as modifying the default
+configuration (ciphers, MACs, keys, etc.) on a per-session manner (see more in the *Advanced usage* section).
+Furthermore, one can change almost any configured `SshClient` parameter - although its influence on currently established
+sessions depends on the actual changed configuration. Here is how a typical usage would look like

```java

@@ -281,7 +309,9 @@ Once the `SshClient` instance is properly configured it needs to be `start()`-ed

# Embedding an SSHD server instance in 5 minutes

-SSHD is designed to be easily embedded in your application as an SSH server. The embedded SSH server needs to be configured before it can be started. Essentially, there are a few simple steps for creating the server - for more details refer to the `SshServer` class.
+SSHD is designed to be easily embedded in your application as an SSH server. The embedded SSH server needs
+to be configured before it can be started. Essentially, there are a few simple steps for creating the
+server - for more details refer to the `SshServer` class.

## Creating an instance of the `SshServer` class

@@ -293,22 +323,36 @@ Creating an instance of `SshServer` is as simple as creating a new object

```

-It will configure the server with sensible defaults for ciphers, macs, key exchange algorithm, etc... If different behavior is required, one should consult the code of the `setUpDefaultServer` as well as `checkConfig` methods as a reference for available options and configure the SSH server the way it is needed.
+It will configure the server with sensible defaults for ciphers, macs, key exchange algorithm, etc...
+If different behavior is required, one should consult the code of the `setUpDefaultServer` as well as
+`checkConfig` methods as a reference for available options and configure the SSH server the way it is needed.

## Configuring the server instance

There are a few things that need to be configured on the server before being able to actually use it:

-* Port - `sshd.setPort(22);` - sets the listen port for the server instance. If not set explicitly then a **random** free port is selected by the O/S. In any case, once the server is `start()`-ed one can query the instance as to the assigned port via `sshd.getPort()`.
+* Port - `sshd.setPort(22);` - sets the listen port for the server instance. If not set explicitly then a
+**random** free port is selected by the O/S. In any case, once the server is `start()`-ed one can query the
+instance as to the assigned port via `sshd.getPort()`.


-In this context, the listen bind address can also be specified explicitly via `sshd.setHost(...some IP address...)` that causes the server to bind to a specific network address rather than **all** addresses (the default). Using `"0.0.0.0"` as the bind address is also tantamount to binding to all addresses.
+In this context, the listen bind address can also be specified explicitly via `sshd.setHost(...some IP address...)`
+that causes the server to bind to a specific network address rather than **all** addresses (the default). Using
+`"0.0.0.0"` as the bind address is also tantamount to binding to all addresses.


-* `KeyPairProvider` - `sshd.setKeyPairProvider(...);` - sets the host's private keys used for key exchange with clients as well as representing the host's "identities". There are several choices - one can load keys from standard PEM files or generate them in the code. It's usually a good idea to save generated keys, so that if the SSHD server is restarted, the same keys will be used to authenticate the server and avoid the warning the clients might get if the host keys are modified. **Note**: saving key files in PEM format requires that the [Bouncy Castle](https://www.bouncycastle.org/) supporting artifacts be available in the code's classpath.
+* `KeyPairProvider` - `sshd.setKeyPairProvider(...);` - sets the host's private keys used for key exchange with
+clients as well as representing the host's "identities". There are several choices - one can load keys from standard
+PEM files or generate them in the code. It's usually a good idea to save generated keys, so that if the SSHD server
+is restarted, the same keys will be used to authenticate the server and avoid the warning the clients might get if
+the host keys are modified. **Note**: saving key files in PEM format requires that the [Bouncy Castle](https://www.bouncycastle.org/)
+supporting artifacts be available in the code's classpath.


-* `ShellFactory` - That's the part one usually has to write to customize the SSHD server. The shell factory will be used to create a new shell each time a user logs in and wants to run an interactive shell. SSHD provides a simple implementation that you can use if you want. This implementation will create a process and delegate everything to it, so it's mostly useful to launch the OS native shell. E.g.,
+* `ShellFactory` - That's the part one usually has to write to customize the SSHD server. The shell factory will
+be used to create a new shell each time a user logs in and wants to run an interactive shell. SSHD provides a simple
+implementation that you can use if you want. This implementation will create a process and delegate everything to it,
+so it's mostly useful to launch the OS native shell. E.g.,


```java
@@ -318,10 +362,12 @@ In this context, the listen bind address can also be specified explicitly via `s
```


-There is an out-of-the-box `InteractiveProcessShellFactory` that detects the O/S and spawns the relevant shell. Note that the `ShellFactory` is not required. If none is configured, any request for an interactive shell will be denied to clients.
+There is an out-of-the-box `InteractiveProcessShellFactory` that detects the O/S and spawns the relevant shell. Note
+that the `ShellFactory` is not required. If none is configured, any request for an interactive shell will be denied to clients.


-* `CommandFactory` - The `CommandFactory` provides the ability to run a **single** direct command at a time instead of an interactive session (it also uses a **different** channel type than shells). It can be used **in addition** to the `ShellFactory`.
+* `CommandFactory` - The `CommandFactory` provides the ability to run a **single** direct command at a time instead
+of an interactive session (it also uses a **different** channel type than shells). It can be used **in addition** to the `ShellFactory`.


SSHD provides a `CommandFactory` to support SCP that can be configured in the following way:
@@ -333,7 +379,9 @@ SSHD provides a `CommandFactory` to support SCP that can be configured in the fo

```

-One can also use the `ScpCommandFactory` on top of one's own `CommandFactory` by placing the command factory as a **delegate** of the `ScpCommandFactory`. The `ScpCommandFactory` will intercept SCP commands and execute them by itself, while passing all other commands to the delegate `CommandFactory`
+One can also use the `ScpCommandFactory` on top of one's own `CommandFactory` by placing the command factory as a **delegate**
+of the `ScpCommandFactory`. The `ScpCommandFactory` will intercept SCP commands and execute them by itself, while passing all
+other commands to the delegate `CommandFactory`


```java
@@ -366,10 +414,12 @@ These custom classes can be configured on the SSHD server using the respective s

```

-Several useful implementations are available that can be used as-is or extended in order to provide some custom behavior. In any case, the default initializations are:
+Several useful implementations are available that can be used as-is or extended in order to provide some custom behavior. In any
+case, the default initializations are:

* `DefaultAuthorizedKeysAuthenticator` - uses the _authorized_keys_ file the same way as the SSH daemon does
-* `DefaultKeyboardInteractiveAuthenticator` - for password-based or interactive authentication. **Note:** this authenticator requires a `PasswordAuthenticator` to be configured since it delegates some of the functionality to it.
+* `DefaultKeyboardInteractiveAuthenticator` - for password-based or interactive authentication. **Note:** this authenticator
+requires a `PasswordAuthenticator` to be configured since it delegates some of the functionality to it.

## Configuring ciphers, macs, digest...

@@ -385,71 +435,115 @@ Configuring supported factories can be done with the following code:

```

-One can configure other security components using built-in factories the same way. It is important to remember though that the **order** of the factories is important as it affects the key exchange phase where the client and server decide what options to use out of each peer's reported preferences.
+One can configure other security components using built-in factories the same way. It is important to remember though
+that the **order** of the factories is important as it affects the key exchange phase where the client and server decide
+what options to use out of each peer's reported preferences.

## Starting the Server

-Once we have configured the server, one need only call `sshd.start();`. **Note**: once the server is started, all of the configurations (except the port) can still be *overridden* while the server is running (caveat emptor). In such cases, only **new** clients that connect to the server after the change will be affected - with the exception of the negotiation options (keys, macs, ciphers, etc...) which take effect the next time keys are re-exchanged, that can affect live sessions and not only new ones.
+Once we have configured the server, one need only call `sshd.start();`. **Note**: once the server is started, all of the
+configurations (except the port) can still be *overridden* while the server is running (caveat emptor). In such cases,
+only **new** clients that connect to the server after the change will be affected - with the exception of the negotiation
+options (keys, macs, ciphers, etc...) which take effect the next time keys are re-exchanged, that can affect live sessions
+and not only new ones.

# SSH functionality breakdown

## Security providers setup

While the code supports _BouncyCastle_ and _EdDSA_ security providers out-of-the-box,
-it also provides a way to [add security providers](https://issues.apache.org/jira/browse/SSHD-713) via the `SecurityProviderRegistrar` interface implementation. In order to add support for a new security provider one needs to implement the registrar interface and make the code aware of it.
+it also provides a way to [add security providers](https://issues.apache.org/jira/browse/SSHD-713) via the `SecurityProviderRegistrar`
+interface implementation. In order to add support for a new security provider one needs to implement the registrar interface and make
+the code aware of it.

### Default/built-in security provider registrars

-The code contains built-in security provider registrars for _BouncyCastle_ and _EdDSA_ (a.k.a. `ed25519`). It automatically detects the existence of the required artifacts (since they are optional dependencies) and executes the respective security provider registration. This behavior is controlled by the `org.apache.sshd.security.registrars` system property. This property contains a comma-separated list of **fully-qualified** class names implementing the `SecurityProviderRegistrar` interface and assumed to contain a default **public** no-arguments constructor. The code automatically parses the list and attempts to instantiate and invoke the registrar.
+The code contains built-in security provider registrars for _BouncyCastle_ and _EdDSA_ (a.k.a. `ed25519`). It automatically detects
+the existence of the required artifacts (since they are optional dependencies) and executes the respective security provider registration.
+This behavior is controlled by the `org.apache.sshd.security.registrars` system property. This property contains a comma-separated list
+of **fully-qualified** class names implementing the `SecurityProviderRegistrar` interface and assumed to contain a default **public**
+no-arguments constructor. The code automatically parses the list and attempts to instantiate and invoke the registrar.

**Note:**


-- The registration code automatically parses the configured registrars list and instantiates them. In this context, one can use the special `none` value to indicate that the code should not attempt to automatically register the default providers.
+- The registration code automatically parses the configured registrars list and instantiates them. In this context, one can use the
+special `none` value to indicate that the code should not attempt to automatically register the default providers.

-- A registrar instance might be created but eventually discarded and not invoked if it is disabled, unsupported or already registered programmatically via `SecurityUtils#registerSecurityProvider`.
+- A registrar instance might be created but eventually discarded and not invoked if it is disabled, unsupported or already registered
+programmatically via `SecurityUtils#registerSecurityProvider`.


-- The registration attempt is a **one-shot** deal - i.e., once the registrars list is parsed and successfully resolved, any modifications to the registered security providers must be done **programatically**. One can call `SecurityUtils#isRegistrationCompleted()` to find out if the registration phase has already been executed.
+- The registration attempt is a **one-shot** deal - i.e., once the registrars list is parsed and successfully resolved, any modifications
+to the registered security providers must be done **programatically**. One can call `SecurityUtils#isRegistrationCompleted()` to find out
+if the registration phase has already been executed.


-- The registrars are consulted in the same **order** as they were initially registered - either programmatically or via the system property configuration. Therefore, if two or more registrars support the same algorithm, then the earlier registered one will be used.
+- The registrars are consulted in the same **order** as they were initially registered - either programmatically or via the system property
+configuration. Therefore, if two or more registrars support the same algorithm, then the earlier registered one will be used.


-- If no matching registrar was found, then the default security provider is used. If none set, the JCE defaults are invoked. The default security provider can be configured either via the `org.apache.sshd.security.defaultProvider` system property or by programmatically invoking `SecurityUtils#setDefaultProviderChoice`. **Note:** if the system property option is used, then it is assumed to contain a security provider's **name** (rather than its `Provider` class name...).
+- If no matching registrar was found, then the default security provider is used. If none set, the JCE defaults are invoked. The default
+security provider can be configured either via the `org.apache.sshd.security.defaultProvider` system property or by programmatically
+invoking `SecurityUtils#setDefaultProviderChoice`. **Note:** if the system property option is used, then it is assumed to contain a security
+provider's **name** (rather than its `Provider` class name...).


-- If programmatic selection of the default security provider choice is required, then the code flow must ensure that `SecurityUtils#setDefaultProviderChoice` is called before **any** security entity (e.g., ciphers, keys, etc...) are required. Theoretically, one could change the choice after ciphers have been been requested but before keys were generated (e.g....), but it is dangerous and may yield unpredictable behavior.
+- If programmatic selection of the default security provider choice is required, then the code flow must ensure that
+`SecurityUtils#setDefaultProviderChoice` is called before **any** security entity (e.g., ciphers, keys, etc...) are
+required. Theoretically, one could change the choice after ciphers have been been requested but before keys were generated
+(e.g....), but it is dangerous and may yield unpredictable behavior.


### Implementing a new security provider registrar

-See `AbstractSecurityProviderRegistrar` helper class for a default implementation of most of the required functionality, as well as the existing implementations for _BouncyCastle_ and _EdDSA_ for examples of how to implement it. The most important issues to consider when adding such an implementation are:
+See `AbstractSecurityProviderRegistrar` helper class for a default implementation of most of the required functionality, as
+well as the existing implementations for _BouncyCastle_ and _EdDSA_ for examples of how to implement it. The most important
+issues to consider when adding such an implementation are:

-* Try using reflection API to detect the existence of the registered provider class and/or instantiate it. The main reason for this recommendation is that it isolates the code from a direct dependency on the provider's classes and makes class loading issue less likely.
+* Try using reflection API to detect the existence of the registered provider class and/or instantiate it. The main reason
+for this recommendation is that it isolates the code from a direct dependency on the provider's classes and makes class loading
+issue less likely.


-* Decide whether to use the provider's name or instance when creating security related entities such as ciphers, keys, etc... **Note:** the default preference is to use the provider name, thus registering via `Security.addProvider` call. In order to change that, either register the instance yourself or override the `isNamedProviderUsed` method. In this context, **cache** the generated `Provider` instance if the instance rather than the name is used. **Note:** using only the provider instance instead of the name is a rather new feature and has not been fully tested. It is possible though to decide and use it anyway as long as it can be configurably disabled.
+* Decide whether to use the provider's name or instance when creating security related entities such as ciphers, keys, etc...
+**Note:** the default preference is to use the provider name, thus registering via `Security.addProvider` call. In order to
+change that, either register the instance yourself or override the `isNamedProviderUsed` method. In this context, **cache**
+the generated `Provider` instance if the instance rather than the name is used. **Note:** using only the provider instance
+instead of the name is a rather new feature and has not been fully tested. It is possible though to decide and use it anyway
+as long as it can be configurably disabled.


-* The default implementation provides fine-grained control over the declared supported security entities - ciphers, signatures, key generators, etc... By default, it is done via consulting a system property composed of `org.apache.sshd.security.provider`, followed by the security provider name and the relevant security entity - e.g., `org.apache.sshd.security.provider.BC.KeyFactory` is assumed to contain a comma-separated list of supported `KeyFactory` algorithms.
+* The default implementation provides fine-grained control over the declared supported security entities - ciphers, signatures,
+key generators, etc... By default, it is done via consulting a system property composed of `org.apache.sshd.security.provider`,
+followed by the security provider name and the relevant security entity - e.g., `org.apache.sshd.security.provider.BC.KeyFactory`
+is assumed to contain a comma-separated list of supported `KeyFactory` algorithms.

**Note:**


-* The same naming convention can be used to enable/disable the registrar - even if supported - e.g., `org.apache.sshd.security.provider.BC.enabled=false` disables the _BouncyCastle_ registrar.
+* The same naming convention can be used to enable/disable the registrar - even if supported - e.g.,
+`org.apache.sshd.security.provider.BC.enabled=false` disables the _BouncyCastle_ registrar.


-* One can use `all` or `*` to specify that all entities of the specified type are supported - e.g., `org.apache.sshd.security.provider.BC.MessageDigest=all`. In this context, one can override the `getDefaultSecurityEntitySupportValue` method if no fine-grained configuration is required per-entity type,
+* One can use `all` or `*` to specify that all entities of the specified type are supported - e.g.,
+`org.apache.sshd.security.provider.BC.MessageDigest=all`. In this context, one can override the
+`getDefaultSecurityEntitySupportValue` method if no fine-grained configuration is required per-entity type,


* The result of an `isXxxSupported` call is/should be **cached** (see `AbstractSecurityProviderRegistrar`).


-* For ease of implementation, all support query calls are routed to the `isSecurityEntitySupported` method so that one can concentrate all the configuration in a single method. This is done for **convenience** reasons - the code will invoke the correct support query as per the type of entity it needs. E.g., if it needs a cipher, it will invoke `isCipherSupported` - which by default will invoke `isSecurityEntitySupported` with the `Cipher` class as its argument.
+* For ease of implementation, all support query calls are routed to the `isSecurityEntitySupported` method
+so that one can concentrate all the configuration in a single method. This is done for **convenience**
+reasons - the code will invoke the correct support query as per the type of entity it needs. E.g., if it
+needs a cipher, it will invoke `isCipherSupported` - which by default will invoke `isSecurityEntitySupported`
+with the `Cipher` class as its argument.


-* Specifically for **ciphers** the argument to the support query contains a **transformation** (e.g., `AES/CBC/NoPadding`) so one should take that into account when parsing the input argument to decide which cipher is referenced - see `SecurityProviderRegistrar.getEffectiveSecurityEntityName(Class<?>, String)` helper method
+* Specifically for **ciphers** the argument to the support query contains a **transformation** (e.g., `AES/CBC/NoPadding`)
+so one should take that into account when parsing the input argument to decide which cipher is referenced - see
+`SecurityProviderRegistrar.getEffectiveSecurityEntityName(Class<?>, String)` helper method


## `FileSystemFactory` usage
@@ -539,8 +633,8 @@ that it has finished. The framework will then take care of propagating the exit
the command. **Note**: the command may not assume that it is done until its `destroy()` method is called - i.e., it should not
release or null-ify any of its internal state even if `onExit()` was called.

-Upon calling the `onExit` method the code sends an [SSH_MSG_CHANNEL_EOF](https://tools.ietf.org/html/rfc4254#section-5.3) message, and the provided result status code
-is sent as an `exit-status` message as described in [RFC4254 - section 6.10](https://tools.ietf.org/html/rfc4254#section-6.10).
+Upon calling the `onExit` method the code sends an [SSH_MSG_CHANNEL_EOF](https://tools.ietf.org/html/rfc4254#section-5.3) message,
+and the provided result status code is sent as an `exit-status` message as described in [RFC4254 - section 6.10](https://tools.ietf.org/html/rfc4254#section-6.10).
The provided message is simply logged at DEBUG level.

```java
@@ -699,8 +793,8 @@ monitoring and intervention on the accessed local files.

## SFTP

-Both client-side and server-side SFTP are supported. Starting from version 2.0, the SFTP related code is located in the `sshd-sftp`, so you need to add
-this additional dependency to your maven project:
+Both client-side and server-side SFTP are supported. Starting from version 2.0, the SFTP related code is located
+in the `sshd-sftp` artifact, so one needs to add this additional dependency to one's maven project:

```xml

@@ -998,9 +1092,13 @@ Furthermore several [OpenSSH SFTP extensions](https://github.com/openssh/openssh
* `***@openssh.com`


-On the server side, the reported standard extensions are configured via the `SftpSubsystem.CLIENT_EXTENSIONS_PROP` configuration key, and the _OpenSSH_ ones via the `SftpSubsystem.OPENSSH_EXTENSIONS_PROP`.
+On the server side, the reported standard extensions are configured via the `SftpSubsystem.CLIENT_EXTENSIONS_PROP` configuration
+key, and the _OpenSSH_ ones via the `SftpSubsystem.OPENSSH_EXTENSIONS_PROP`.

-On the client side, all the supported extensions are classes that implement `SftpClientExtension`. These classes can be used to query the client whether the remote server supports the specific extension and then obtain a parser for its contents. Users can easily add support for more extensions in a similar manner as the existing ones by implementing an appropriate `ExtensionParser` and then registering it at the `ParserUtils` - see the existing ones for details how this can be achieved.
+On the client side, all the supported extensions are classes that implement `SftpClientExtension`. These classes can be used
+to query the client whether the remote server supports the specific extension and then obtain a parser for its contents. Users
+can easily add support for more extensions in a similar manner as the existing ones by implementing an appropriate `ExtensionParser`
+and then registering it at the `ParserUtils` - see the existing ones for details how this can be achieved.


```java
@@ -1061,24 +1159,33 @@ One can skip all the conditional code if a specific known extension is required:

If an exception is thrown during processing of an SFTP command, then the exception is translated into a `SSH_FXP_STATUS` message
using a registered `SftpErrorStatusDataHandler`. The default implementation provides a short description of the failure based on the thrown
-exception type. However, users may override it when creating the `SftpSubsystemFactory` and provide their own codes and/or messages - e.g., for debugging one can register a `DetailedSftpErrorStatusDataHandler` (see `sshd-contrib`) that "leaks" more information in the generated message.
+exception type. However, users may override it when creating the `SftpSubsystemFactory` and provide their own codes and/or messages - e.g.,
+for debugging one can register a `DetailedSftpErrorStatusDataHandler` (see `sshd-contrib`) that "leaks" more information in the generated message.


## Port forwarding

### Standard port forwarding

-Port forwarding as specified in [RFC 4254 - section 7](https://tools.ietf.org/html/rfc4254#section-7) is fully supported by the client and server. From the client side, this capability is exposed via the `start/stopLocal/RemotePortForwarding` method. The key player in this capability is the configured `ForwardingFilter` that controls this feature - on **both** sides - client and server. By default, this capability is **disabled** - i.e., the user must provide an implementation and call the appropriate `setForwardingFilter` method on the client/server.
+Port forwarding as specified in [RFC 4254 - section 7](https://tools.ietf.org/html/rfc4254#section-7) is fully
+supported by the client and server. From the client side, this capability is exposed via the `start/stopLocal/RemotePortForwarding`
+method. The key player in this capability is the configured `ForwardingFilter` that controls this feature - on **both** sides - client
+and server. By default, this capability is **disabled** - i.e., the user must provide an implementation and call the appropriate
+`setForwardingFilter` method on the client/server.

-The code contains 2 simple implementations - an accept-all and a reject-all one that can be used for these trivial policies. **Note:** setting a _null_ filter is equivalent to rejecting all such attempts.
+The code contains 2 simple implementations - an accept-all and a reject-all one that can be used for these trivial
+policies. **Note:** setting a _null_ filter is equivalent to rejecting all such attempts.

### SOCKS

-The code implements a [SOCKS](https://en.wikipedia.org/wiki/SOCKS) proxy for versions 4 and 5. The proxy capability is invoked via the `start/stopDynamicPortForwarding` methods.
+The code implements a [SOCKS](https://en.wikipedia.org/wiki/SOCKS) proxy for versions 4 and 5. The proxy capability is
+invoked via the `start/stopDynamicPortForwarding` methods.

### Proxy agent

-The code provides to some extent an SSH proxy agent via the available `SshAgentFactory` implementations. As of latest version both [Secure Shell Authentication Agent Protocol Draft 02](https://tools.ietf.org/html/draft-ietf-secsh-agent-02) and its [OpenSSH](https://www.libssh.org/features/) equivalent are supported. **Note:** in order to support this feature the
+The code provides to some extent an SSH proxy agent via the available `SshAgentFactory` implementations. As of latest version
+both [Secure Shell Authentication Agent Protocol Draft 02](https://tools.ietf.org/html/draft-ietf-secsh-agent-02) and its
+[OpenSSH](https://www.libssh.org/features/) equivalent are supported. **Note:** in order to support this feature the
[Apache Portable Runtime Library](https://apr.apache.org/) needs to be added to the Maven dependencies:

```xml
@@ -1096,27 +1203,45 @@ is available in the LD\_LIBRARY\_PATH.
# Advanced configuration and interaction

## Properties and inheritance model
-The code's behavior is highly customizable not only via non-default implementations of interfaces but also as far as the **parameters** that govern its behavior - e.g., timeouts, min./max. values, allocated memory size, etc... All the customization related code flow implements a **hierarchical** `PropertyResolver` inheritance model where the "closest" entity is consulted first, and then its "owner", and so on until the required value is found. If the entire hierarchy yielded no specific result, then some pre-configured default is used. E.g., if a channel requires some parameter in order to decide how to behave, then the following configuration hierarchy is consulted:
+The code's behavior is highly customizable not only via non-default implementations of interfaces but also as far as
+the **parameters** that govern its behavior - e.g., timeouts, min./max. values, allocated memory size, etc... All the
+customization related code flow implements a **hierarchical** `PropertyResolver` inheritance model where the "closest"
+entity is consulted first, and then its "owner", and so on until the required value is found. If the entire hierarchy
+yielded no specific result, then some pre-configured default is used. E.g., if a channel requires some parameter in order
+to decide how to behave, then the following configuration hierarchy is consulted:

* The channel-specific configuration
* The "owning" session configuration
* The "owning" client/server instance configuration
-* The system properties - **Note:** any configuration value required by the code can be provided via a system property bearing the `org.apache.sshd.config` prefix - see `SyspropsMapWrapper` for the implementation details.
+* The system properties - **Note:** any configuration value required by the code can be provided via a system property bearing
+the `org.apache.sshd.config` prefix - see `SyspropsMapWrapper` for the implementation details.


### Using the inheritance model for fine-grained/targeted configuration

-As previously mentioned, this hierarchical lookup model is not limited to "simple" configuration values (strings, integers, etc.), but used also for **interfaces/implementations** such as cipher/MAC/compression/authentication/etc. factories - the exception being that the system properties are not consulted in such a case. This code behavior provides highly customizable fine-grained/targeted control of the code's behavior - e.g., one could impose usage of specific ciphers/authentication methods/etc. or present different public key "identities"/welcome banner behavior/etc., based on address, username or whatever other decision parameter is deemed relevant by the user's code. This can be done on __both__ sides of the connection - client or server. E.g., the client could present different keys based on the server's address/identity string/welcome banner, or the server could accept only specific types of authentication methods based on the client's address/username/etc... This can be don
e in conjunction with the usage of the various `EventListener`-s provided by the code (see below).
+As previously mentioned, this hierarchical lookup model is not limited to "simple" configuration values (strings, integers, etc.), but
+used also for **interfaces/implementations** such as cipher/MAC/compression/authentication/etc. factories - the exception being that
+the system properties are not consulted in such a case. This code behavior provides highly customizable fine-grained/targeted control
+of the code's behavior - e.g., one could impose usage of specific ciphers/authentication methods/etc. or present different public key
+"identities"/welcome banner behavior/etc., based on address, username or whatever other decision parameter is deemed relevant by the
+user's code. This can be done on __both__ sides of the connection - client or server. E.g., the client could present different keys
+based on the server's address/identity string/welcome banner, or the server could accept only specific types of authentication methods
+based on the client's address/username/etc... This can be done in conjunction with the usage of the various `EventListener`-s provided
+by the code (see below).

-One of the code locations where this behavior can be leveraged is when the server provides __file-based__ services (SCP, SFTP) in order to provide a different/limited view of the available files based on the username - see the section dealing with `FileSystemFactory`-ies.
+One of the code locations where this behavior can be leveraged is when the server provides __file-based__ services (SCP, SFTP) in
+order to provide a different/limited view of the available files based on the username - see the section dealing with `FileSystemFactory`-ies.

## Welcome banner configuration

-According to [RFC 4252 - section 5.4](https://tools.ietf.org/html/rfc4252#section-5.4) the server may send a welcome banner message during the authentication process. Both the message contents and the phase at which it is sent can be configured/customized.
+According to [RFC 4252 - section 5.4](https://tools.ietf.org/html/rfc4252#section-5.4) the server may send a welcome
+banner message during the authentication process. Both the message contents and the phase at which it is sent can be
+configured/customized.

### Welcome banner content customization

-The welcome banner contents are controlled by the `ServerAuthenticationManager.WELCOME_BANNER` configuration key - there are several possible values for this key:
+The welcome banner contents are controlled by the `ServerAuthenticationManager.WELCOME_BANNER` configuration
+key - there are several possible values for this key:

* A simple string - in which case its contents are the welcome banner.

@@ -1124,13 +1249,18 @@ The welcome banner contents are controlled by the `ServerAuthenticationManager.W
* A file [URI](https://docs.oracle.com/javase/8/docs/api/java/net/URI.html) - or a string starting with `"file:/"` followed by the file path - see below.


-* A [URL](https://docs.oracle.com/javase/8/docs/api/java/net/URL.html) - or a string containing "://" - in which case the [URL#openStream()](https://docs.oracle.com/javase/8/docs/api/java/net/URL.html#openStream) method is invoked and its contents are read.
+* A [URL](https://docs.oracle.com/javase/8/docs/api/java/net/URL.html) - or a string containing "://" - in which
+case the [URL#openStream()](https://docs.oracle.com/javase/8/docs/api/java/net/URL.html#openStream) method is invoked
+and its contents are read.


-* A [File](https://docs.oracle.com/javase/8/docs/api/java/io/File.html) or a [Path](https://docs.oracle.com/javase/8/docs/api/java/nio/file/Path.html) - in this case, the file's contents are __re-loaded__ every time it is required and sent as the banner contents.
+* A [File](https://docs.oracle.com/javase/8/docs/api/java/io/File.html) or
+a [Path](https://docs.oracle.com/javase/8/docs/api/java/nio/file/Path.html) - in this case, the file's contents are __re-loaded__ every time it is required and sent as the banner contents.


-* The special value `ServerAuthenticationManager.AUTO_WELCOME_BANNER_VALUE` which generates a combined "random art" of all the server's keys as described in `Perrig A.` and `Song D.`-s article [Hash Visualization: a New Technique to improve Real-World Security](http://sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf) - _International Workshop on Cryptographic Techniques and E-Commerce (CrypTEC '99)_
+* The special value `ServerAuthenticationManager.AUTO_WELCOME_BANNER_VALUE` which generates a combined "random art" of
+all the server's keys as described in `Perrig A.` and `Song D.`-s article
+[Hash Visualization: a New Technique to improve Real-World Security](http://sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf) - _International Workshop on Cryptographic Techniques and E-Commerce (CrypTEC '99)_


* One can also override the `ServerUserAuthService#resolveWelcomeBanner` method and use whatever other content customization one sees fit.
@@ -1142,7 +1272,8 @@ The welcome banner contents are controlled by the `ServerAuthenticationManager.W

2. If the banner is loaded from a file or URL resource, then one can configure the [Charset](https://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html) used to convert the file's contents into a string via the `ServerAuthenticationManager.WELCOME_BANNER_CHARSET` configuration key (default=`UTF-8`).

-3. In this context, see also the `ServerAuthenticationManager.WELCOME_BANNER_LANGUAGE` configuration key - which provides control over the declared language tag, although most clients seem to ignore it.
+3. In this context, see also the `ServerAuthenticationManager.WELCOME_BANNER_LANGUAGE` configuration key - which
+provides control over the declared language tag, although most clients seem to ignore it.


### Welcome banner sending phase
@@ -1152,12 +1283,17 @@ According to [RFC 4252 - section 5.4](https://tools.ietf.org/html/rfc4252#sectio
The SSH server may send an SSH_MSG_USERAUTH_BANNER message at any time after this authentication protocol starts and before authentication is successful.
-The code contains a `WelcomeBannerPhase` enumeration that can be used to configure via the `ServerAuthenticationManager.WELCOME_BANNER_PHASE` configuration key the authentication phase at which the welcome banner is sent (see also the `ServerAuthenticationManager.DEFAULT_BANNER_PHASE` value). In this context, note that if the `NEVER` phase is configured, no banner will be sent even if one has been configured via one of the methods mentioned previously.
+The code contains a `WelcomeBannerPhase` enumeration that can be used to configure via the `ServerAuthenticationManager.WELCOME_BANNER_PHASE`
+configuration key the authentication phase at which the welcome banner is sent (see also the `ServerAuthenticationManager.DEFAULT_BANNER_PHASE` value).
+In this context, note that if the `NEVER` phase is configured, no banner will be sent even if one has been configured via one of the methods mentioned previously.


## `HostConfigEntryResolver`

-This interface provides the ability to intervene during the connection and authentication phases and "re-write" the user's original parameters. The `DefaultConfigFileHostEntryResolver` instance used to set up the default client instance follows the [SSH config file](https://www.digitalocean.com/community/tutorials/how-to-configure-custom-connection-options-for-your-ssh-client) standards, but the interface can be replaced so as to implement whatever proprietary logic is required.
+This interface provides the ability to intervene during the connection and authentication phases and "re-write"
+the user's original parameters. The `DefaultConfigFileHostEntryResolver` instance used to set up the default
+client instance follows the [SSH config file](https://www.digitalocean.com/community/tutorials/how-to-configure-custom-connection-options-for-your-ssh-client)
+standards, but the interface can be replaced so as to implement whatever proprietary logic is required.


```java
@@ -1179,13 +1315,20 @@ This interface provides the ability to intervene during the connection and authe

## `SshConfigFileReader`

-Can be used to read various standard SSH [client](http://linux.die.net/man/5/ssh_config) or [server](http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html) configuration files and initialize the client/server respectively. Including (among other things), bind address, ciphers, signature, MAC(s), KEX protocols, compression, welcome banner, etc..
+Can be used to read various standard SSH [client](http://linux.die.net/man/5/ssh_config)
+or [server](http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html) configuration files
+and initialize the client/server respectively. Including (among other things), bind address, ciphers,
+signature, MAC(s), KEX protocols, compression, welcome banner, etc..

## Event listeners

-The code supports registering many types of event listeners that enable receiving notifications about important events as well as sometimes intervening in the way these events are handled. All listener interfaces extend `SshdEventListener` so they can be easily detected and distinguished from other `EventListener`(s).
+The code supports registering many types of event listeners that enable receiving notifications about important events
+as well as sometimes intervening in the way these events are handled. All listener interfaces extend `SshdEventListener`
+so they can be easily detected and distinguished from other `EventListener`(s).

-In general, event listeners are **cumulative** - e.g., any channel event listeners registered on the `SshClient/Server` are automatically added to all sessions, *in addition* to any such listeners registered on the `Session`, as well as any specific listeners registered on a specific `Channel` - e.g.,
+In general, event listeners are **cumulative** - e.g., any channel event listeners registered on the `SshClient/Server` are
+automatically added to all sessions, *in addition* to any such listeners registered on the `Session`, as well as any specific
+listeners registered on a specific `Channel` - e.g.,


```java
@@ -1216,7 +1359,10 @@ registered listener.

### `SessionListener`

-Informs about session related events. One can modify the session - although the modification effect depends on the session's **state**. E.g., if one changes the ciphers *after* the key exchange (KEX) phase, then they will take effect only if the keys are re-negotiated. It is important to read the documentation very carefully and understand at which stage each listener method is invoked and what are the repercussions of changes at that stage. In this context, it is worth mentioning that one can attach to sessions **arbitrary attributes** that can be retrieved by the user's code later on:
+Informs about session related events. One can modify the session - although the modification effect depends on the session's **state**. E.g., if one
+changes the ciphers *after* the key exchange (KEX) phase, then they will take effect only if the keys are re-negotiated. It is important to read the
+documentation very carefully and understand at which stage each listener method is invoked and what are the repercussions of changes at that stage.
+In this context, it is worth mentioning that one can attach to sessions **arbitrary attributes** that can be retrieved by the user's code later on:


```java
@@ -1244,18 +1390,25 @@ Informs about session related events. One can modify the session - although the
### `ChannelListener`


-Informs about channel related events - as with sessions, once can influence the channel to some extent, depending on the channel's **state**. The ability to influence channels is much more limited than sessions. In this context, it is worth mentioning that one can attach to channels **arbitrary attributes** that can be retrieved by the user's code later on - same was as it is done for sessions.
+Informs about channel related events - as with sessions, once can influence the channel to some extent, depending on the channel's **state**.
+The ability to influence channels is much more limited than sessions. In this context, it is worth mentioning that one can attach to channels
+**arbitrary attributes** that can be retrieved by the user's code later on - same was as it is done for sessions.


### `UnknownChannelReferenceHandler`


-Invoked whenever a message intended for an unknown channel is received. By default, the code **ignores** the vast majority of such messages and logs them at DEBUG level. For a select few types of messages the code generates an `SSH_CHANNEL_MSG_FAILURE` packet that is sent to the peer session - see `DefaultUnknownChannelReferenceHandler` implementation. The user may register handlers at any level - client/server, session and/or connection service - the one registered "closest" to connection service will be used.
+Invoked whenever a message intended for an unknown channel is received. By default, the code **ignores** the vast majority of such messages
+and logs them at DEBUG level. For a select few types of messages the code generates an `SSH_CHANNEL_MSG_FAILURE` packet that is sent to the
+peer session - see `DefaultUnknownChannelReferenceHandler` implementation. The user may register handlers at any level - client/server, session
+and/or connection service - the one registered "closest" to connection service will be used.


### `SignalListener`

-Informs about signal requests as described in [RFC 4254 - section 6.9](https://tools.ietf.org/html/rfc4254#section-6.9), break requests (sent as SIGINT) as described in [RFC 4335](https://tools.ietf.org/html/rfc4335) and "window-change" (sent as SIGWINCH) requests as described in [RFC 4254 - section 6.7](https://tools.ietf.org/html/rfc4254#section-6.7)
+Informs about signal requests as described in [RFC 4254 - section 6.9](https://tools.ietf.org/html/rfc4254#section-6.9), break requests
+(sent as SIGINT) as described in [RFC 4335](https://tools.ietf.org/html/rfc4335) and "window-change" (sent as SIGWINCH) requests as described
+in [RFC 4254 - section 6.7](https://tools.ietf.org/html/rfc4254#section-6.7)


### `SftpEventListener`
@@ -1274,7 +1427,10 @@ Provides information about major SFTP protocol events. The listener is registere

### `PortForwardingEventListener`

-Informs and allows tracking of port forwarding events as described in [RFC 4254 - section 7](https://tools.ietf.org/html/rfc4254#section-7) as well as the (simple) [SOCKS](https://en.wikipedia.org/wiki/SOCKS) protocol (versions 4, 5). In this context, one can create a `PortForwardingTracker` that can be used in a `try-with-resource` block so that the set up forwarding is automatically torn down when the tracker is `close()`-d:
+Informs and allows tracking of port forwarding events as described in [RFC 4254 - section 7](https://tools.ietf.org/html/rfc4254#section-7)
+as well as the (simple) [SOCKS](https://en.wikipedia.org/wiki/SOCKS) protocol (versions 4, 5). In this context, one can create a
+`PortForwardingTracker` that can be used in a `try-with-resource` block so that the set up forwarding is automatically torn down when
+the tracker is `close()`-d:


```java
@@ -1316,22 +1472,27 @@ Inform about SCP related events. `ScpTransferEventListener`(s) can be registered

### Reserved messages

-The implementation can be used to intercept and process the [SSH_MSG_IGNORE](https://tools.ietf.org/html/rfc4253#section-11.2), [SSH_MSG_DEBUG](https://tools.ietf.org/html/rfc4253#section-11.3) and [SSH_MSG_UNIMPLEMENTED](https://tools.ietf.org/html/rfc4253#section-11.4) messages. The handler can be registered on either side - server
-or client, as well as on the session. A special [patch](https://issues.apache.org/jira/browse/SSHD-699) has been introduced
-that automatically ignores such messages if they are malformed - i.e., they never reach the handler.
+The implementation can be used to intercept and process the [SSH_MSG_IGNORE](https://tools.ietf.org/html/rfc4253#section-11.2),
+[SSH_MSG_DEBUG](https://tools.ietf.org/html/rfc4253#section-11.3) and [SSH_MSG_UNIMPLEMENTED](https://tools.ietf.org/html/rfc4253#section-11.4)
+messages. The handler can be registered on either side - server or client, as well as on the session. A special
+[patch](https://issues.apache.org/jira/browse/SSHD-699) has been introduced that automatically ignores such messages
+if they are malformed - i.e., they never reach the handler.

#### SSH message stream "stuffing" and keys re-exchange

[RFC 4253 - section 9](https://tools.ietf.org/html/rfc4253#section-9) recommends re-exchanging keys every once in a while
-based on the amount of traffic and the selected cipher - the matter is further clarified in [RFC 4251 - section 9.3.2](https://tools.ietf.org/html/rfc4251#section-9.3.2). These recommendations are mirrored in the code via the `FactoryManager`
-related `REKEY_TIME_LIMIT`, `REKEY_PACKETS_LIMIT` and `REKEY_BLOCKS_LIMIT` configuration properties that
-can be used to configure said behavior - please be sure to read the relevant _Javadoc_ as well as the aforementioned RFC section(s) when
-manipulating them. This behavior can also be controlled programmatically by overriding the `AbstractSession#isRekeyRequired()` method.
+based on the amount of traffic and the selected cipher - the matter is further clarified in [RFC 4251 - section 9.3.2](https://tools.ietf.org/html/rfc4251#section-9.3.2).
+These recommendations are mirrored in the code via the `FactoryManager` related `REKEY_TIME_LIMIT`, `REKEY_PACKETS_LIMIT`
+and `REKEY_BLOCKS_LIMIT` configuration properties that can be used to configure said behavior - please be sure to read
+the relevant _Javadoc_ as well as the aforementioned RFC section(s) when manipulating them. This behavior can also be
+controlled programmatically by overriding the `AbstractSession#isRekeyRequired()` method.

As an added security mechanism [RFC 4251 - section 9.3.1](https://tools.ietf.org/html/rfc4251#section-9.3.1) recommends adding
"spurious" [SSH_MSG_IGNORE](https://tools.ietf.org/html/rfc4253#section-11.2) messages. This functionality is mirrored in the
`FactoryManager` related `IGNORE_MESSAGE_FREQUENCY`, `IGNORE_MESSAGE_VARIANCE` and `IGNORE_MESSAGE_SIZE`
-configuration properties that can be used to configure said behavior - please be sure to read the relevant _Javadoc_ as well as the aforementioned RFC section when manipulating them. This behavior can also be controlled programmatically by overriding the `AbstractSession#resolveIgnoreBufferDataLength()` method.
+configuration properties that can be used to configure said behavior - please be sure to read the relevant _Javadoc_ as well
+as the aforementioned RFC section when manipulating them. This behavior can also be controlled programmatically by overriding
+the `AbstractSession#resolveIgnoreBufferDataLength()` method.

#### `ReservedSessionMessagesHandler`

@@ -1417,7 +1578,8 @@ it is detected. For global request handlers this is done by registering them on

```

-For channel-specific requests, one uses the channel's `add/removeRequestHandler` method to manage its handlers. The way request handlers are invoked when a global/channel-specific request is received is as follows:
+For channel-specific requests, one uses the channel's `add/removeRequestHandler` method to manage its handlers. The way
+request handlers are invoked when a global/channel-specific request is received is as follows:

* All currently registered handlers' `process` method is invoked with the request type string parameter (among others).
The implementation should examine the request parameters and decide whether it is able to process it.
@@ -1470,24 +1632,33 @@ the handler may choose to build and send the response within its own code, in wh


* `keepalive@*` - Used by many implementations (including this one) to "ping" the peer and make sure the connection is still alive.
-In this context, the SSHD code allows the user to configure both the frequency and content of the heartbeat request (including whether to send this request at all) via the `ClientFactoryManager`-s `HEARTBEAT_INTERVAL`, `HEARTBEAT_REQUEST` and `DEFAULT_KEEP_ALIVE_HEARTBEAT_STRING` configuration properties.
+In this context, the SSHD code allows the user to configure both the frequency and content of the heartbeat request (including whether
+to send this request at all) via the `ClientFactoryManager`-s `HEARTBEAT_INTERVAL`, `HEARTBEAT_REQUEST` and `DEFAULT_KEEP_ALIVE_HEARTBEAT_STRING`
+configuration properties.


-* `no-more-sessions@*` - As described in [OpenSSH protocol section 2.2](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL). In this context, the code consults the `ServerFactoryManagder.MAX_CONCURRENT_SESSIONS` server-side configuration property in order to
+* `no-more-sessions@*` - As described in [OpenSSH protocol section 2.2](https://github.com/openssh/openssh-portable/blob/master/PROTOCOL).
+In this context, the code consults the `ServerFactoryManagder.MAX_CONCURRENT_SESSIONS` server-side configuration property in order to
decide whether to accept a successfully authenticated session.


# Extension modules

-There are several extension modules available - specifically, the _sshd-contrib_ module contains some of them. **Note:** the module contains experimental code that may find its way some time
-in the future to a standard artifact. It is also subject to changes and/or deletion without any prior announcement. Therefore, any code that relies on it should also store a copy of the sources
-in case the classes it used it are modified or deleted.
+There are several extension modules available - specifically, the _sshd-contrib_ module contains some of them. **Note:** the
+module contains experimental code that may find its way some time in the future to a standard artifact. It is also subject to
+changes and/or deletion without any prior announcement. Therefore, any code that relies on it should also store a copy of the
+sources in case the classes it used it are modified or deleted.

## Command line clients

-The _apache-sshd.zip_ distribution provides `Windows/Linux` scripts that use the MINA SSHD code base to implement the common _ssh, scp, sftp_ commands. The clients accept most useful switches from the original commands they mimic, where the `-o Option=Value` arguments can be used to configure the client/server in addition to the system properties mechanism. For more details, consult the _main_ methods code in the respective `SshClientMain`, `SftpCommandMain` and `ScpClientMain` classes. The code also includes `SshKeyScanMain` that is a simple implementation for [ssh-keyscan(1)](https://www.freebsd.org/cgi/man.cgi?query=ssh-keyscan&sektion=1).
+The _apache-sshd.zip_ distribution provides `Windows/Linux` scripts that use the MINA SSHD code base to implement the common
+_ssh, scp, sftp_ commands. The clients accept most useful switches from the original commands they mimic, where the `-o Option=Value`
+arguments can be used to configure the client/server in addition to the system properties mechanism. For more details, consult
+the _main_ methods code in the respective `SshClientMain`, `SftpCommandMain` and `ScpClientMain` classes. The code also includes
+`SshKeyScanMain` that is a simple implementation for [ssh-keyscan(1)](https://www.freebsd.org/cgi/man.cgi?query=ssh-keyscan&sektion=1).

-The distribution also includes also an _sshd_ script that can be used to launch a server instance - see `SshServerMain#main` for activation command line arguments and options.
+The distribution also includes also an _sshd_ script that can be used to launch a server instance - see `SshServerMain#main`
+for activation command line arguments and options.

In order to use this CLI code as part of another project, one needs to include the _sshd-cli_ module:

@@ -1513,10 +1684,12 @@ the class that implements this interface. **Note:** if more than one such instan

### Command line SSH daemon

-* **Port** - by default the SSH server sets up to list on port 8000 in order to avoid conflicts with any running SSH O/S daemon. This can be modified by providing a `-p NNNN`
-or `-o Port=NNNN` command line option.
+* **Port** - by default the SSH server sets up to list on port 8000 in order to avoid conflicts with any running SSH O/S daemon.
+This can be modified by providing a `-p NNNN` or `-o Port=NNNN` command line option.
+
+* **Subsystem(s)** - the server automatically detects subsystems using the
+[Java ServiceLoader mechanism](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html).

-* **Subsystem(s)** - the server automatically detects subsystems using the [Java ServiceLoader mechanism](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html).
This can be overwritten as follows (in this order):

1. Provide a `org.apache.sshd.server.subsystem.SubsystemFactory` system property containing comma-separated fully-qualified names of classes implementing
@@ -1533,16 +1706,17 @@ name of a class that implements the `ShellFactory` interface. The implementation

## GIT support

-The _sshd-git_ artifact contains both client and server-side command factories for issuing and handling some _git_ commands. The code is based on [JGit](https://github.com/eclipse/jgit)
-and iteracts with it smoothly.
+The _sshd-git_ artifact contains both client and server-side command factories for issuing and handling some _git_ commands. The code is based
+on [JGit](https://github.com/eclipse/jgit) and iteracts with it smoothly.

### Client-side

-This module provides SSHD-based replacements for the SSH and SFTP transports used by the JGIT client - see `GitSshdSessionFactory` - it can be used as a drop-in replacement
-for the [JSCH](http://www.jcraft.com/jsch/) based built-in session factory provided by _jgit_. In this context, it is worth noting that the `GitSshdSessionFactory` has been tailored so as to provide
-flexible control over which `SshClient` instance to use, and even which `ClientSession`. The default instance allocates a **new** client every time a new `GitSshdSession` is created - which is
-started and stopped as necessary. However, this can be pretty wasteful, so if one intends to issue several commands that access GIT repositories via SSH, one should maintain a **single**
-client instance and re-use it:
+This module provides SSHD-based replacements for the SSH and SFTP transports used by the JGIT client - see `GitSshdSessionFactory` - it
+can be used as a drop-in replacement for the [JSCH](http://www.jcraft.com/jsch/) based built-in session factory provided by _jgit_. In
+this context, it is worth noting that the `GitSshdSessionFactory` has been tailored so as to provide flexible control over which `SshClient`
+instance to use, and even which `ClientSession`. The default instance allocates a **new** client every time a new `GitSshdSession` is
+created - which is started and stopped as necessary. However, this can be pretty wasteful, so if one intends to issue several commands
+that access GIT repositories via SSH, one should maintain a **single** client instance and re-use it:


```java
@@ -1618,11 +1792,19 @@ is completed (regardless of whether successful or not):

## LDAP adaptors

-The _sshd-ldap_ artifact contains an [LdapPasswordAuthenticator](https://issues.apache.org/jira/browse/SSHD-607) and an [LdapPublicKeyAuthenticator](https://issues.apache.org/jira/browse/SSHD-608) that have been written along the same lines as the [openssh-ldap-publickey](https://github.com/AndriiGrytsenko/openssh-ldap-publickey) project. The authenticators can be easily configured to match most LDAP schemes, or alternatively serve as base classes for code that extends them and adds proprietary logic.
+The _sshd-ldap_ artifact contains an [LdapPasswordAuthenticator](https://issues.apache.org/jira/browse/SSHD-607) and
+an [LdapPublicKeyAuthenticator](https://issues.apache.org/jira/browse/SSHD-608) that have been written along the same
+lines as the [openssh-ldap-publickey](https://github.com/AndriiGrytsenko/openssh-ldap-publickey) project. The authenticators
+can be easily configured to match most LDAP schemes, or alternatively serve as base classes for code that extends them
+and adds proprietary logic.

## PROXY / SSLH protocol hooks

-The code contains [support for "wrapper" protocols](https://issues.apache.org/jira/browse/SSHD-656) such as [PROXY](http://www.haproxy.org/download/1.6/doc/proxy-protocol.txt) or [sslh](http://www.rutschle.net/tech/sslh.shtml). The idea is that one can register either a `ClientProxyConnector` or `ServerProxyAcceptor` and intercept the 1st packet being sent/received (respectively) **before** it reaches the SSHD code. This gives the programmer the capability to write a front-end that routes outgoing/incoming packets:
+The code contains [support for "wrapper" protocols](https://issues.apache.org/jira/browse/SSHD-656) such
+as [PROXY](http://www.haproxy.org/download/1.6/doc/proxy-protocol.txt) or [sslh](http://www.rutschle.net/tech/sslh.shtml).
+The idea is that one can register either a `ClientProxyConnector` or `ServerProxyAcceptor` and intercept
+the 1st packet being sent/received (respectively) **before** it reaches the SSHD code. This gives the programmer
+the capability to write a front-end that routes outgoing/incoming packets:

* `SshClient/ClientSesssion#setClientProxyConnector` - sets a proxy that intercepts the 1st packet before being sent to the server

@@ -1699,23 +1881,28 @@ try (ClientSession session = client.connect(login, host, port).await().getSessio
```


-**Note:** `UserInteraction#isInteractionAllowed` is consulted prior to invoking `getUpdatedPassword` - if it returns _false_ then password retrieval method is not invoked,
-and it is assumed that no more passwords are available
+**Note:** `UserInteraction#isInteractionAllowed` is consulted prior to invoking `getUpdatedPassword` - if it
+returns _false_ then password retrieval method is not invoked, and it is assumed that no more passwords are available
+

+* `SimpleAccessControlScpEventListener` - Provides a simple access control by making a distinction between
+methods that upload data and ones that download it via SCP. In order to use it, simply extend it and override
+its `isFileUpload/DownloadAllowed` methods

-* `SimpleAccessControlScpEventListener` - Provides a simple access control by making a distinction between methods that upload data and ones that download it via SCP.
-In order to use it, simply extend it and override its `isFileUpload/DownloadAllowed` methods

+* `SimpleAccessControlSftpEventListener` - Provides a simple access control by making a distinction between
+methods that provide SFTP file information - including reading data - and those that modify it

-* `SimpleAccessControlSftpEventListener` - Provides a simple access control by making a distinction between methods that provide SFTP file information - including
-reading data - and those that modify it

+* `ProxyProtocolAcceptor` - A working prototype to support the PROXY protocol as described in
+[HAProxy Documentation](http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)

-* `ProxyProtocolAcceptor` - A working prototype to support the PROXY protocol as described in [HAProxy Documentation](http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)

+* `ThrottlingPacketWriter` - An example of a way to overcome big window sizes when sending data - as
+described in [SSHD-754](https://issues.apache.org/jira/browse/SSHD-754) and [SSHD-768](https://issues.apache.org/jira/browse/SSHD-768)

-* `ThrottlingPacketWriter` - An example of a way to overcome big window sizes when sending data - as described in [SSHD-754](https://issues.apache.org/jira/browse/SSHD-754)
-and [SSHD-768](https://issues.apache.org/jira/browse/SSHD-768)
+* `AndroidOpenSSLSecurityProviderRegistrar` - A security registrar that uses the [AndroidOpenSSL](https://github.com/guardianproject/openssl-android)
+security provider

# Builtin components


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 5d9afed..a67ae3b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -616,6 +616,7 @@
<configuration>
<excludes>
<exclude>README.md</exclude>
+ <exclude>CHANGES.md</exclude>
<exclude>src/docs/**</exclude>
<exclude>src/test/resources/**</exclude>
<exclude>**/stty-output-*.txt</exclude>

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java
----------------------------------------------------------------------
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java b/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java
index 025c209..1ba94d2 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java
@@ -24,6 +24,7 @@ import java.net.SocketAddress;
import java.util.Map;
import java.util.Objects;

+import org.apache.sshd.common.AttributeRepository;
import org.apache.sshd.common.config.ConfigFileReaderSupport;
import org.apache.sshd.common.config.LogLevelValue;
import org.apache.sshd.common.helpers.AbstractFactoryManager;
@@ -124,7 +125,7 @@ public abstract class CliSupport {

@Override
public void connectionEstablished(
- IoConnector connector, SocketAddress local, SocketAddress remote)
+ IoConnector connector, SocketAddress local, AttributeRepository context, SocketAddress remote)
throws IOException {
out.append("Connection established via ").append(Objects.toString(connector))
.append("- local=").append(Objects.toString(local))
@@ -134,7 +135,7 @@ public abstract class CliSupport {

@Override
public void abortEstablishedConnection(
- IoConnector connector, SocketAddress local, SocketAddress remote, Throwable reason)
+ IoConnector connector, SocketAddress local, AttributeRepository context, SocketAddress remote, Throwable reason)
throws IOException {
out.append("Abort established connection ").append(Objects.toString(connector))
.append(" - local=").append(Objects.toString(local))

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
----------------------------------------------------------------------
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
index 2d129f6..4d3644c 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
@@ -60,6 +60,7 @@ import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.NamedResource;
import org.apache.sshd.common.PropertyResolver;
import org.apache.sshd.common.PropertyResolverUtils;
+import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.cipher.BuiltinCiphers;
import org.apache.sshd.common.cipher.Cipher;
import org.apache.sshd.common.compression.BuiltinCompressions;
@@ -236,7 +237,7 @@ public abstract class SshClientCliSupport extends CliSupport {
}

if (port <= 0) {
- port = ConfigFileReaderSupport.DEFAULT_PORT;
+ port = SshConstants.DEFAULT_PORT;
}

// TODO use a configurable wait time

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java
----------------------------------------------------------------------
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java
index 558245b..9619499 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java
@@ -62,8 +62,8 @@ import org.apache.sshd.client.future.ConnectFuture;
import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.cipher.ECCurves;
-import org.apache.sshd.common.config.ConfigFileReaderSupport;
import org.apache.sshd.common.config.keys.BuiltinIdentities;
import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.config.keys.PublicKeyEntry;
@@ -714,7 +714,7 @@ public class SshKeyScanMain implements Channel, Callable<Void>, ServerKeyVerifie
public static <S extends SshKeyScanMain> S initializeScanner(S scanner, Collection<String> hosts) throws IOException {
setInputStream(scanner, hosts);
if (scanner.getPort() <= 0) {
- scanner.setPort(ConfigFileReaderSupport.DEFAULT_PORT);
+ scanner.setPort(SshConstants.DEFAULT_PORT);
}

if (scanner.getTimeout() <= 0L) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-cli/src/test/java/org/apache/sshd/cli/server/SshFsMounter.java
----------------------------------------------------------------------
diff --git a/sshd-cli/src/test/java/org/apache/sshd/cli/server/SshFsMounter.java b/sshd-cli/src/test/java/org/apache/sshd/cli/server/SshFsMounter.java
index 914714c..0fbc926 100644
--- a/sshd-cli/src/test/java/org/apache/sshd/cli/server/SshFsMounter.java
+++ b/sshd-cli/src/test/java/org/apache/sshd/cli/server/SshFsMounter.java
@@ -35,7 +35,7 @@ import java.util.concurrent.Future;

import org.apache.sshd.common.PropertyResolver;
import org.apache.sshd.common.PropertyResolverUtils;
-import org.apache.sshd.common.config.ConfigFileReaderSupport;
+import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.io.BuiltinIoServiceFactoryFactories;
import org.apache.sshd.common.io.IoServiceFactory;
import org.apache.sshd.common.util.GenericUtils;
@@ -250,7 +250,7 @@ public final class SshFsMounter extends SshServerCliSupport {
//////////////////////////////////////////////////////////////////////////

public static void main(String[] args) throws Exception {
- int port = ConfigFileReaderSupport.DEFAULT_PORT;
+ int port = SshConstants.DEFAULT_PORT;
boolean error = false;
Map<String, Object> options = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
int numArgs = GenericUtils.length(args);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostHashValue.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostHashValue.java b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostHashValue.java
index bf43462..f423259 100644
--- a/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostHashValue.java
+++ b/sshd-common/src/main/java/org/apache/sshd/client/config/hosts/KnownHostHashValue.java
@@ -29,7 +29,7 @@ import org.apache.sshd.common.Factory;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.NamedResource;
import org.apache.sshd.common.RuntimeSshException;
-import org.apache.sshd.common.config.ConfigFileReaderSupport;
+import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.mac.Mac;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.NumberUtils;
@@ -135,7 +135,7 @@ public class KnownHostHashValue {
}

public static String createHostPattern(String host, int port) {
- if ((port <= 0) || (port == ConfigFileReaderSupport.DEFAULT_PORT)) {
+ if ((port <= 0) || (port == SshConstants.DEFAULT_PORT)) {
return host;
}

@@ -148,7 +148,7 @@ public class KnownHostHashValue {
}

public static <A extends Appendable> A appendHostPattern(A sb, String host, int port) throws IOException {
- boolean nonDefaultPort = (port > 0) && (port != ConfigFileReaderSupport.DEFAULT_PORT);
+ boolean nonDefaultPort = (port > 0) && (port != SshConstants.DEFAULT_PORT);
if (nonDefaultPort) {
sb.append(HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM);
}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/403402f5/sshd-common/src/main/java/org/apache/sshd/common/Closeable.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/Closeable.java b/sshd-common/src/main/java/org/apache/sshd/common/Closeable.java
index 6a6f622..f4f9310 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/Closeable.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/Closeable.java
@@ -104,8 +104,9 @@ public interface Closeable extends Channel {
}

static long getMaxCloseWaitTime(PropertyResolver resolver) {
- return (resolver == null) ? DEFAULT_CLOSE_WAIT_TIMEOUT
- : resolver.getLongProperty(CLOSE_WAIT_TIMEOUT, DEFAULT_CLOSE_WAIT_TIMEOUT);
+ return (resolver == null)
+ ? DEFAULT_CLOSE_WAIT_TIMEOUT
+ : resolver.getLongProperty(CLOSE_WAIT_TIMEOUT, DEFAULT_CLOSE_WAIT_TIMEOUT);
}

static void close(Closeable closeable) throws IOException {
@@ -116,7 +117,8 @@ public interface Closeable extends Channel {
if ((!closeable.isClosed()) && (!closeable.isClosing())) {
CloseFuture future = closeable.close(true);
long maxWait = (closeable instanceof PropertyResolver)
- ? getMaxCloseWaitTime((PropertyResolver) closeable) : DEFAULT_CLOSE_WAIT_TIMEOUT;
+ ? getMaxCloseWaitTime((PropertyResolver) closeable)
+ : DEFAULT_CLOSE_WAIT_TIMEOUT;
boolean successful = future.await(maxWait);
if (!successful) {
throw new SocketTimeoutException("Failed to receive closure confirmation within " + maxWait + " millis");
Loading...