Bagaimana saya bisa meng-hash kata sandi di Java?


176

Saya perlu hash kata sandi untuk penyimpanan dalam database. Bagaimana saya bisa melakukan ini di Jawa?

Saya berharap untuk mengambil kata sandi teks biasa, menambahkan garam acak, lalu menyimpan garam dan kata sandi hash dalam database.

Kemudian ketika seorang pengguna ingin masuk, saya bisa mengambil kata sandi yang dikirimkan, menambahkan garam acak dari informasi akun mereka, memotongnya dan melihat apakah itu setara dengan kata sandi hash yang disimpan dengan informasi akun mereka.


11
@YGL ini sebenarnya bukan rekombinasi saat ini dengan serangan GPU yang sangat murah, keluarga SHA sebenarnya adalah pilihan yang sangat buruk untuk hashing kata sandi (terlalu cepat) bahkan dengan garam. Gunakan bcrypt, scrypt atau PBKDF2
Eran Medan

11
Mengapa pertanyaan ini ditutup? Ini adalah pertanyaan untuk masalah teknik nyata, dan jawabannya sangat berharga. OP tidak meminta perpustakaan, ia bertanya bagaimana menyelesaikan masalah teknik.
stackoverflowuser2010

12
Luar biasa. Pertanyaan ini memiliki 52 upvotes, dan seseorang memutuskan untuk menutupnya sebagai "di luar topik".
stackoverflowuser2010

1
Ya, saya sudah diposting di Meta tentang masalah penutupan ini sebelumnya, dipukuli cukup buruk.
Chris Dutrow

8
Pertanyaan ini harus dibuka kembali. Ini pertanyaan tentang bagaimana menulis program untuk memecahkan masalah yang dijelaskan (otentikasi kata sandi), dengan solusi kode pendek. Melihat kata pemicu "perpustakaan" tidak membenarkan secara tertutup untuk menutup pertanyaan; dia tidak meminta rekomendasi perpustakaan, dia bertanya bagaimana cara hash kata sandi. Sunting: Di sana, perbaiki.
erickson

Jawaban:


157

Anda benar-benar dapat menggunakan fasilitas bawaan Java runtime untuk melakukan ini. SunJCE di Java 6 mendukung PBKDF2, yang merupakan algoritma yang baik untuk digunakan untuk hashing kata sandi.

byte[] salt = new byte[16];
random.nextBytes(salt);
KeySpec spec = new PBEKeySpec("password".toCharArray(), salt, 65536, 128);
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] hash = f.generateSecret(spec).getEncoded();
Base64.Encoder enc = Base64.getEncoder();
System.out.printf("salt: %s%n", enc.encodeToString(salt));
System.out.printf("hash: %s%n", enc.encodeToString(hash));

Berikut kelas utilitas yang dapat Anda gunakan untuk otentikasi kata sandi PBKDF2:

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;
import java.util.Base64;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;

/**
 * Hash passwords for storage, and test passwords against password tokens.
 * 
 * Instances of this class can be used concurrently by multiple threads.
 *  
 * @author erickson
 * @see <a href="http://stackoverflow.com/a/2861125/3474">StackOverflow</a>
 */
public final class PasswordAuthentication
{

  /**
   * Each token produced by this class uses this identifier as a prefix.
   */
  public static final String ID = "$31$";

  /**
   * The minimum recommended cost, used by default
   */
  public static final int DEFAULT_COST = 16;

  private static final String ALGORITHM = "PBKDF2WithHmacSHA1";

  private static final int SIZE = 128;

  private static final Pattern layout = Pattern.compile("\\$31\\$(\\d\\d?)\\$(.{43})");

  private final SecureRandom random;

  private final int cost;

  public PasswordAuthentication()
  {
    this(DEFAULT_COST);
  }

  /**
   * Create a password manager with a specified cost
   * 
   * @param cost the exponential computational cost of hashing a password, 0 to 30
   */
  public PasswordAuthentication(int cost)
  {
    iterations(cost); /* Validate cost */
    this.cost = cost;
    this.random = new SecureRandom();
  }

  private static int iterations(int cost)
  {
    if ((cost < 0) || (cost > 30))
      throw new IllegalArgumentException("cost: " + cost);
    return 1 << cost;
  }

