diff --git a/script.sql b/script.sql index 50c9619..45de6d0 100644 --- a/script.sql +++ b/script.sql @@ -360,7 +360,8 @@ create table usuario ( id int unsigned primary key auto_increment, nombre varchar(255) not null, - password varchar(255) not null, + password binary(32) not null, + salt binary(16) not null, trabajador_id int unsigned not null, foreign key (trabajador_id) references trabajador (id) on delete cascade on update cascade, inserted_at timestamp default current_timestamp @@ -443,7 +444,4 @@ values (1, 2), (9, 2); INSERT INTO trabajador -VALUES (1, '197638990', 'Daniel', 'Cortes', 'Pincheira', '2019-06-04', '2019-06-04 18:12:55'); - -INSERT INTO usuario -VALUES (1, 'admin', 'IF7nbMo9zxGOH++0op8eE+obeILVzsZCguAqPVydL/0=', 1, '2019-06-05 00:08:50'); +VALUES (1, '197638990', 'Daniel', 'Cortes', 'Pincheira', '2019-06-04', '2019-06-04 18:12:55'); \ No newline at end of file diff --git a/src/main/java/xyz/danielcortes/controllers/mantenedores/trabajador/usuario/UsuarioCreateController.java b/src/main/java/xyz/danielcortes/controllers/mantenedores/trabajador/usuario/UsuarioCreateController.java index 0a6c30f..84bdbf8 100644 --- a/src/main/java/xyz/danielcortes/controllers/mantenedores/trabajador/usuario/UsuarioCreateController.java +++ b/src/main/java/xyz/danielcortes/controllers/mantenedores/trabajador/usuario/UsuarioCreateController.java @@ -1,12 +1,11 @@ package xyz.danielcortes.controllers.mantenedores.trabajador.usuario; -import java.nio.charset.StandardCharsets; import xyz.danielcortes.controllers.LaunchController; import xyz.danielcortes.controllers.mantenedores.trabajador.TrabajadorViewController; import xyz.danielcortes.framework.BaseController; import xyz.danielcortes.framework.BasePanel; -import xyz.danielcortes.framework.Hash; import xyz.danielcortes.framework.PanelName; +import xyz.danielcortes.framework.Passwords; import xyz.danielcortes.framework.ValidationResult; import xyz.danielcortes.models.Trabajador; import xyz.danielcortes.models.Usuario; @@ -68,7 +67,8 @@ public class UsuarioCreateController extends BaseController { Usuario usuario = new Usuario(); usuario.setNombre(user); - usuario.setPassword(Hash.sha256(new String(pass).getBytes(StandardCharsets.UTF_8))); + usuario.setSalt(Passwords.getNextSalt()); + usuario.setPassword(Passwords.hash(pass, usuario.getSalt())); usuario.setTrabajador(trabajador); this.repository.save(usuario); diff --git a/src/main/java/xyz/danielcortes/controllers/mantenedores/trabajador/usuario/UsuarioUpdateController.java b/src/main/java/xyz/danielcortes/controllers/mantenedores/trabajador/usuario/UsuarioUpdateController.java index 014ccf7..cda5c11 100644 --- a/src/main/java/xyz/danielcortes/controllers/mantenedores/trabajador/usuario/UsuarioUpdateController.java +++ b/src/main/java/xyz/danielcortes/controllers/mantenedores/trabajador/usuario/UsuarioUpdateController.java @@ -1,12 +1,11 @@ package xyz.danielcortes.controllers.mantenedores.trabajador.usuario; -import java.nio.charset.StandardCharsets; import xyz.danielcortes.controllers.LaunchController; import xyz.danielcortes.controllers.mantenedores.trabajador.TrabajadorViewController; import xyz.danielcortes.framework.BaseController; import xyz.danielcortes.framework.BasePanel; -import xyz.danielcortes.framework.Hash; import xyz.danielcortes.framework.PanelName; +import xyz.danielcortes.framework.Passwords; import xyz.danielcortes.framework.ValidationResult; import xyz.danielcortes.models.Trabajador; import xyz.danielcortes.repository.UsuarioRepository; @@ -77,7 +76,8 @@ public class UsuarioUpdateController extends BaseController { } trabajador.getUsuario().setNombre(user); - trabajador.getUsuario().setPassword(Hash.sha256(new String(pass).getBytes(StandardCharsets.UTF_8))); + trabajador.getUsuario().setSalt(Passwords.getNextSalt()); + trabajador.getUsuario().setPassword(Passwords.hash(pass, trabajador.getUsuario().getSalt())); this.repository.update(trabajador.getUsuario()); this.volver(); diff --git a/src/main/java/xyz/danielcortes/framework/Hash.java b/src/main/java/xyz/danielcortes/framework/Hash.java deleted file mode 100644 index 784b3b6..0000000 --- a/src/main/java/xyz/danielcortes/framework/Hash.java +++ /dev/null @@ -1,22 +0,0 @@ -package xyz.danielcortes.framework; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Base64; - -public class Hash { - - private static MessageDigest sha; - - static { - try { - sha = MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } - } - - public static String sha256(byte[] toHash) { - return Base64.getEncoder().encodeToString(sha.digest(toHash)); - } -} diff --git a/src/main/java/xyz/danielcortes/framework/Passwords.java b/src/main/java/xyz/danielcortes/framework/Passwords.java new file mode 100644 index 0000000..cab6478 --- /dev/null +++ b/src/main/java/xyz/danielcortes/framework/Passwords.java @@ -0,0 +1,46 @@ +package xyz.danielcortes.framework; + +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.spec.InvalidKeySpecException; +import java.util.Arrays; +import java.util.Random; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; + +public class Passwords { + private static final Random RANDOM = new SecureRandom(); + private static final int ITERATIONS = 10000; + private static final int KEY_LENGHT = 256; + + private Passwords(){} + + public static byte[] getNextSalt() { + byte[] salt = new byte[16]; + RANDOM.nextBytes(salt); + return salt; + } + + public static byte[] hash(char[] password, byte[] salt){ + PBEKeySpec spec = new PBEKeySpec(password, salt, ITERATIONS, KEY_LENGHT); + Arrays.fill(password, Character.MIN_VALUE); + try{ + SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); + return skf.generateSecret(spec).getEncoded(); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + throw new AssertionError("Error while hashing a password: " + e.getMessage(), e); + } finally { + spec.clearPassword(); + } + } + + public static boolean isExpectedPassword(char[] password, byte[] salt, byte[] expectedHash){ + byte[] pwdHash = hash(password, salt); + Arrays.fill(password, Character.MIN_VALUE); + if(pwdHash.length != expectedHash.length) return false; + for(int i = 0; i < pwdHash.length; i++){ + if(pwdHash[i] != expectedHash[i]) return false; + } + return true; + } +} diff --git a/src/main/java/xyz/danielcortes/login/LoginController.java b/src/main/java/xyz/danielcortes/login/LoginController.java index 313e0e0..a406495 100644 --- a/src/main/java/xyz/danielcortes/login/LoginController.java +++ b/src/main/java/xyz/danielcortes/login/LoginController.java @@ -1,14 +1,13 @@ package xyz.danielcortes.login; import java.awt.Dimension; -import java.nio.charset.StandardCharsets; import java.util.Optional; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.WindowConstants; -import xyz.danielcortes.framework.Hash; import xyz.danielcortes.framework.LoggedEvent; import xyz.danielcortes.framework.LoggedListener; +import xyz.danielcortes.framework.Passwords; import xyz.danielcortes.models.Usuario; import xyz.danielcortes.repository.UsuarioRepository; @@ -46,8 +45,8 @@ public class LoginController { } Usuario user = optionalUser.get(); - String pass = Hash.sha256(new String(this.view.getPassField().getPassword()).getBytes(StandardCharsets.UTF_8)); - if (pass.equals(user.getPassword())) { + char[] pass = this.view.getPassField().getPassword(); + if (Passwords.isExpectedPassword(pass, user.getSalt(), user.getPassword())) { this.frame.dispose(); loggedListener.loginTry(new LoggedEvent(this, user)); } else { diff --git a/src/main/java/xyz/danielcortes/models/Usuario.java b/src/main/java/xyz/danielcortes/models/Usuario.java index 4efb12e..de79efe 100644 --- a/src/main/java/xyz/danielcortes/models/Usuario.java +++ b/src/main/java/xyz/danielcortes/models/Usuario.java @@ -1,6 +1,5 @@ package xyz.danielcortes.models; -import java.util.Objects; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; @@ -21,8 +20,11 @@ public class Usuario { @Column(name = "nombre") private String nombre; - @Column(name = "password") - private String password; + @Column(name = "password", columnDefinition = "BINARY(32) NOT NULL") + private byte[] password; + + @Column(name = "salt", columnDefinition = "BINARY(16) NOT NULL") + private byte[] salt; @OneToOne @JoinColumn(name = "trabajador_id", referencedColumnName = "id") @@ -44,14 +46,22 @@ public class Usuario { this.nombre = nombre; } - public String getPassword() { + public byte[] getPassword() { return password; } - public void setPassword(String password) { + public void setPassword(byte[] password) { this.password = password; } + public byte[] getSalt() { + return salt; + } + + public void setSalt(byte[] salt) { + this.salt = salt; + } + public Trabajador getTrabajador() { return trabajador; } @@ -59,22 +69,4 @@ public class Usuario { public void setTrabajador(Trabajador trabajador) { this.trabajador = trabajador; } - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (!(o instanceof Usuario)) - return false; - Usuario usuario = (Usuario) o; - return Objects.equals(id, usuario.id) && - Objects.equals(nombre, usuario.nombre) && - Objects.equals(password, usuario.password) && - Objects.equals(trabajador, usuario.trabajador); - } - - @Override - public int hashCode() { - return Objects.hash(id, nombre, password, trabajador); - } }