PasswordGenerator.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.InputStream;
  22. import java.io.Serializable;
  23. import java.security.SecureRandom;
  24. import java.util.ArrayList;
  25. import java.util.Collections;
  26. import java.util.List;
  27. import java.util.Properties;

  28. import org.openspcoop2.utils.UtilsException;
  29. import org.openspcoop2.utils.io.Base64Utilities;
  30. import org.openspcoop2.utils.io.HexBinaryUtilities;

  31. /**
  32.  * PasswordGenerator
  33.  *
  34.  * @author Andrea Poli (apoli@link.it)
  35.  * @author $Author$
  36.  * @version $Rev$, $Date$
  37.  */

  38. public class PasswordGenerator extends PasswordVerifier implements Serializable {
  39.    
  40.     private static final long serialVersionUID = 1L;
  41.    
  42.     public static PasswordGenerator DEFAULT;
  43.     static {
  44.         DEFAULT = new PasswordGenerator();
  45.         DEFAULT.setIncludeLowerCaseLetter(true);
  46.         DEFAULT.setIncludeUpperCaseLetter(true);
  47.         DEFAULT.setIncludeNumber(true);
  48.         DEFAULT.setIncludeNotAlphanumericSymbol(true);
  49.     }
  50.    
  51.     public static void main(String[] args) throws UtilsException {
  52.         // Metodo utilizzato dal setup antinstaller
  53.         System.out.println(DEFAULT.generate());
  54.     }
  55.    
  56.     public PasswordGenerator(){
  57.         super();
  58.     }
  59.     public PasswordGenerator(String resource) throws UtilsException{
  60.         super(resource);
  61.     }
  62.     public PasswordGenerator(InputStream is) throws UtilsException{
  63.         super(is);
  64.     }
  65.     public PasswordGenerator(Properties p) throws UtilsException{
  66.         super(p);
  67.     }
  68.     public PasswordGenerator(PasswordVerifier pv) throws UtilsException{
  69.         super(pv);
  70.         if(!this.includeLowerCaseLetter && !this.includeUpperCaseLetter && !this.includeNumber) {
  71.             // non essendoci vincoli genero password come il generatore di default
  72.             this.includeLowerCaseLetter = DEFAULT.isIncludeLowerCaseLetter();
  73.             this.includeUpperCaseLetter = DEFAULT.isIncludeUpperCaseLetter();
  74.             this.includeNumber = DEFAULT.isIncludeNumber();
  75.         }
  76.     }
  77.    
  78.     private String dictionaryChars = "abcdefghijklmnopqrstuvwxyz";
  79.     private String dictionaryNumbers = "1234567890";
  80.     private String dictionaryAlpha = "!?@#$%^&*()-+<>.:_";

  81.     public String getDictionaryChars() {
  82.         return this.dictionaryChars;
  83.     }
  84.     public void setDictionaryChars(String dictionaryChars) {
  85.         this.dictionaryChars = dictionaryChars;
  86.     }
  87.     public String getDictionaryNumbers() {
  88.         return this.dictionaryNumbers;
  89.     }
  90.     public void setDictionaryNumbers(String dictionaryNumbers) {
  91.         this.dictionaryNumbers = dictionaryNumbers;
  92.     }
  93.     public String getDictionaryAlpha() {
  94.         return this.dictionaryAlpha;
  95.     }
  96.     public void setDictionaryAlpha(String dictionaryAlpha) {
  97.         this.dictionaryAlpha = dictionaryAlpha;
  98.     }
  99.    
  100.     private int defaultLength = 10;
  101.     public int getDefaultLength() {
  102.         return this.defaultLength;
  103.     }
  104.     public void setDefaultLength(int defaultLength) {
  105.         this.defaultLength = defaultLength;
  106.     }
  107.    
  108.     private boolean base64 = false;
  109.     private boolean hex = false;
  110.     public boolean isBase64() {
  111.         return this.base64;
  112.     }
  113.     public void setBase64(boolean base64) {
  114.         this.base64 = base64;
  115.     }
  116.     public boolean isHex() {
  117.         return this.hex;
  118.     }
  119.     public void setHex(boolean hex) {
  120.         this.hex = hex;
  121.     }
  122.    
  123.     private String prefix = null;
  124.     private String suffix = null;
  125.     public String getPrefix() {
  126.         return this.prefix;
  127.     }
  128.     public void setPrefix(String prefix) {
  129.         this.prefix = prefix;
  130.     }
  131.     public String getSuffix() {
  132.         return this.suffix;
  133.     }
  134.     public void setSuffix(String suffix) {
  135.         this.suffix = suffix;
  136.     }
  137.    
  138.     public String generate() throws UtilsException{
  139.                
  140.         if(this.minLenght>0){
  141.             if(this.defaultLength<this.minLenght) {
  142.                 this.defaultLength = this.minLenght;
  143.             }
  144.         }
  145.        
  146.         if(this.maxLenght>0){
  147.             if(this.defaultLength>this.maxLenght){
  148.                 this.defaultLength = this.maxLenght;
  149.             }
  150.         }
  151.        
  152.         return this.generate(this.defaultLength);
  153.     }
  154.     public String generate(int length) throws UtilsException{
  155.         return this.generate("login",length);
  156.     }
  157.     public String generate(String username, int length) throws UtilsException{
  158.        
  159.         if(this.minLenght>0){
  160.             if(length<this.minLenght){
  161.                 throw new UtilsException("La password deve essere composta almeno da "+this.minLenght+" caratteri");
  162.             }
  163.         }
  164.         if(this.maxLenght>0){
  165.             if(length>this.maxLenght){
  166.                 throw new UtilsException("La password non deve essere composta da più di "+this.minLenght+" caratteri");
  167.             }
  168.         }
  169.        
  170.         int tentativi = 100;
  171.         for (int i = 0; i < tentativi; i++) {
  172.             String password = _generate(length);
  173.             if(this.validate(username, password)) {
  174.                
  175.                 if(this.base64) {
  176.                     password =  Base64Utilities.encodeAsString(password.getBytes());
  177.                 }
  178.                 else if(this.hex) {
  179.                     password = HexBinaryUtilities.encodeAsString(password.getBytes());
  180.                 }
  181.                
  182.                 // NOTA i prefissi e i suffissi non si codificano in modo che si possa aggiungere caratteri speciali che consentano di identificare le parti una volta effettuata la codifica (es. il '.' in base64)
  183.                 if(this.prefix!=null) {
  184.                     password = this.prefix + password;
  185.                 }
  186.                 if(this.suffix!=null) {
  187.                     password = password + this.suffix;
  188.                 }
  189.                
  190.                 return password;
  191.             }
  192.         }
  193.        
  194.         throw new UtilsException("La generazione non è riuscita a produrre una password che soddisfi tutti i vincoli");
  195.     }
  196.    
  197.     //private Random random = new Random();
  198.     private SecureRandom random = new SecureRandom();
  199.    
  200.     private String _generate(int length) throws UtilsException{
  201.        
  202.         if(!this.includeLowerCaseLetter && !this.includeUpperCaseLetter && !this.includeNumber) {
  203.             throw new UtilsException("La generazione richiede almeno che l'utilizzo di numeri o caratteri sia abilitato");
  204.         }
  205.        
  206.         List<String> password = new ArrayList<>();

  207.        
  208.         String tmpDictionaryCharsLowerCase = new String(this.dictionaryChars);
  209.         String tmpDictionaryCharsUpperCase = new String(this.dictionaryChars);
  210.         String tmpDictionaryNumbers = new String(this.dictionaryNumbers);
  211.         String tmpDictionaryAlpha = new String(this.dictionaryAlpha);
  212.        
  213.         int i = 0;
  214.         if(this.includeNotAlphanumericSymbol){
  215.             int randomOffset = this.random.nextInt(tmpDictionaryAlpha.length());
  216.             String s = tmpDictionaryAlpha.charAt(randomOffset)+"";
  217.             password.add(s);
  218.             i++;
  219.             if(this.allDistinctCharacters){
  220.                 tmpDictionaryAlpha = tmpDictionaryAlpha.replace(s, "");
  221.             }
  222.         }
  223.         for (; i < length;) {
  224.             boolean addChar = false;
  225.             if(this.includeLowerCaseLetter){
  226.                 if(tmpDictionaryCharsLowerCase.length()>0) {
  227.                     int randomOffset = this.random.nextInt(tmpDictionaryCharsLowerCase.length());
  228.                     String s = tmpDictionaryCharsLowerCase.charAt(randomOffset)+"";
  229.                     password.add(s);
  230.                     addChar = true;
  231.                     i++;
  232.                     if(this.allDistinctCharacters){
  233.                         tmpDictionaryCharsLowerCase = tmpDictionaryCharsLowerCase.replace(s, "");
  234.                     }
  235.                 }
  236.             }
  237.             if(i >= length) {
  238.                 break;
  239.             }
  240.             if(this.includeUpperCaseLetter){
  241.                 if(tmpDictionaryCharsUpperCase.length()>0) {
  242.                     int randomOffset = this.random.nextInt(tmpDictionaryCharsUpperCase.length());
  243.                     String s = tmpDictionaryCharsUpperCase.charAt(randomOffset)+"";
  244.                     password.add(s.toUpperCase());
  245.                     addChar = true;
  246.                     i++;
  247.                     if(this.allDistinctCharacters){
  248.                         tmpDictionaryCharsUpperCase = tmpDictionaryCharsUpperCase.replace(s, "");
  249.                     }
  250.                 }
  251.             }
  252.             if(i >= length) {
  253.                 break;
  254.             }
  255.             if(this.includeNumber){
  256.                 if(tmpDictionaryNumbers.length()>0) {
  257.                     int randomOffset = this.random.nextInt(tmpDictionaryNumbers.length());
  258.                     String s = tmpDictionaryNumbers.charAt(randomOffset)+"";
  259.                     password.add(s);
  260.                     addChar = true;
  261.                     i++;
  262.                     if(this.allDistinctCharacters){
  263.                         tmpDictionaryNumbers = tmpDictionaryNumbers.replace(s, "");
  264.                     }
  265.                 }
  266.             }
  267.             if(!addChar) {
  268.                 throw new UtilsException("Sono terminati i caratteri utilizzabili per la generazione della password di lunghezza '"+length+"'");
  269.             }
  270.         }
  271.        
  272.        
  273.         Collections.shuffle(password);
  274.        
  275.         StringBuilder bf = new StringBuilder();
  276.         for (String s : password) {
  277.             bf.append(s);
  278.         }
  279.         return  bf.toString();
  280.        
  281.     }
  282. }