  /**
   * Hash a password for storage.
   * 
   * @return a secure authentication token to be stored for later authentication 
   */
  public String hash(char[] password)
  {
    byte[] salt = new byte[SIZE / 8];
    random.nextBytes(salt);
    byte[] dk = pbkdf2(password, salt, 1 << cost);
    byte[] hash = new byte[salt.length + dk.length];
    System.arraycopy(salt, 0, hash, 0, salt.length);
    System.arraycopy(dk, 0, hash, salt.length, dk.length);
    Base64.Encoder enc = Base64.getUrlEncoder().withoutPadding();
    return ID + cost + '$' + enc.encodeToString(hash);
  }

  /**
   * Authenticate with a password and a stored password token.
   * 
   * @return true if the password and token match
   */
  public boolean authenticate(char[] password, String token)
  {
    Matcher m = layout.matcher(token);
    if (!m.matches())
      throw new IllegalArgumentException("Invalid token format");
    int iterations = iterations(Integer.parseInt(m.group(1)));
    byte[] hash = Base64.getUrlDecoder().decode(m.group(2));
    byte[] salt = Arrays.copyOfRange(hash, 0, SIZE / 8);
    byte[] check = pbkdf2(password, salt, iterations);
    int zero = 0;
    for (int idx = 0; idx < check.length; ++idx)
      zero |= hash[salt.length + idx] ^ check[idx];
    return zero == 0;
  }

  private static byte[] pbkdf2(char[] password, byte[] salt, int iterations)
  {
    KeySpec spec = new PBEKeySpec(password, salt, iterations, SIZE);
    try {
      SecretKeyFactory f = SecretKeyFactory.getInstance(ALGORITHM);
      return f.generateSecret(spec).getEncoded();
    }
    catch (NoSuchAlgorithmException ex) {
      throw new IllegalStateException("Missing algorithm: " + ALGORITHM, ex);
    }
    catch (InvalidKeySpecException ex) {
      throw new IllegalStateException("Invalid SecretKeyFactory", ex);
    }
  }

  /**
   * Hash a password in an immutable {@code String}. 
   * 
   * <p>Passwords should be stored in a {@code char[]} so that it can be filled 
   * with zeros after use instead of lingering on the heap and elsewhere.
   * 
   * @deprecated Use {@link #hash(char[])} instead
   */
  @Deprecated
  public String hash(String password)
  {
    return hash(password.toCharArray());
  }

  /**
   * Authenticate with a password in an immutable {@code String} and a stored 
   * password token. 
   * 
   * @deprecated Use {@link #authenticate(char[],String)} instead.
   * @see #hash(String)
   */
  @Deprecated
  public boolean authenticate(String password, String token)
  {
    return authenticate(password.toCharArray(), token);
  }

}

11
Anda mungkin ingin sedikit berhati-hati dengan konversi byte ke hex dengan BigInteger: nol di depan dihapus. Tidak apa-apa untuk debug cepat, tetapi saya telah melihat bug dalam kode produksi karena efek itu.
Thomas Pornin

24
@ thomas-pornin menyoroti mengapa kita membutuhkan perpustakaan , bukan blok kode yang hampir ada. Menakutkan bahwa jawaban yang diterima tidak menjawab pertanyaan tentang topik yang begitu penting.
Nilzor

9
Gunakan algoritma PBKDF2WithHmacSHA512 dimulai dengan Java 8. Ini sedikit lebih kuat.
iwan.z

1
Catatan, algs yang ada tidak dihapus dalam versi: java_4: PBEWithMD5AndDES, DESede, DES java_5 / 6/7: PBKDF2WithHmacSHA1, PBE (hanya di Jawa 5), PBEWithSHA1AndRC2_40, PBEWithSHA1And, PBEWithMD5AndTriple java_8: PBEWithHmacSHA224AndAES_128, PBEWithHmacSHA384AndAES_128, PBEWithHmacSHA512AndAES_128, RC4_40, PBKDF2WithHmacSHA256 , PBEWithHmacSHA1AndAES_128, RC4_128, PBKDF2WithHmacSHA224, PBEWithHmacSHA256AndAES_256, RC2_128, PBEWithHmacSHA224AndAES_256, PBEWithHmacSHA384AndAES_256, PBEWithHmacSHA512AndAES_256, PBKDF2WithHmacSHA512, PBEWithHmacSHA256AndAES_128, PBKDF2WithHmacSHA384, PBEWithHmacSHA1AndAES_256
iwan.z

