diff --git a/.gitignore b/.gitignore index 70e39ba..4bd5679 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,5 @@ log.txt secureData*.txt logfile.txt rmc_log.txt +*secureData*.txt +logfileConf.txt diff --git a/JFramework/auth/src/main/java/it/richkmeli/jframework/auth/AuthDatabaseJframeworkManager.java b/JFramework/auth/src/main/java/it/richkmeli/jframework/auth/AuthDatabaseJframeworkManager.java index 69b74cc..ce6b262 100644 --- a/JFramework/auth/src/main/java/it/richkmeli/jframework/auth/AuthDatabaseJframeworkManager.java +++ b/JFramework/auth/src/main/java/it/richkmeli/jframework/auth/AuthDatabaseJframeworkManager.java @@ -54,7 +54,7 @@ public User getUser(String email) throws AuthDatabaseException, ModelException { public boolean addUser(User user) throws AuthDatabaseException { //Logger.info("AuthDatabaseManager, addUser. User: " + user.email); //String hash = Crypto.hash(user.getPassword()); - String password = Crypto.hashPassword(user.getPassword(), false); + String password = Crypto.hashPassword(user.getPassword()); user.setPassword(password); try { return create(user); @@ -81,7 +81,7 @@ public boolean isUserPresent(String email) throws AuthDatabaseException, ModelEx @Override public boolean editPassword(String email, String pass) throws AuthDatabaseException, ModelException { - String password = Crypto.hashPassword(pass, false); + String password = Crypto.hashPassword(pass); try { return update(new User(email, password, null)); } catch (DatabaseException e) { @@ -102,7 +102,22 @@ public boolean editAdmin(String email, Boolean isAdmin) throws AuthDatabaseExcep public boolean checkPassword(String email, String pass) throws AuthDatabaseException, ModelException { User user = getUser(email); if (user != null) { - return Crypto.verifyPassword(user.getPassword(), pass); + boolean passwordMatches = Crypto.verifyPassword(user.getPassword(), pass); + if (passwordMatches) { + // Check if the hash needs to be upgraded + String decodedDbHash = new String(java.util.Base64.getUrlDecoder().decode(user.getPassword())); + if (decodedDbHash.startsWith("000000000")) { + // Old hash format, upgrade it + try { + editPassword(email, pass); + Logger.info("Password for user " + email + " has been migrated to the new hash format."); + } catch (AuthDatabaseException | ModelException e) { + Logger.error("Failed to migrate password for user " + email, e); + // Don't fail the login, but log the error. The user can still log in. + } + } + } + return passwordMatches; } else { return false; } diff --git a/JFramework/crypto/clientSecureData.txt b/JFramework/crypto/clientSecureData.txt new file mode 100644 index 0000000..3a147a5 --- /dev/null +++ b/JFramework/crypto/clientSecureData.txt @@ -0,0 +1 @@ +oQMPy0fTLRdPWzyQN_a8Z3ZSx86PNn0Kmx7YaPDJWWmrCMtYyRqpKexxWLuhC_7-3Sh7bv4ntIZ2QyHfTPsfZwEXjcgsDuplwLT3sshGCzesJGseey0uVT6UHxrTWq-4eiebbeH9OcuD9BbHLSde4Wtvphgnj7nnTlFNWpq2fsd8a1qnCMU0-irML-6_vwuH57CO9oSCuqqwoKYPXPY1H5MMBdeUqnltGXpZWLpuqUkyTgULjkwJMxxsFFrcVt0dO2Ms4mAZOT0Ih0FBe06eRQAVTIZDIudwLoHVqdSdh4pBzjs6OS77GduFc9AfY0Yy_cv28bYbCmTD5OTL8GhB6An_03_alHPI165-PQ3RiNZazGiS88xgtNyI5NB5GVyBY3HLhaWxi2Phvk6uTnBpJUewJ1v8aJ3Rtz36H-GqonMXS4EkkVlnn8gQdp8sXHz40Crm-NC_Ilsee6Z8X6h8it_ODyp0rgjE8zRx9oOUlUdsraYG7jwqXPDY7Jvm8-o4jaarkkj78-vIql6Xzc_CMu0a8YSpOVG1IvRAk1h0sH9eoS8iZRlKvIJB_RTOjq1L-teK50htuBTF53oQreSPUG77LIoZqSvpBbdtqFeSOyDlZdRjLF0nzKlNrMFgUCcE-ynfGgHBaP6xuK_6zGreouB96__AiiL66yEYp3QX2AILlTWnTRalP08QOP3JPBLUodIqM93ugkfxwROQrSTFiTQxKvl--KrHVbTYkpKFslvsq80dJ9Hs2mbDu41FZbiD9wgXbOTkRxWg8Nrlsbx5cRMGk70YImwZql6KdDiXPyDek0YOycZRkAt2GvqRYotYCh9WvMparnQ1dHHCgAO5FTNJSQeSRaKdLhsJEfKvXNSiKFCDLsnaEa3pWXt7-IXNve3oyJIMcwH7JsnuMDTY-fQjV4EwvRbuxP0soZe2NcOcxMGaIYkorTNZqB9GyebYobgAMZ0rdJsFrVcrjukxpV_oyRyNwbvRVZGby0Au_NGazUNz41qJXlywynyBwCQtA_k7FNe4Gq2pzoFzSog7mh9Gz_9VlBrSqxSj3X6NfRI4J8C9FWq6_FmrznUiWg1miWTsWXdoWn0iZEq1mBQQ0rPvSkuPtG_1RaUW2p3neywD3zGtFL4YgOdzejCtbkN5pcubvL-6bd5d6l7yyXcQu6gQ0m7yhNn4vXE-xMDjkRK_Dr6ljZ4UXBt3MQfBuPaHi0ONAuzAx2bqfTWGJ2sjH1ez3jUu1AS1JaADHveBNMJxtwkvm4wc6ZfI1aL75U6hSPkNTG8yyG8AJIAuROIIBkQPNJUyZSxDXI0QHJNj6cMNWD2F5DtJaCsEzVfmVjsjfslAmUDsVBDXcLwY4wV7b_hCTho2GAPgcRN2Rj6G0XM7eRtRqOteJu_NqxWjLVUoPk2Arq7anF68Eh4z6Bw19eiPXAY-0bblCVcEROaxCUfjemeWILJEfPlXLSvNsox5fRfam1fWihEEyqv2s6A3Wpm7G69Ou2t8ZC_mgv938EJ2Yv8MORSiERxnbrslz7UuBmut9ckLqbZV9jdQlxOmKg2EOPRTTlbHB0a3mh4fRzLPFRsaco_7xkwwg_eqwltTQlyFLtSckuXS1HzpZQdWr1sulZa4zU3qU_TkTkUMfp1g2G2XaudX_AhppgLqn8tCdd0iyG5ItqsVT5BR1RuOrAntPEeSCAVCHDte4YPim3dnSk43Ga6Ox9et1peOndugZHueowj4p3vWWufrgAVQBCmvLWD-hE465x9kzwLDr6RMDKWVFGFAJTeQpYVff82VeY36lto2G06Rj2ItTzVYf5wa0VCe1aR0-xUndgKXOVRS3ZJJmxWNQnONIiT03qRP9x8BIjBxokiUaaSEjGmWP8Us3dT8I7HbHfe4y9j8b85rhxb1e2YW7IO2RdIQJ79Qz5A6rF64fT0LgUkOf8fsqghNHBxkYFDFT2TXcBE_koyxNqVCg7p2IT8Kg140RDIvodZHYPfT2RDIH7Sw5qLoT-r8icUJReHVJjgisSeuITEOhRenYTj_y0qstnFRviKiOnWZaHe2dsVHcmtn9mCn3DL8NnVb53x01ch_6QFL4EjqQUvALXDFNuZ0nF6-isjwcGLI80x5uREQwVNKMbwcs5_D92xeroI2ZpP7VXeYlRuGGC3M4IevFEALE7Ql0DSF_AvCakajXddoWtXJSmPS6souGvvxfLNitb2L_dIsNmcEpbbwvZbTu7wmvhLHlwZMiEu-5bNA9_Em4aSFIEkMz2O727q7UghW9QTEEKKEIRray2Ol8Hs6trCjEMivcIzi-oZ_uA019b0QzbW4t5UudqgzDlEw008X9u1aT5Dly4zmRq836u06xgjTOe2XtBDBkJGBCwUMfH0GydfceTVMN-WHgrEn3SMsDWnmjtkyCMye41xzCFcb_AOvdUhsuSr-plDS8UHWZrxStwLGMR-JxGbbeVPU9ns8xbXnjXzUoJWXLEUOANkztXXv87gzAknqd7aq5OlfXxr7_nHImOtx3c6gEAxW1KG5VlOSZTcs-8g4gp3Y4VrW9GfU8ZU_nUam0luRhyDlilkWr0FrpnM6J0bExYVgXkCTQki2AqJuGkAbp97AlJU5oSX85kNkxX9cXAsOOfO_xj3HbuweyCQ6XLHCIi7dpW6q0xmf9uSCosbtqaA0SIqDxECs9rWLidik5iCe7h0Lqe3OxuLEUdneVkJRdoMLfagdyOSDpDZ0ZVHH3vxaQl6O4qgkvTRN6-PBmxSGNnMLrvzgj3UAntNKQVmK64UUxJtjkuSrItW-hmdjuUI32Yx8bXzA0YMMOc8O7imD1zwubhtwQ4TdTOSlYEH37Jc3yafXKOsjkjBCNmFrQt1Q_eXQXo8leRgCpyWwRGcS9JGnjSHWavDX1vHekAqIf04hkyNDcqdhZ4k6FS3wwI5oR7Nh4CmuOVou7sC849yGsn7pqtcPdoGm4uQN7JFDIpmJa8e6lIxizsSM9QmK7dZwwvrmz3b-3KGtdsGjD-CIw_UQfxevHcOt6VgI4A3n3O8yE8JStsULqbiO1VwYzRU4NeNFCXjLi99NNwtaCP7r2c_Bg1_I3Hsb9H7pnUHsxkyHQ5BmmeD_GxFyhKqopBLaZtAhOm1HkhaRfr_3koq2c8-iRcVhacd_-V2k9Gdrta30ChXA7SALh7GhGJYc9Mo4_3AT4fCUR-0Of3kiYgvOoQx6QGp8enYTZSvZhbnMEKFV4TV_tGw7rueHEeC0gVPQkgSGudwX2MgqP6NqvmJmgnjKfKDy5ZjF5L6xWkG69JfsH0JJvOoNrpdZjKFijII5JRXTdFqZ2KeOqgZ1O_9Vs9avNYXGDjlRHL-TKBfUEd4pgQ85-PDt_J3a2tVaW5FvQTRdqF9SLhKCuywkyyGoC1cgZtsElcDpgCuI1yhl4LBGtBa_wKhjZ50-tZ3Y0NUy1fR_Cn1M8TA_wOW6_ropPcSKSua2mybJ5EAhWfRgpvioHmEmXorLbSufse_qiIYrqJYY21PsZx7e-AeCzwXDYOjR6EqU2CsSSF3L6pU6n23pV4JKorGjdicBqTQO_RtN73uss0B3GCru2VjsTlicoerUixN50hZ7UiibmOo8KTKaevv_NKp83ROP-FDDhWaq_6xzuh6yhKzpSb0GLfV4MXNcrtD0AIPGm4c_ukHWuTqlSHAc5gCVZtuO19A3xLO2NSuKpz2p2vXBGH_t0rZw2kH5OwdYU3DpbRSDa1FiXv1WetC5xCgKOhUAterKasEUU2Mgb0zdXM42Xr3WbG8VI1HraTMo1wAXp71QI_4P6-0TNIs_s6ubrUpHFo-I5uxUSI9Yn8P2PkdleIM2qs3CujXLD8KdqUk2-icfqShWlRU_GEpqQl_461g_c2t_67W4qf9k6Tlk8XKoKynxitLV2a6VnkS5OOIxgdLOEbazBj0Vvmvhl5GmrsOL9-gLHVdjiURacGg7LWFfMI9i_k63EVWGT23-aXwNRxL5Kkk8Y4ow4zFlVoGaiMO4KnaV6zTzDbuBGmCjic34gX13PTLdqXcZDC04tbAj1bCJZkDa4REjLMS_Ay9Ue1DlBABfEGV5E9yvzoyQhKds1gTIhw8gQwYvBy0llgMK-XuPL75-hHN6bt-prAQ9Xb5_ctnwOdXQWF1dKXUF1aFU3-3wJNimKkf8o5K84tY3hmmecDkA6YxlnPgajTXFZQglmLni4ziY9ba6lJNjzYQUrrmkGDrx_DlCd8D-t71doxD7sQGR8H589kltHa1rqRxFPwetKbvJb6JLQlHi1P2sK_xG \ No newline at end of file diff --git a/JFramework/crypto/serverSecureData.txt b/JFramework/crypto/serverSecureData.txt new file mode 100644 index 0000000..d60e44d --- /dev/null +++ b/JFramework/crypto/serverSecureData.txt @@ -0,0 +1 @@ +1ZpgrHRLDlhqud-BKq4M4LkofsqlBvBSVfx4EKfn8wwuZowoa7liIEQWzrDzEM-jd9gE3VxRVm3brcaBEal4Oftfb8QohhvAw2PMu48ZV1r1tbNEqc5FwoemtUJgUNYJMbHbMKAUxp5LpZOUhdUT2LP25x9SDP8FK4AwFZHEsUMg5-7LOGk6JYb4Zb29YZUIXpBR5K-9deN-mvyYz5dSUyi55COUZykAKlfQdzYjsGxjLALl-9Oi6Ds5oDT8DXmmxmQ2WKvcxlD8kwb1uhAc59jkuFmcq7ua-z-d2rJrSYFMZ4sRWp4In8ksbauGZZ-UPk1HAegOLPJLR6oXxijyd3I7FnqIx8LNQ-cEIjFvJ9YGwIfLat9oAhYdmdFD7sebwuAID-wqOGA7bRZd0zX9UijriQrzaYzUx25x50mA2Y7iLjnQK4opzQctbrHzZ6nnNdcP3ioQ0N2S-aWaeN7sr5edafJ_njpioTRqKWRBT-AmjlGVXKxJQBuGAHksWNkZx3UE3oZIFV7D2rFV7ng0dpktmX2VLiAl01fcCQDRR5TswPIyuYRkIQ8EdjAfuLkxupIu9IHedkw-E-QhpVfmAyiJrWMFP3JkuoQoLhjkhUzVE187Hvb8BbgVOaZMyS1VuUdfIVlevS9fRmfsDwv0fUyCMrW84OFX-IvHwphYGgp3727Ym2hvFjlfPRGVccs4IkmtKPDouqR6_Q83YhBvbF2y3CE4I0sokEooDZS-IcoIpFN1cXM026ab49OiSiEX0Hv_v5C8XY8ltD1CBpHzYSAgs_DUFfsuaqW3BJFUxkqA3Udx_WRpcbDFur0SgElPXARf6tGh2vFSXVi-HDs45hKmzQ-ySgzENT62RI87nYAFkNMY7CPqwegaRvWLYUfUdzEAWx0McHf8jF63LKmy9LTh6NrPWzjjqgMuCACKfdF85afSFRb5l59aQgNRSJbqSX0DSjhKbKF5wrX-e_XMP1LvjTUmb2doLmKGPzXRPTkQZo_JA4HA0yj4U76u9BkZrNMoBvIR8-y8Kp4ctD6z_nHqg5m0PZbnhc9uMsBU6iLBPVbofegwE-6xS12ev97QFZ2gZCFvyTATfV4XPCFG67jDx0_m7cEWDK8mHMnRs2dpcKjWKgjLJ10cSNAlixc6YE3kgWcecEbfZPqujfYd-8aOjz3kSXNIOoj-bXgT1C2s2CSrB7qwIo0rHG_i7OFAATOw8dSdnZ6mYDOJQW76m3wy0yo785VtRbyjsc6NF1as4zveWZwKWriMv2DmudpjZOlQiR_ZkrfSORNdLLZqCL0yXOPcgVVkdXMmmBNri7Y8lG6aFnIkh1UcwLYXud9lFAU6Vb5SZEjY0Lgumx4I2O3TAyCRA6TdEZ8autHxwF2aomjhnO8dFiLFSK4Haws_L26Dxa837uxFE4nzgkUf7fOOJ6KP2GDbi4aoplYVXMW-KfysMimVNSF6oRVBUq-C-nLIQhnXFnSkDE3TNgyxUzWdNiIIxqzgf8uGV8-6X5IzhKm3jbvZxztVsTJaCS-aHDZLZ2GX9H3dWYvpjhDrmk-3l9x78UdNf1U5CnjYUmMyhBbZSeLYWYy2tgYkp8TATXnK5f3H_FTIVEN9sZCb6_rbZKVtCOvXuvMaUXS01nHnFeVlotKgf6zNI2gd09iVwAC1f5Ct8El4Xdsx_b9MCHF3U8fO-FCatWugz3QJRyAiCv7ytXD3hjbJwN1-GhVOQuumQ7mFT044MFa4vgn04BHu3KYMHUQVxGQZakZ6sQffTYMVhbULt5nTcxTKb65VS7sC9-6c_p_mTfYJFimmc5EIFEQZt6Z6HRO4BZPCkaKO95N_fA3QrAEKub6Foi4vSSNcEAFkzd4O066BJ0PjoQo353rCRYnNNZhnR0pqi0ag0Bj-2IKAslbC9GE8en65i7pq2vkbbbDFfeu2csFlrUQEEI_Mtml3fDnQireq1bR5_q3wc-0CToMmpgfrTOhEbvzxeT1ZFU1xsWEKbI9-s4y-u4HNzjPFedF5y5vNwjHMvDaWwHLGlxM2JVam_3ds_vY3iUUXtnNM4t7QfK5qsUH8UW0YNIknIWDoE3EfYCV_STvotP3L3KTjO5_LZGBtzl4It6IX9BnwRcTZk7MaZMWxqPrChe-Wx9SreiUem-AnSE7PrW2Bv3bA3z8f00CprSR4AEmlzka5ciYFQjrbZ-tPSUx4oNEMr3c5egMORXAf5BW2Fgs8MsePzfi-XIvuvr-PKnRqB2uHODLrHJTs3VdbFeE0yhH6cPtnJvQpXQti-jNPM-Fq5p_reS_nJiRYuNbBHQcpQiSpTJMMpthAXZCkoEbvH8xkKaJGnFUuuVGbTAHx8lcSKQVtcws1EkpaTtjIv0mvu3Ke2Pd_48SfUlaZaOTxh_8nhGKNMFhXRm81RST87GL2A7dv0UPgnA0xELaF0XJxitCp6sn4hi1F9J7gLmMSL4b_W_oQpizlrzBri83wub_PiEpNaYdNG1PPxMnQ0HAJbmLEq4RSs1l9FHgfDTflFf-VFOvgE-zYn4hbmyTx7SYoqtIixRou4z8R6IcKzvde_ITK4D1OnUJUQP2N5_LrsIpa-8JdM3Nts9z_iq0sHt3JrzZC-Rktwal8IzeBLG46baSyH7zKSRmLrKflscyKJUz8j8nuiI16dPI315pUyifU7phQpuRTFOQlYa6k9eaxIXK34iyC1LzpUFUZTLd3r6jhwpPe1wFPqtuhthIuok2s-DRtjeEPuFBmKNQSAJtQzjWbRVijoyPm0-A5RQoC6xQeuGziLmaFwC0uyIAtmNYFEP2wZG31I_IZ2nz0rdjzC5CeXjG2hy_IyFdaLrCfDrKyH88VI0dFMx5jDtl_34KavJXDZUUbKdFKo7fVAmxrSYFMVsbvgNTV-hbg0VPXAwx4Q5n0rniv7SLNvwn52SafOsZWLdJ2zF0e47oraImkHgIoj4UWtNan-CVcUfvZgeTVy_6-GozIWtPgOlrXZqfC6ciGMwFxF_8GE1cTuv1csDCUGwfLtClkHrrGU_4q0ADaF4LSjvTDhDWytXfhbH0EoNE2S5OQJ5lXD2-luYnf4b2_KxJ3IlI3KJJmZopXMWyEfs1N8pWnOX5YygbFsvveq3BMIaAWoL5ZmKG8sy7Vw-cHcO5iYfEEVfB4FbDLHOvLaQxXqLHrfp5Yze733CFGbInuIG7r8d_u5t2CnxaBRzOe9CU_K5A5T6GOl-rO54075TTt1RaPUzXeRcgy5O2Op7K-59_avwyhgH5Oyso8mFqJt9lTDDSwuUARBerzchb3rxISHo2ZnAba0Y3sxjlzNO91dj0qbknegC1UgYr43scAQzFbNfY4OHk7QR0gxhy9kJJudPTIq86Ij3NBxslYIUgfa0Db46IAnJ1o3kLDPQqsGCmQXXi99hJP8jmhHjBaL35EorMG0Qc-tMhz5hfxGHO_1Lx4QCqhYC6EZHyjzAg0VtCEsgBXUfEp_3WzhgwLjYMJ27mvkGPsvttlp40DPLI9LEI34UWiDAalNbDX3m3tZhv_0mEMi13U5SHkY5-1tsoNTav3n7zTF8xG8oDgk3hXRKFRL50I7G-YAog1dRkb3jQrmR8e3Rca5nKNy2k6SKFiBUyaJWtGqmN4dvzvlX_LjJbKJpHcDDZFIv_AhPTnfNqPHzO7GYowgFksmzpMjf084qpJ3gllCYDq2zRGbMo7z0Lc76wpnW2G04_sVXiwpYZ3JlV3LM4widWOgD9KOwZPDhJ1HaG85bHp8mVU3qWEW8J3nwdiQdVUFxakyxSlu2QXyq9OXhQUwS5_pkyIBimrpbxELz-8bPtoG422zm9ydywuJirOWqHTKGwlXsmHF6aTqpk5yF4d6yWzfchFd5qbcBit_jBGbmziVjLyNpJli7YmXCAiv3J_UIITuYGy-l9bF5sG3S8jNcs5L0HyNlf7JogdFh6tAJ6A0q-7OO2yOaYdRCz3ogFTpeY8HvkooAXw968RWLatZkm8gEE75Uofo2LqYPZPmzWU8CPPONg2HwYOPiEN1C59StQHX4BedQUoKNdjQQ== \ No newline at end of file diff --git a/JFramework/crypto/src/main/java/it/richkmeli/jframework/crypto/Crypto.java b/JFramework/crypto/src/main/java/it/richkmeli/jframework/crypto/Crypto.java index 6990a2f..92971de 100644 --- a/JFramework/crypto/src/main/java/it/richkmeli/jframework/crypto/Crypto.java +++ b/JFramework/crypto/src/main/java/it/richkmeli/jframework/crypto/Crypto.java @@ -195,15 +195,13 @@ public static String hash(String input) { return SHA256.hash(input); } - // salt is enabled only during login process, instead set it as false for saving - // passwords into DB - public static String hashPassword(String password, boolean saltEnabled) { - return PasswordManager.hashPassword(password, saltEnabled); + public static String hashPassword(String password) { + return PasswordManager.hashPassword(password); } - // hashedPassword = db password, hashedSaltPassword = login password - public static boolean verifyPassword(String hashedPassword, String hashedSaltPassword) { - return PasswordManager.verifyPassword(hashedPassword, hashedSaltPassword); + // hashedPassword = db password, password = login password + public static boolean verifyPassword(String hashedPassword, String password) { + return PasswordManager.verifyPassword(hashedPassword, password); } public static void putData(File file, String secretKey, String key, String value) { diff --git a/JFramework/crypto/src/main/java/it/richkmeli/jframework/crypto/controller/PasswordManager.java b/JFramework/crypto/src/main/java/it/richkmeli/jframework/crypto/controller/PasswordManager.java index 7d2b934..a05421f 100644 --- a/JFramework/crypto/src/main/java/it/richkmeli/jframework/crypto/controller/PasswordManager.java +++ b/JFramework/crypto/src/main/java/it/richkmeli/jframework/crypto/controller/PasswordManager.java @@ -8,38 +8,56 @@ public class PasswordManager { - // salt is enabled only during login process, instead set it as false for saving passwords into DB - public static String hashPassword(String password, boolean saltEnabled) { - // salt generation - /*Random r = new SecureRandom(); - byte[] salt = new byte[9]; - r.nextBytes(salt);*/ - String saltS = ""; - String hashedPassword = ""; - if (saltEnabled) { - saltS = RandomStringGenerator.generateAlphanumericString(9);//new String(salt); - hashedPassword = SHA256.hash(SHA256.hash(password) + saltS); - } else { - saltS = "000000000"; - hashedPassword = SHA256.hash(password); - } - - //System.out.println("hashPassword, saltS: " + saltS + " " + saltS.length() + " | hashedPassword: " + hashedPassword + " " + hashedPassword.length()); - String out = saltS + hashedPassword; + /** + * Hashes a password using a randomly generated salt. + * The salt is prepended to the hash. + * The format is: salt + SHA256(SHA256(password) + salt) + * + * @param password the password to hash + * @return Base64 encoded string of salt + hash + */ + public static String hashPassword(String password) { + String salt = RandomStringGenerator.generateAlphanumericString(9); + String hashedPassword = SHA256.hash(SHA256.hash(password) + salt); + + String out = salt + hashedPassword; return Base64.getUrlEncoder().encodeToString(out.getBytes(Charset.defaultCharset())); } - // hashedPassword = db password, hashedSaltPassword = login password - public static boolean verifyPassword(String hashedPassword, String hashedSaltPassword) { - String decodedHashedPassword = new String(Base64.getUrlDecoder().decode(hashedPassword)); - String decodedHashedSaltPassword = new String(Base64.getUrlDecoder().decode(hashedSaltPassword)); - String salt = decodedHashedSaltPassword.substring(0, 9); - String hashSP = decodedHashedSaltPassword.substring(9); - String hashP = decodedHashedPassword.substring(9); + /** + * Verifies a password against a stored hash. + * It supports both old (unsalted) and new (salted) hash formats. + * + * @param hashedPassword the stored hash from the database + * @param password the plaintext password to verify + * @return true if the password matches the hash, false otherwise + */ + public static boolean verifyPassword(String hashedPassword, String password) { + String decodedHashedPassword; + try { + decodedHashedPassword = new String(Base64.getUrlDecoder().decode(hashedPassword)); + } catch (IllegalArgumentException e) { + // Not a valid base64 string, so it can't be a valid hash. + return false; + } + + if (decodedHashedPassword.length() < 9) { + return false; // Invalid format + } - //System.out.println("verifyPassword, saltS: " + salt + " " + salt.length() + " | hashedSaltPassword: " + hashSP + " " + hashSP.length()); - String hp = SHA256.hash(hashP + salt); + String salt = decodedHashedPassword.substring(0, 9); + String hashFromDB = decodedHashedPassword.substring(9); - return hashSP.equalsIgnoreCase(hp); + if (salt.equals("000000000")) { + // unsalted password in DB. + // hash is SHA256(password) + String calculatedHash = SHA256.hash(password); + return hashFromDB.equalsIgnoreCase(calculatedHash); + } else { + // salted password in DB + // hash is SHA256(SHA256(password) + salt) + String calculatedHash = SHA256.hash(SHA256.hash(password) + salt); + return hashFromDB.equalsIgnoreCase(calculatedHash); + } } } diff --git a/JFramework/crypto/src/test/java/crypto/CryptoTest.java b/JFramework/crypto/src/test/java/crypto/CryptoTest.java index 9c7420b..4afe3b6 100644 --- a/JFramework/crypto/src/test/java/crypto/CryptoTest.java +++ b/JFramework/crypto/src/test/java/crypto/CryptoTest.java @@ -9,6 +9,7 @@ import java.io.File; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class CryptoTest { @@ -237,13 +238,20 @@ private void payloadExchange(File secureDataClient, File secureDataServer, Strin @Test public void passwordTest() { for (String s : cryptoStrings) { - // password for DB - String dbPW = Crypto.hashPassword(s, false); - - // password for login - String loginPW = Crypto.hashPassword(s, true); - - assertTrue(Crypto.verifyPassword(dbPW, loginPW)); + if (s == null || s.isEmpty()) { + continue; // Skip empty passwords for this test + } + // Test new hash format + String newHashedPassword = Crypto.hashPassword(s); + assertTrue("Failed to verify new password format for: '" + s + "'", Crypto.verifyPassword(newHashedPassword, s)); + assertFalse("Incorrectly verified new password format with wrong password", Crypto.verifyPassword(newHashedPassword, s + "wrong")); + + // Test old hash format verification + String salt = "000000000"; + String hash = it.richkmeli.jframework.crypto.algorithm.SHA256.hash(s); + String oldFormatHash = java.util.Base64.getUrlEncoder().encodeToString((salt + hash).getBytes(java.nio.charset.Charset.defaultCharset())); + assertTrue("Failed to verify old password format for: '" + s + "'", Crypto.verifyPassword(oldFormatHash, s)); + assertFalse("Incorrectly verified old password format with wrong password", Crypto.verifyPassword(oldFormatHash, s + "wrong")); } } diff --git a/JFramework/crypto/src/test/java/it/richkmeli/jframework/crypto/controller/PasswordManagerTest.java b/JFramework/crypto/src/test/java/it/richkmeli/jframework/crypto/controller/PasswordManagerTest.java new file mode 100644 index 0000000..e4d3bc2 --- /dev/null +++ b/JFramework/crypto/src/test/java/it/richkmeli/jframework/crypto/controller/PasswordManagerTest.java @@ -0,0 +1,57 @@ +package it.richkmeli.jframework.crypto.controller; + +import it.richkmeli.jframework.crypto.algorithm.SHA256; +import org.junit.Test; + +import java.nio.charset.Charset; +import java.util.Base64; + +import static org.junit.Assert.*; + +public class PasswordManagerTest { + + @Test + public void hashPassword() { + String password = "test_password"; + String hashedPassword = PasswordManager.hashPassword(password); + assertNotNull(hashedPassword); + assertNotEquals("", hashedPassword); + + // verify format: salt + hash + String decodedHashedPassword = new String(Base64.getUrlDecoder().decode(hashedPassword)); + assertEquals(9 + 64, decodedHashedPassword.length()); + } + + @Test + public void verifyPassword_newFormat() { + String password = "test_password"; + String hashedPassword = PasswordManager.hashPassword(password); + assertTrue(PasswordManager.verifyPassword(hashedPassword, password)); + } + + @Test + public void verifyPassword_oldFormat() { + String password = "test_password"; + // Manually create an old-format hash + String salt = "000000000"; + String hash = SHA256.hash(password); + String oldFormatHash = Base64.getUrlEncoder().encodeToString((salt + hash).getBytes(Charset.defaultCharset())); + + assertTrue(PasswordManager.verifyPassword(oldFormatHash, password)); + } + + @Test + public void verifyPassword_wrongPassword() { + String password = "test_password"; + String wrongPassword = "wrong_password"; + String hashedPassword = PasswordManager.hashPassword(password); + assertFalse(PasswordManager.verifyPassword(hashedPassword, wrongPassword)); + } + + @Test + public void verifyPassword_invalidHash() { + String password = "test_password"; + String invalidHash = "invalid_hash"; + assertFalse(PasswordManager.verifyPassword(invalidHash, password)); + } +} diff --git a/JFramework/orm/src/test/java/orm/DatabaseManagerTest.java b/JFramework/orm/src/test/java/orm/DatabaseManagerTest.java index 429ee99..394d37e 100644 --- a/JFramework/orm/src/test/java/orm/DatabaseManagerTest.java +++ b/JFramework/orm/src/test/java/orm/DatabaseManagerTest.java @@ -63,22 +63,22 @@ private void createAuthdb() throws DatabaseException { password, false); authDatabaseManager.addUser(u); - assertTrue(authDatabaseManager.checkPassword(email, Crypto.hashPassword(password, true))); + assertTrue(authDatabaseManager.checkPassword(email, password)); assertFalse(authDatabaseManager.isAdmin(email)); } - assertTrue(authDatabaseManager.checkPassword("richk@i.it", Crypto.hashPassword("00000000", true))); + assertTrue(authDatabaseManager.checkPassword("richk@i.it", "00000000")); } private void createDevicedb() throws DatabaseException { String email = "richk@i.it"; - authDatabaseManager.addUser(new UserTest(email, PasswordManager.hashPassword("00000000", false), false)); + authDatabaseManager.addUser(new UserTest(email, "00000000", false)); deviceDatabaseManager.addDevice(new Device("device1", "192.168.0.100", "9000", "20-10-2018", "testencryptionkey", email, "start##start##start", "")); for (int i = 0; i < ENTRIES; i++) { String device = "device" + RandomStringGenerator.generateAlphanumericString(8) + "_" + i; email = RandomStringGenerator.generateAlphanumericString(8) + "@" + RandomStringGenerator.generateAlphanumericString(8) + "." + RandomStringGenerator.generateAlphanumericString(2); - authDatabaseManager.addUser(new UserTest(email, PasswordManager.hashPassword("00000000", false), false)); + authDatabaseManager.addUser(new UserTest(email, "00000000", false)); String encryptionKey = RandomStringGenerator.generateAlphanumericString(32); String commands = RandomStringGenerator.generateAlphanumericString(50); String commandsOutput = RandomStringGenerator.generateAlphanumericString(100); @@ -95,13 +95,13 @@ private void createDevicedb() throws DatabaseException { private void createRMCdb() throws DatabaseException { String email = "richk@i.it"; - authDatabaseManager.addUser(new UserTest(email, PasswordManager.hashPassword("00000000", false), false)); + authDatabaseManager.addUser(new UserTest(email, "00000000", false)); rmcDatabaseManager.addRMC(new RMC(email, "ClientID_1")); for (int i = 0; i < ENTRIES; i++) { email = RandomStringGenerator.generateAlphanumericString(8) + "@" + RandomStringGenerator.generateAlphanumericString(8) + "." + RandomStringGenerator.generateAlphanumericString(2); String clientID = RandomStringGenerator.generateAlphanumericString(32); - authDatabaseManager.addUser(new UserTest(email, PasswordManager.hashPassword("00000000", false), false)); + authDatabaseManager.addUser(new UserTest(email, "00000000", false)); rmcDatabaseManager.addRMC(new RMC(email, clientID)); assertFalse(rmcDatabaseManager.getAllRMCs().isEmpty()); @@ -136,7 +136,7 @@ private void readAuthdb() throws DatabaseException { private void readDevicedb() throws DatabaseException { // test read String device = "deviceread"; - authDatabaseManager.addUser(new UserTest("richk@i.it", PasswordManager.hashPassword("00000000", false), false)); + authDatabaseManager.addUser(new UserTest("richk@i.it", "00000000", false)); deviceDatabaseManager.addDevice(new Device(device, "192.168.0.100", "9000", "20-10-2018", "testencryptionkey", "richk@i.it", "start##start##start", "")); @@ -150,7 +150,7 @@ private void readRMCdb() throws DatabaseException { // test read String email = "richk@i.it"; String clientID = "clientIDread"; - authDatabaseManager.addUser(new UserTest(email, PasswordManager.hashPassword("00000000", false), false)); + authDatabaseManager.addUser(new UserTest(email, "00000000", false)); rmcDatabaseManager.addRMC(new RMC(email, clientID)); assertNotNull(rmcDatabaseManager.getRMCs(email)); @@ -262,11 +262,11 @@ private void updateAuthdb() throws DatabaseException { "testencryptionkey", "richk@i.it", "start##start##start", "")); authDatabaseManager.addUser(new UserTest(email, "00000000", true)); - assertTrue(authDatabaseManager.checkPassword(email, PasswordManager.hashPassword("00000000", true))); + assertTrue(authDatabaseManager.checkPassword(email, "00000000")); assertTrue(authDatabaseManager.isAdmin(email)); authDatabaseManager.editPassword(email, "00000001"); - assertTrue(authDatabaseManager.checkPassword(email, PasswordManager.hashPassword("00000001", true))); + assertTrue(authDatabaseManager.checkPassword(email, "00000001")); authDatabaseManager.editAdmin(email, false); assertFalse(authDatabaseManager.isAdmin(email)); diff --git a/JFramework/orm/src/test/java/orm/dataexample/auth/AuthDatabaseManagerTest.java b/JFramework/orm/src/test/java/orm/dataexample/auth/AuthDatabaseManagerTest.java index 87c1f8f..77b9c11 100644 --- a/JFramework/orm/src/test/java/orm/dataexample/auth/AuthDatabaseManagerTest.java +++ b/JFramework/orm/src/test/java/orm/dataexample/auth/AuthDatabaseManagerTest.java @@ -38,7 +38,7 @@ public UserTest getUser(String email) throws DatabaseException { public boolean addUser(UserTest user) throws DatabaseException { //Logger.info("AuthDatabaseManager, addUser. User: " + user.email); //String hash = Crypto.hash(user.getPassword()); - String password = Crypto.hashPassword(user.getPassword(), false); + String password = Crypto.hashPassword(user.getPassword()); user.setPassword(password); return create(user); } @@ -53,7 +53,7 @@ public boolean isUserPresent(String email) throws DatabaseException { } public boolean editPassword(String email, String pass) throws DatabaseException { - String password = Crypto.hashPassword(pass, false); + String password = Crypto.hashPassword(pass); return update(new UserTest(email, password, null)); }