PasswordVerifier.java

  1. /*
  2.  * GovWay - A customizable API Gateway
  3.  * https://govway.org
  4.  *
  5.  * Copyright (c) 2005-2025 Link.it srl (https://link.it).
  6.  *
  7.  * This program is free software: you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License version 3, as published by
  9.  * the Free Software Foundation.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18.  *
  19.  */


  20. package org.openspcoop2.utils.crypt;

  21. import java.io.File;
  22. import java.io.FileInputStream;
  23. import java.io.InputStream;
  24. import java.io.Serializable;
  25. import java.util.ArrayList;
  26. import java.util.Date;
  27. import java.util.Enumeration;
  28. import java.util.List;
  29. import java.util.Properties;

  30. import org.apache.commons.lang.StringUtils;
  31. import org.openspcoop2.utils.Utilities;
  32. import org.openspcoop2.utils.UtilsException;
  33. import org.openspcoop2.utils.date.DateManager;
  34. import org.openspcoop2.utils.regexp.RegularExpressionEngine;

  35. /**
  36.  * PasswordVerifier
  37.  *
  38.  * @author Andrea Poli (apoli@link.it)
  39.  * @author $Author$
  40.  * @version $Rev$, $Date$
  41.  */

  42. public class PasswordVerifier implements Serializable {
  43.    
  44.     private static final long serialVersionUID = 1L;
  45.    
  46.     private static final String PROPERTY_REGULAR_EXPRESSIONS_PREFIX = "passwordVerifier.regularExpression.";
  47.     private static final String PROPERTY_LOGIN_CONTAINS = "passwordVerifier.notContainsLogin";
  48.     private static final String PROPERTY_RESTRICTED_WORDS = "passwordVerifier.restrictedWords";
  49.     private static final String PROPERTY_MIN_LENGTH = "passwordVerifier.minLength";
  50.     private static final String PROPERTY_MAX_LENGTH = "passwordVerifier.maxLength";
  51.     private static final String PROPERTY_INCLUDE_LOWER_CASE_LETTER = "passwordVerifier.lowerCaseLetter";
  52.     private static final String PROPERTY_INCLUDE_UPPER_CASE_LETTER = "passwordVerifier.upperCaseLetter";
  53.     private static final String PROPERTY_INCLUDE_NUMBER = "passwordVerifier.includeNumber";
  54.     private static final String PROPERTY_INCLUDE_NOT_ALPHANUMERIC_SYMBOL = "passwordVerifier.includeNotAlphanumericSymbol";
  55.     private static final String PROPERTY_ALL_DISTINCT_CHARACTERS = "passwordVerifier.allDistinctCharacters";
  56.     private static final String PROPERTY_EXPIRE = "passwordVerifier.expireDays";
  57.     private static final String PROPERTY_HISTORY = "passwordVerifier.history";
  58.    
  59.     protected List<String> regulaExpressions = new ArrayList<>();
  60.     protected boolean notContainsLogin = false;
  61.     protected List<String> restrictedWords = new ArrayList<>();
  62.     protected int minLenght = -1;
  63.     protected int maxLenght = -1;
  64.     protected boolean includeLowerCaseLetter = false;
  65.     protected boolean includeUpperCaseLetter = false;
  66.     protected boolean includeNumber = false;
  67.     protected boolean includeNotAlphanumericSymbol = false;
  68.     protected boolean allDistinctCharacters = false;
  69.     protected boolean checkPasswordExpire;
  70.     protected int expireDays;
  71.     protected boolean history;

  72.     public PasswordVerifier(){}
  73.     public PasswordVerifier(PasswordVerifier pv){
  74.         this.regulaExpressions = pv.regulaExpressions;
  75.         this.notContainsLogin = pv.notContainsLogin;
  76.         this.restrictedWords = pv.restrictedWords;
  77.         this.minLenght = pv.minLenght;
  78.         this.maxLenght = pv.maxLenght;
  79.         this.includeLowerCaseLetter = pv.includeLowerCaseLetter;
  80.         this.includeUpperCaseLetter = pv.includeUpperCaseLetter;
  81.         this.includeNumber = pv.includeNumber;
  82.         this.includeNotAlphanumericSymbol = pv.includeNotAlphanumericSymbol;
  83.         this.allDistinctCharacters = pv.allDistinctCharacters;
  84.         this.checkPasswordExpire = pv.checkPasswordExpire;
  85.         this.expireDays = pv.expireDays;
  86.         this.history = pv.history;
  87.     }
  88.     public PasswordVerifier(String resource) throws UtilsException{
  89.         this(resource, true);
  90.     }
  91.     public PasswordVerifier(String resource, boolean useDefaultIfNotFound) throws UtilsException{
  92.         InputStream is = null;
  93.         try{
  94.             File f = new File(resource);
  95.             if(f.exists()){
  96.                 is = new FileInputStream(f);
  97.             }
  98.             else{
  99.                 is = PasswordVerifier.class.getResourceAsStream(resource);
  100.             }
  101.             if(is==null) {
  102.                 is = PasswordVerifier.class.getResourceAsStream("/org/openspcoop2/utils/crypt/consolePassword.properties");
  103.             }
  104.             if(is!=null){
  105.                 Properties p = new Properties();
  106.                 p.load(is);
  107.                 this._init(p);
  108.             }
  109.             else{
  110.                 throw new Exception("Resource ["+resource+"] not found");
  111.             }
  112.         }catch(Exception e){
  113.             throw new UtilsException(e.getMessage(),e);
  114.         }finally{
  115.             try{
  116.                 if(is!=null){
  117.                     is.close();
  118.                 }
  119.             }catch(Exception eClose){
  120.                 // close
  121.             }
  122.         }
  123.     }
  124.     public PasswordVerifier(InputStream is) throws UtilsException{
  125.         try{
  126.             Properties p = new Properties();
  127.             p.load(is);
  128.             this._init(p);
  129.         }catch(Exception e){
  130.             throw new UtilsException(e.getMessage(),e);
  131.         }
  132.     }
  133.     public PasswordVerifier(Properties p) throws UtilsException{
  134.         this._init(p);
  135.     }
  136.     private void _init(Properties p) throws UtilsException{
  137.         try{
  138.            
  139.             Properties tmpP = Utilities.readProperties(PROPERTY_REGULAR_EXPRESSIONS_PREFIX, p);
  140.             if(tmpP!=null && tmpP.size()>0){
  141.                 Enumeration<?> en =tmpP.keys();
  142.                 while (en.hasMoreElements()) {
  143.                     String key = (String) en.nextElement();
  144.                     String value = tmpP.getProperty(key);
  145.                     this.regulaExpressions.add(value);
  146.                 }
  147.             }
  148.            
  149.             String tmp = p.getProperty(PROPERTY_LOGIN_CONTAINS);
  150.             if(tmp!=null){
  151.                 tmp=tmp.trim();
  152.                 try{
  153.                     this.notContainsLogin = Boolean.parseBoolean(tmp);
  154.                 }catch(Exception e){
  155.                     throw new Exception("Property '"+PROPERTY_LOGIN_CONTAINS+"' with wrong value '"+tmp+"': "+e.getMessage(),e);
  156.                 }
  157.             }
  158.            
  159.             tmp = p.getProperty(PROPERTY_RESTRICTED_WORDS);
  160.             if(tmp!=null){
  161.                 tmp=tmp.trim();
  162.                 if(tmp.contains(",")){
  163.                     String [] split = tmp.split(",");
  164.                     for (int i = 0; i < split.length; i++) {
  165.                         this.restrictedWords.add(split[i].trim());
  166.                     }
  167.                 }
  168.                 else{
  169.                     this.restrictedWords.add(tmp);
  170.                 }
  171.             }

  172.             tmp = p.getProperty(PROPERTY_MIN_LENGTH);
  173.             if(tmp!=null){
  174.                 tmp=tmp.trim();
  175.                 try{
  176.                     this.minLenght = Integer.parseInt(tmp);
  177.                 }catch(Exception e){
  178.                     throw new Exception("Property '"+PROPERTY_MIN_LENGTH+"' with wrong value '"+tmp+"': "+e.getMessage(),e);
  179.                 }
  180.             }
  181.            
  182.             tmp = p.getProperty(PROPERTY_MAX_LENGTH);
  183.             if(tmp!=null){
  184.                 tmp=tmp.trim();
  185.                 try{
  186.                     this.maxLenght = Integer.parseInt(tmp);
  187.                 }catch(Exception e){
  188.                     throw new Exception("Property '"+PROPERTY_MAX_LENGTH+"' with wrong value '"+tmp+"': "+e.getMessage(),e);
  189.                 }
  190.             }
  191.            
  192.             tmp = p.getProperty(PROPERTY_INCLUDE_LOWER_CASE_LETTER);
  193.             if(tmp!=null){
  194.                 tmp=tmp.trim();
  195.                 try{
  196.                     this.includeLowerCaseLetter = Boolean.parseBoolean(tmp);
  197.                 }catch(Exception e){
  198.                     throw new Exception("Property '"+PROPERTY_INCLUDE_LOWER_CASE_LETTER+"' with wrong value '"+tmp+"': "+e.getMessage(),e);
  199.                 }
  200.             }
  201.            
  202.             tmp = p.getProperty(PROPERTY_INCLUDE_UPPER_CASE_LETTER);
  203.             if(tmp!=null){
  204.                 tmp=tmp.trim();
  205.                 try{
  206.                     this.includeUpperCaseLetter = Boolean.parseBoolean(tmp);
  207.                 }catch(Exception e){
  208.                     throw new Exception("Property '"+PROPERTY_INCLUDE_UPPER_CASE_LETTER+"' with wrong value '"+tmp+"': "+e.getMessage(),e);
  209.                 }
  210.             }
  211.            
  212.             tmp = p.getProperty(PROPERTY_INCLUDE_NUMBER);
  213.             if(tmp!=null){
  214.                 tmp=tmp.trim();
  215.                 try{
  216.                     this.includeNumber = Boolean.parseBoolean(tmp);
  217.                 }catch(Exception e){
  218.                     throw new Exception("Property '"+PROPERTY_INCLUDE_NUMBER+"' with wrong value '"+tmp+"': "+e.getMessage(),e);
  219.                 }
  220.             }
  221.            
  222.             tmp = p.getProperty(PROPERTY_INCLUDE_NOT_ALPHANUMERIC_SYMBOL);
  223.             if(tmp!=null){
  224.                 tmp=tmp.trim();
  225.                 try{
  226.                     this.includeNotAlphanumericSymbol = Boolean.parseBoolean(tmp);
  227.                 }catch(Exception e){
  228.                     throw new Exception("Property '"+PROPERTY_INCLUDE_NOT_ALPHANUMERIC_SYMBOL+"' with wrong value '"+tmp+"': "+e.getMessage(),e);
  229.                 }
  230.             }
  231.            
  232.             tmp = p.getProperty(PROPERTY_ALL_DISTINCT_CHARACTERS);
  233.             if(tmp!=null){
  234.                 tmp=tmp.trim();
  235.                 try{
  236.                     this.allDistinctCharacters = Boolean.parseBoolean(tmp);
  237.                 }catch(Exception e){
  238.                     throw new Exception("Property '"+PROPERTY_ALL_DISTINCT_CHARACTERS+"' with wrong value '"+tmp+"': "+e.getMessage(),e);
  239.                 }
  240.             }
  241.            
  242.             tmp = p.getProperty(PROPERTY_EXPIRE);
  243.             if(tmp!=null){
  244.                 tmp=tmp.trim();
  245.                 try{
  246.                     this.expireDays = Integer.parseInt(tmp);
  247.                     this.checkPasswordExpire = this.expireDays>0;
  248.                 }catch(Exception e){
  249.                     throw new Exception("Property '"+PROPERTY_EXPIRE+"' with wrong value '"+tmp+"': "+e.getMessage(),e);
  250.                 }
  251.             }

  252.             tmp = p.getProperty(PROPERTY_HISTORY);
  253.             if(tmp!=null){
  254.                 tmp=tmp.trim();
  255.                 try{
  256.                     this.history = Boolean.parseBoolean(tmp);
  257.                 }catch(Exception e){
  258.                     throw new Exception("Property '"+PROPERTY_HISTORY+"' with wrong value '"+tmp+"': "+e.getMessage(),e);
  259.                 }
  260.             }
  261.            
  262.         }catch(Exception e){
  263.             throw new UtilsException(e.getMessage(),e);
  264.         }
  265.     }
  266.    
  267.     public List<String> getRegulaExpressions() {
  268.         return this.regulaExpressions;
  269.     }
  270.     public void setRegulaExpressions(List<String> regulaExpressions) {
  271.         this.regulaExpressions = regulaExpressions;
  272.     }
  273.     public boolean isNotContainsLogin() {
  274.         return this.notContainsLogin;
  275.     }
  276.     public void setNotContainsLogin(boolean notContainsLogin) {
  277.         this.notContainsLogin = notContainsLogin;
  278.     }
  279.     public List<String> getRestrictedWords() {
  280.         return this.restrictedWords;
  281.     }
  282.     public void setRestrictedWords(List<String> restrictedWords) {
  283.         this.restrictedWords = restrictedWords;
  284.     }
  285.     public int getMinLenght() {
  286.         return this.minLenght;
  287.     }
  288.     public void setMinLenght(int minLenght) {
  289.         this.minLenght = minLenght;
  290.     }
  291.     public int getMaxLenght() {
  292.         return this.maxLenght;
  293.     }
  294.     public void setMaxLenght(int maxLenght) {
  295.         this.maxLenght = maxLenght;
  296.     }
  297.     public boolean isIncludeLowerCaseLetter() {
  298.         return this.includeLowerCaseLetter;
  299.     }
  300.     public void setIncludeLowerCaseLetter(boolean includeLowerCaseLetter) {
  301.         this.includeLowerCaseLetter = includeLowerCaseLetter;
  302.     }
  303.     public boolean isIncludeUpperCaseLetter() {
  304.         return this.includeUpperCaseLetter;
  305.     }
  306.     public void setIncludeUpperCaseLetter(boolean includeUpperCaseLetter) {
  307.         this.includeUpperCaseLetter = includeUpperCaseLetter;
  308.     }
  309.     public boolean isIncludeNumber() {
  310.         return this.includeNumber;
  311.     }
  312.     public void setIncludeNumber(boolean includeNumber) {
  313.         this.includeNumber = includeNumber;
  314.     }
  315.     public boolean isIncludeNotAlphanumericSymbol() {
  316.         return this.includeNotAlphanumericSymbol;
  317.     }
  318.     public void setIncludeNotAlphanumericSymbol(boolean includeNotAlphanumericSymbol) {
  319.         this.includeNotAlphanumericSymbol = includeNotAlphanumericSymbol;
  320.     }
  321.     public boolean isAllDistinctCharacters() {
  322.         return this.allDistinctCharacters;
  323.     }
  324.     public void setAllDistinctCharacters(boolean allDistinctCharacters) {
  325.         this.allDistinctCharacters = allDistinctCharacters;
  326.     }
  327.     public int getExpireDays() {
  328.         return this.expireDays;
  329.     }
  330.     public void setExpireDays(int expireDays) {
  331.         this.expireDays = expireDays;
  332.         this.checkPasswordExpire = this.expireDays>0;
  333.     }
  334.     public boolean isCheckPasswordExpire() {
  335.         return this.checkPasswordExpire;
  336.     }
  337.     public boolean isHistory() {
  338.         return this.history;
  339.     }
  340.     public void setHistory(boolean history) {
  341.         this.history = history;
  342.     }
  343.    
  344.     public boolean validate(String login, String password){
  345.         StringBuilder bf = new StringBuilder();
  346.         return this.validate(login,password,bf);
  347.     }
  348.     public boolean validate(String login, String password,StringBuilder bfMotivazioneErrore){
  349.         if(password==null) {
  350.             bfMotivazioneErrore.append("Password non fornita");
  351.             return false;
  352.         }
  353.         password = password.trim();
  354.         if(this.regulaExpressions.size()>0){
  355.             for (String regExp : this.regulaExpressions) {
  356.                 try{
  357.                     if (RegularExpressionEngine.isMatch(password, regExp) == false){
  358.                         bfMotivazioneErrore.append("La password non rispetta l'espressione regolare: "+regExp);
  359.                         return false;
  360.                     }
  361.                 }catch(Exception e){
  362.                     bfMotivazioneErrore.append("Rilevato un errore durante la verifica della password con l'espressione regolare '"+regExp+"': "+e.getMessage());
  363.                     return false;
  364.                 }
  365.             }
  366.         }
  367.         if(this.notContainsLogin){
  368.             if(password.contains(login)){
  369.                 bfMotivazioneErrore.append("La password contiene il nome di login");
  370.                 return false;
  371.             }
  372.         }
  373.         if(this.restrictedWords.size()>0){
  374.             for (String word : this.restrictedWords) {
  375.                 if(password.toLowerCase().equals(word)){
  376.                     bfMotivazioneErrore.append("La password corrisponde ad una delle parole riservate: "+word);
  377.                     return false;
  378.                 }
  379.             }
  380.         }
  381.         if(this.minLenght>0){
  382.             if(password.length()<this.minLenght){
  383.                 bfMotivazioneErrore.append("La password deve essere composta almeno da ").append(this.minLenght).
  384.                     append(" caratteri mentre quella fornita ha una lunghezza di ").append(password.length()).append(" caratteri");
  385.                 return false;
  386.             }
  387.         }
  388.         if(this.maxLenght>0){
  389.             if(password.length()>this.maxLenght){
  390.                 bfMotivazioneErrore.append("La password non deve essere composta da più di ").append(this.minLenght).
  391.                     append(" caratteri mentre quella fornita ha una lunghezza di ").append(password.length()).append(" caratteri");
  392.                 return false;
  393.             }
  394.         }
  395.         if(this.includeLowerCaseLetter){
  396.             boolean found = false;
  397.             for (int i = 0; i < password.length(); i++) {
  398.                 String c = password.charAt(i)+"";
  399.                 if(StringUtils.isAllLowerCase(c)){
  400.                     found = true;
  401.                     break;
  402.                 }
  403.             }
  404.             if(!found){
  405.                 bfMotivazioneErrore.append("La password deve contenere almeno una lettera minuscola (a - z)");
  406.                 return false;
  407.             }
  408.         }
  409.         if(this.includeUpperCaseLetter){
  410.             boolean found = false;
  411.             for (int i = 0; i < password.length(); i++) {
  412.                 String c = password.charAt(i)+"";
  413.                 if(StringUtils.isAllUpperCase(c)){
  414.                     found = true;
  415.                     break;
  416.                 }
  417.             }
  418.             if(!found){
  419.                 bfMotivazioneErrore.append("La password deve contenere almeno una lettera maiuscola (A - Z)");
  420.                 return false;
  421.             }
  422.         }
  423.         if(this.includeNumber){
  424.             boolean found = false;
  425.             for (int i = 0; i < password.length(); i++) {
  426.                 String c = password.charAt(i)+"";
  427.                 if(StringUtils.isNumeric(c)){
  428.                     found = true;
  429.                     break;
  430.                 }
  431.             }
  432.             if(!found){
  433.                 bfMotivazioneErrore.append("La password deve contenere almeno un numero (0 - 9)");
  434.                 return false;
  435.             }
  436.         }
  437.         if(this.includeNotAlphanumericSymbol){
  438.             boolean found = false;
  439.             for (int i = 0; i < password.length(); i++) {
  440.                 String c = password.charAt(i)+"";
  441.                 if(!StringUtils.isNumeric(c) && !StringUtils.isAlpha(c)){
  442.                     found = true;
  443.                     break;
  444.                 }
  445.             }
  446.             if(!found){
  447.                 bfMotivazioneErrore.append("La password deve contenere almeno un carattere non alfanumerico (ad esempio, !, $, #, %, @)");
  448.                 return false;
  449.             }
  450.         }
  451.         if(this.allDistinctCharacters){
  452.             for (int i = 0; i < password.length(); i++) {
  453.                 String c = password.charAt(i)+"";
  454.                 int count = 0;
  455.                 for (int j = 0; j < password.length(); j++) {
  456.                     String check = password.charAt(j)+"";
  457.                     if(check.equals(c)){
  458.                         count++;
  459.                     }
  460.                 }
  461.                 if(count>1){
  462.                     bfMotivazioneErrore.append("Tutti i caratteri utilizzati devono essere differenti mentre nella password fornita il carattere '"+c+"' appare "+count+" volte");
  463.                     return false;
  464.                 }
  465.             }
  466.         }
  467.         return true;
  468.     }
  469.    
  470.     public boolean isPasswordExpire(Date lastUpdatePassword){
  471.         StringBuilder bf = new StringBuilder();
  472.         return this.isPasswordExpire(lastUpdatePassword,bf);
  473.     }
  474.     public boolean isPasswordExpire(Date lastUpdatePassword, StringBuilder bfMotivazioneErrore) {
  475.         if(this.checkPasswordExpire) {
  476.             Date now = DateManager.getDate();
  477.             long expireMs = ((long) this.expireDays * 24 * 60 * 60 * 1000);
  478.             Date expireDate = new Date(lastUpdatePassword.getTime() + expireMs );
  479.             if(expireDate.before(now)) {
  480.                 bfMotivazioneErrore.append("Password impostata da più di "+this.expireDays+" giorni");
  481.                 return true;
  482.             }
  483.         }
  484.         return false;
  485.     }
  486.    
  487.     public boolean existsRestriction(){
  488.         String s = this.help("", "", false, false);
  489.         return s != null && !"".equals(s);
  490.     }
  491.    
  492.     public String help(){
  493.         return this.help("\n", "- ", true, false);
  494.     }
  495.     public String help(String separator){
  496.         return this.help(separator, "- ", true, false);
  497.     }
  498.     public boolean existsRestrictionUpdate(){
  499.         String s = this.help("", "", false, true);
  500.         return s != null && !"".equals(s);
  501.     }
  502.    
  503.     public String helpUpdate(){
  504.         return this.help("\n", "- ", true, true);
  505.     }
  506.     public String helpUpdate(String separator){
  507.         return this.help(separator, "- ", true, true);
  508.     }
  509.     public String help(String separator, String elenco, boolean premessa, boolean update){
  510.         StringBuilder bf = new StringBuilder();
  511.         if(premessa){
  512.             bf.append("La password deve rispettare i seguenti vincoli: ");
  513.         }
  514.         if(this.regulaExpressions.size()>0){
  515.             for (String regExp : this.regulaExpressions) {
  516.                 bf.append(separator);
  517.                 bf.append(elenco).append("deve soddisfare l'espressione regolare: "+regExp);
  518.             }
  519.         }
  520.         if(this.notContainsLogin){
  521.             bf.append(separator);
  522.             bf.append(elenco).append("non deve contenere il nome di login dell'utente");
  523.         }
  524.         if(this.restrictedWords.size()>0){
  525.             bf.append(separator);
  526.             bf.append(elenco).append("non deve corrispondere ad una delle seguenti parole riservate: "+this.restrictedWords);
  527.         }
  528.         if(this.minLenght>0){
  529.             bf.append(separator);
  530.             bf.append(elenco).append("deve essere composta almeno da ").append(this.minLenght).append(" caratteri");
  531.         }
  532.         if(this.maxLenght>0){
  533.             bf.append(separator);
  534.             bf.append(elenco).append("non deve essere composta da più di ").append(this.maxLenght).append(" caratteri");
  535.         }
  536.         if(this.includeLowerCaseLetter){
  537.             bf.append(separator);
  538.             bf.append(elenco).append("deve contenere almeno una lettera minuscola (a - z)");
  539.         }
  540.         if(this.includeUpperCaseLetter){
  541.             bf.append(separator);
  542.             bf.append(elenco).append("deve contenere almeno una lettera maiuscola (A - Z)");
  543.         }
  544.         if(this.includeNumber){
  545.             bf.append(separator);
  546.             bf.append(elenco).append("deve contenere almeno un numero (0 - 9)");
  547.         }
  548.         if(this.includeNotAlphanumericSymbol){
  549.             bf.append(separator);
  550.             bf.append(elenco).append("deve contenere almeno un carattere non alfanumerico (ad esempio, !, $, #, %, @)");
  551.         }
  552.         if(this.allDistinctCharacters){
  553.             bf.append(separator);
  554.             bf.append(elenco).append("tutti i caratteri utilizzati devono essere differenti");
  555.         }
  556.         if(this.history){
  557.             if(update) {
  558.                 bf.append(separator);
  559.                 bf.append(elenco).append("non deve corrispondere ad una precedente password");
  560.             }
  561.         }
  562.         return bf.toString();
  563.     }
  564. }