4
@TheTosters Ya, waktu eksekusi akan lebih lama kata sandi yang salah ; lebih khusus, kata sandi yang salah akan memakan waktu yang sama dengan kata sandi yang benar. Ini mencegah serangan waktu, meskipun saya akui saya tidak bisa memikirkan cara praktis untuk mengeksploitasi kerentanan seperti ini dalam kasus ini. Tapi Anda tidak perlu mengambil jalan pintas. Hanya karena saya tidak bisa melihatnya, bukan berarti pikiran yang lebih licik tidak akan melihatnya.
erickson

97

Berikut ini adalah implementasi lengkap dengan dua metode melakukan apa yang Anda inginkan:

String getSaltedHash(String password)
boolean checkPassword(String password, String stored)

Intinya adalah bahwa bahkan jika penyerang mendapatkan akses ke database dan kode sumber Anda, kata sandi tetap aman.

import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.SecureRandom;
import org.apache.commons.codec.binary.Base64;

public class Password {
    // The higher the number of iterations the more 
    // expensive computing the hash is for us and
    // also for an attacker.
    private static final int iterations = 20*1000;
    private static final int saltLen = 32;
    private static final int desiredKeyLen = 256;

    /** Computes a salted PBKDF2 hash of given plaintext password
        suitable for storing in a database. 
        Empty passwords are not supported. */
    public static String getSaltedHash(String password) throws Exception {
        byte[] salt = SecureRandom.getInstance("SHA1PRNG").generateSeed(saltLen);
        // store the salt with the password
        return Base64.encodeBase64String(salt) + "$" + hash(password, salt);
    }

    /** Checks whether given plaintext password corresponds 
        to a stored salted hash of the password. */
    public static boolean check(String password, String stored) throws Exception{
        String[] saltAndHash = stored.split("\\$");
        if (saltAndHash.length != 2) {
            throw new IllegalStateException(
                "The stored password must have the form 'salt$hash'");
        }
        String hashOfInput = hash(password, Base64.decodeBase64(saltAndHash[0]));
        return hashOfInput.equals(saltAndHash[1]);
    }

    // using PBKDF2 from Sun, an alternative is https://github.com/wg/scrypt
    // cf. http://www.unlimitednovelty.com/2012/03/dont-use-bcrypt.html
    private static String hash(String password, byte[] salt) throws Exception {
        if (password == null || password.length() == 0)
            throw new IllegalArgumentException("Empty passwords are not supported.");
        SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        SecretKey key = f.generateSecret(new PBEKeySpec(
            password.toCharArray(), salt, iterations, desiredKeyLen));
        return Base64.encodeBase64String(key.getEncoded());
    }
}

Kami menyimpan 'salt$iterated_hash(password, salt)' . Garamnya adalah 32 byte acak dan tujuannya adalah jika dua orang yang berbeda memilih kata sandi yang sama, kata sandi yang disimpan akan tetap terlihat berbeda.

Itu iterated_hash, yang pada dasarnya hash(hash(hash(... hash(password, salt) ...)))membuatnya sangat mahal bagi penyerang potensial yang memiliki akses ke database Anda untuk menebak kata sandi, hash mereka, dan mencari hash dalam database. Anda harus menghitung ini iterated_hashsetiap kali pengguna login, tetapi itu tidak dikenakan biaya banyak dibandingkan dengan penyerang yang menghabiskan hampir 100% dari hash komputasi waktu mereka.


14
Maaf mengomel, tetapi mengapa saya harus memilih ini dari perpustakaan yang ada? Perpustakaan mungkin memiliki peluang lebih tinggi untuk ditinjau secara menyeluruh. Saya ragu bahwa setiap dari 14 suara yang dipilih menganalisis kode untuk masalah apa pun.
Joachim Sauer

2
@ JoachimSauer Ini pada dasarnya hanya menggunakan perpustakaan (javax.crypto) tetapi Anda benar - kata sandi kosong tidak didukung. Menambahkan pengecualian untuk membuatnya eksplisit. Terima kasih!
Martin Konicek

3
Anda mungkin harus mengubah metode tanda tangan char[] passwordalih-alih String password.
assylias

2
Meskipun tampaknya referensi tidak menerima kesepakatan dengan suara bulat. Lihat juga ini: security.stackexchange.com/a/20369/12614
assylias

3
Apakah Anda yakin bahwa .equals () pada string tidak mengalami hubungan pendek (yaitu: stop looping ketika menemukan dua byte yang tidak sama)? Jika ada, ada risiko serangan waktu yang membocorkan informasi tentang hash kata sandi.
bobpoekert


7

Anda dapat menggunakan implementasi perpustakaan Shiro (sebelumnya JSecurity ) dari apa yang dijelaskan oleh OWASP .

Ini juga terlihat seperti perpustakaan JASYPT yang memiliki utilitas serupa .


Itulah sebenarnya yang saya gunakan. Tetapi karena kami memutuskan untuk tidak menggunakan Shiro, ada beberapa kekhawatiran tentang ketidakefisienan karena harus menyertakan seluruh perpustakaan Shiro hanya untuk satu paket itu.
Chris Dutrow

Saya tidak tahu perpustakaan yang dibuat hanya dari utilitas hashing kata sandi. Anda mungkin lebih baik menggulirkan Anda sendiri jika ketergantungan menjadi perhatian. Jawaban oleh erickson terlihat cukup bagus untuk saya. Atau cukup salin kode dari tautan OWASP yang saya referensikan jika Anda lebih suka menggunakan SHA secara aman.
laz

7

Anda dapat menghitung hash menggunakan MessageDigest, tetapi ini salah dalam hal keamanan. Hash tidak boleh digunakan untuk menyimpan kata sandi, karena mudah pecah.

Anda harus menggunakan algoritma lain seperti bcrypt, PBKDF2 dan scrypt untuk menyimpan kata sandi Anda. Lihat di sini .


3
Bagaimana Anda akan hash kata sandi saat login tanpa menyimpan garam di database?
ZZ Coder

9
Menggunakan nama pengguna sebagai garam bukanlah cacat yang fatal, tetapi itu sama sekali tidak sebaik menggunakan garam dari RNG kriptografi. Dan sama sekali tidak ada masalah menyimpan garam dalam database. Garam itu bukan rahasia.
erickson

1
Bukankah nama pengguna dan email juga disimpan dalam database?
Chris Dutrow

@ZZ Coder, @erickson benar, saya entah bagaimana beranggapan bahwa itu akan menjadi satu garam untuk semua kata sandi, yang akan mengarah ke tabel pelangi yang mudah dihitung.
Bozho

13
Satu masalah dengan menggunakan nama pengguna (atau ID lain seperti email) sebagai garam adalah Anda tidak dapat mengubah ID tanpa meminta pengguna juga menetapkan kata sandi baru.
Lawrence Dol

6

Selain bcrypt dan PBKDF2 yang disebutkan dalam jawaban lain, saya akan merekomendasikan melihat scrypt

MD5 dan SHA-1 tidak direkomendasikan karena relatif cepat sehingga menggunakan komputasi terdistribusi "sewa per jam" (misalnya EC2) atau GPU modern kelas atas yang dapat "memecahkan" kata sandi menggunakan serangan brute force / kamus dengan biaya yang relatif rendah dan masuk akal waktu.

Jika Anda harus menggunakannya, maka paling tidak iterasi algoritma yang telah ditentukan dalam jumlah signifikan (1000+).


6

Sepenuhnya setuju dengan Erickson bahwa PBKDF2 adalah jawabannya.

Jika Anda tidak memiliki opsi itu, atau hanya perlu menggunakan hash, Apache Commons DigestUtils jauh lebih mudah daripada mendapatkan kode JCE dengan benar: https://commons.apache.org/proper/commons-codec/apidocs/org/apache /commons/codec/digest/DigestUtils.html

Jika Anda menggunakan hash, gunakan sha256 atau sha512. Halaman ini memiliki rekomendasi yang baik untuk penanganan dan hashing kata sandi (perhatikan ini tidak merekomendasikan hashing untuk penanganan kata sandi): http://www.daemonology.net/blog/2009-06-11-cryptographic-right-answers.html


Perlu dicatat bahwa SHA512 tidak lebih baik daripada SHA256 (untuk tujuan ini) hanya karena jumlahnya lebih besar.
Azsgy

5

Anda dapat menggunakan Spring Security Crypto (hanya memiliki 2 dependensi kompilasi opsional ), yang mendukung PBKDF2 , BCrypt , SCrypt , dan enkripsi kata sandi Argon2 .

Argon2PasswordEncoder argon2PasswordEncoder = new Argon2PasswordEncoder();
String aCryptedPassword = argon2PasswordEncoder.encode("password");
boolean passwordIsValid = argon2PasswordEncoder.matches("password", aCryptedPassword);
SCryptPasswordEncoder sCryptPasswordEncoder = new SCryptPasswordEncoder();
String sCryptedPassword = sCryptPasswordEncoder.encode("password");
boolean passwordIsValid = sCryptPasswordEncoder.matches("password", sCryptedPassword);
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
String bCryptedPassword = bCryptPasswordEncoder.encode("password");
boolean passwordIsValid = bCryptPasswordEncoder.matches("password", bCryptedPassword);
Pbkdf2PasswordEncoder pbkdf2PasswordEncoder = new Pbkdf2PasswordEncoder();
String pbkdf2CryptedPassword = pbkdf2PasswordEncoder.encode("password");
boolean passwordIsValid = pbkdf2PasswordEncoder.matches("password", pbkdf2CryptedPassword);

4

Sementara rekomendasi NIST PBKDF2 telah disebutkan, saya ingin menunjukkan bahwa ada kompetisi hashing kata sandi publik yang berlangsung dari 2013 hingga 2015. Pada akhirnya, Argon2 dipilih sebagai fungsi hashing kata sandi yang direkomendasikan.

Ada Java binding yang diadopsi dengan cukup baik untuk pustaka asli (asli C) yang dapat Anda gunakan.

Dalam kasus penggunaan rata-rata, saya tidak berpikir itu penting dari perspektif keamanan jika Anda memilih PBKDF2 daripada Argon2 atau sebaliknya. Jika Anda memiliki persyaratan keamanan yang kuat, saya sarankan mempertimbangkan Argon2 dalam evaluasi Anda.

Untuk informasi lebih lanjut tentang keamanan fungsi hashing kata sandi, lihat security.se .


@ zaph Saya mengedit jawaban untuk lebih objektif. Perlu diketahui bahwa rekomendasi NIST mungkin tidak selalu menjadi pilihan terbaik (lihat di sini untuk contoh) - tentu saja ini berlaku untuk apa pun yang direkomendasikan di tempat lain juga. Karena itu saya pikir jawaban ini memberikan nilai untuk pertanyaan ini.
Qw3ry

2

Di sini Anda memiliki dua tautan untuk hashing MD5 dan metode hash lainnya:

API Javadoc: https://docs.oracle.com/javase/1.5.0/docs/api/java/security/MessageDigest.html

Tutorial: http://www.twmacinta.com/myjava/fast_md5.php


3
Perlu diingat bahwa untuk hashing kata sandi, lebih lambat lebih baik. Anda harus menggunakan ribuan iterasi fungsi hash sebagai teknik "penguatan kunci". Juga, garam sangat penting.
erickson

Saya mendapat kesan bahwa beberapa iterasi dari algoritma hashing kualitas akan menghasilkan keamanan yang sama dengan satu iterasi karena panjang byte akan tetap sama?
Chris Dutrow

@erickson Akan lebih baik untuk memperlambat penyerang secara eksplisit.
deamon

6
Tentang penguatan kunci: Garam ada untuk membuat hash yang dikomputasi tidak dapat digunakan. Namun penyerang tidak harus melakukan precompute. Penyerang dapat dengan mudah memasukkan string + garam "on the fly" hingga mereka menemukan yang tepat. Tetapi jika Anda mengulangi ribuan kali untuk hash Anda, mereka harus melakukan hal yang sama. Server Anda tidak akan banyak terpengaruh oleh iterasi 10r karena tidak sering terjadi. Penyerang akan membutuhkan 10k kali daya komputasi.
zockman

2
@Simon hari ini MD5 dianggap tidak berguna untuk hashing kata sandi karena dapat di-crack dalam hitungan detik menggunakan GPU brute force / serangan kamus. Lihat di sini: codahale.com/how-to-safely-store-a-password
Eran Medan

1

Di antara semua skema hash standar, ssha LDAP adalah yang paling aman untuk digunakan,

http://www.openldap.org/faq/data/cache/347.html

Saya hanya akan mengikuti algoritma yang ditentukan di sana dan menggunakan MessageDigest untuk melakukan hash.

Anda perlu menyimpan garam di database Anda seperti yang Anda sarankan.


1
Karena SSHA tidak mengulangi fungsi hash, itu terlalu cepat. Ini memungkinkan penyerang untuk mencoba kata sandi lebih cepat. Algoritma yang lebih baik seperti Bcrypt, PBBKDF1, dan PBKDF2 menggunakan teknik "penguatan kunci" untuk memperlambat penyerang ke titik di mana kata sandi harus kedaluwarsa sebelum mereka dapat memaksa paksa bahkan ruang password 8 huruf.
erickson

Masalah dengan semua mekanisme ini adalah Anda tidak mendapatkan dukungan klien. Masalah dengan kata sandi hash adalah bahwa Anda tidak dapat mendukung kata sandi hash dengan algoritma lain. Dengan ssha, setidaknya semua klien LDAP mendukungnya.
ZZ Coder

Ini bukan "paling aman" itu hanya "cukup kompatibel". bcrypt / scrypt adalah cara yang lebih intensif untuk sumber daya.
eckes

1

Pada 2020, algoritma yang paling kredibel dan fleksibel digunakan,

yang paling mungkin untuk mengoptimalkan kekuatannya diberikan perangkat keras apa pun,

adalah Argon2id atau Argon2i .

Ini menyediakan alat kalibrasi yang diperlukan untuk menemukan parameter kekuatan yang dioptimalkan mengingat waktu hashing target dan perangkat keras yang digunakan.

  • Argon2i adalah spesialis dalam hashing serakah memori
  • Argon2d adalah khusus dalam hashing CPU serakah
  • Argon2id menggunakan kedua metode ini.

Memory greedy hashing akan membantu melawan penggunaan GPU untuk cracking.

Implementasi keamanan Spring / Bouncy Castle tidak dioptimalkan dan relatif seminggu mengingat apa yang bisa digunakan penyerang. lih: dokumentasi Spring

Implementasi saat ini menggunakan Bouncy castle yang tidak mengeksploitasi paralelisme / optimisasi yang akan dilakukan oleh cracker kata sandi, sehingga ada asimetri yang tidak perlu antara penyerang dan pembela.

Implementasi paling kredibel yang digunakan untuk java adalah mkammerer ,

tabung pembungkus / perpustakaan dari implementasi asli resmi yang ditulis dalam Rust.

Ini ditulis dengan baik dan mudah digunakan.

Versi yang disematkan menyediakan versi bawaan untuk Linux, windows dan OSX.

Sebagai contoh, ini digunakan oleh jpmorganchase dalam proyek keamanan tessera yang digunakan untuk mengamankan Quorum , implementasi cryptocurency Ethereum-nya.

Berikut ini contoh kode dari tessera.

Kalibrasi dapat dilakukan menggunakan de.mkammerer.argon2.Argon2Helper # findIterations

Algoritma SCRYPT dan Pbkdf2 mungkin juga dikalibrasi dengan menulis beberapa tolok ukur sederhana, tetapi nilai iterasi aman minimal saat ini, akan membutuhkan waktu hashing yang lebih tinggi.


0

Saya bersandar pada video di udemy dan diedit menjadi kata sandi acak yang lebih kuat

}

private String pass() {
        String passswet="1234567890zxcvbbnmasdfghjklop[iuytrtewq@#$%^&*" ;

        char icon1;
        char[] t=new char[20];

         int rand1=(int)(Math.random()*6)+38;//to make a random within the range of special characters

            icon1=passswet.charAt(rand1);//will produce char with a special character

        int i=0;
        while( i <11) {

             int rand=(int)(Math.random()*passswet.length());
             //notice (int) as the original value of Math>random() is double

             t[i] =passswet.charAt(rand);

             i++;
                t[10]=icon1;
//to replace the specified item with icon1
         }
        return new String(t);
}






}

Saya terbuka untuk dikoreksi, tetapi saya pikir Anda tidak boleh menggunakan angka acak saat hashing. Ini agar fungsi hash Anda tetap deterministik; itu adalah jika Anda hash string beberapa kali Anda akan selalu mendapatkan nilai hash yang sama kembali untuk string itu.
duldi
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.