Skip to content
This repository has been archived by the owner on Aug 2, 2023. It is now read-only.

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Stephane Segning committed Sep 13, 2020
0 parents commit e846c1e
Show file tree
Hide file tree
Showing 6 changed files with 1,038 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.idea
*.iml
*.log
*.class
target
.code
*.zip
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Keycloak Bcrypt
130 changes: 130 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.bayamsell.keycloak</groupId>
<artifactId>keycloak-bcrypt</artifactId>
<version>1.0.0</version>

<properties>
<java.version>11</java.version>
<lombock.version>1.18.10</lombock.version>
<keycloak.version>11.0.2</keycloak.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-parent</artifactId>
<version>${keycloak.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi</artifactId>
</dependency>

<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-server-spi-private</artifactId>
</dependency>

<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
</dependency>

<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
</dependency>

<!-- lombock -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
<version>${lombock.version}</version>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<finalName>${artifactId}-${project.version}</finalName>

<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<verbose>false</verbose>
<release>${java.version}</release>
<source>${java.version}</source>
<target>${java.version}</target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombock.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ear-plugin</artifactId>
<version>3.0.1</version>
<configuration>
<defaultLibBundleDir>lib</defaultLibBundleDir>
<outputFileNameMapping>@{artifactId}@.@{extension}@</outputFileNameMapping>
</configuration>
</plugin>

<plugin>
<groupId>org.wildfly.plugins</groupId>
<artifactId>wildfly-maven-plugin</artifactId>
<version>2.0.1.Final</version>
</plugin>

<!-- Maven Assembly Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<!-- get all project dependencies -->
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<!-- bind to the packaging phase -->
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>

</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.bayamsell.keycloak;

import com.bayamsell.keycloak.helper.BCrypt;
import lombok.AllArgsConstructor;
import org.keycloak.credential.hash.PasswordHashProvider;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.credential.PasswordCredentialModel;
import org.keycloak.models.credential.dto.PasswordCredentialData;
import org.keycloak.models.credential.dto.PasswordSecretData;

/**
* @author <a href="mailto:pro.guillaume.leroy@gmail.com">Guillaume Leroy</a>
*/
@AllArgsConstructor
public class BCryptPasswordHashProvider implements PasswordHashProvider {
// BCrypt uses min of 4 and max of 30 2**log_rounds
private final int MAX_BCRYPT_LOG_ROUNDS = 30;
private final int MIN_BCRYPT_LOG_ROUNDS = 4;

private final String providerId;
private final int defaultIterations;

@Override
public boolean policyCheck(PasswordPolicy policy, PasswordCredentialModel passwordCredentialModel) {
int policyHashIterations = policy.getHashIterations();
if (policyHashIterations == -1) {
policyHashIterations = defaultIterations;
}

PasswordCredentialData passwordCredentialData = passwordCredentialModel.getPasswordCredentialData();
return passwordCredentialData.getHashIterations() == policyHashIterations && providerId.equals(passwordCredentialData.getAlgorithm());
}

@Override
public PasswordCredentialModel encodedCredential(String rawPassword, int iterations) {
int logRounds = iterations == -1 ? iterationsToLogRounds(defaultIterations) : iterationsToLogRounds(iterations);
String salt = BCrypt.gensalt(logRounds);
String hash = BCrypt.hashpw(rawPassword, salt);
return PasswordCredentialModel.createFromValues(providerId, new byte[0], iterations, hash);
}

@Override
public boolean verify(String rawPassword, PasswordCredentialModel passwordCredentialModel) {
PasswordSecretData passwordSecretData = passwordCredentialModel.getPasswordSecretData();
return BCrypt.checkpw(rawPassword, passwordSecretData.getValue());
}

@Override
public String encode(String rawPassword, int iterations) {
int logRounds = iterations == -1 ? iterationsToLogRounds(defaultIterations) : iterationsToLogRounds(iterations);
String salt = BCrypt.gensalt(logRounds);
return BCrypt.hashpw(rawPassword, salt);
}

@Override
public void close() {

}

private int iterationsToLogRounds(int iterations) {
// bcrypt uses 2**log2_rounds with a min of 4 and max of 30 log rounds
return Math.max(MIN_BCRYPT_LOG_ROUNDS, Math.min(MAX_BCRYPT_LOG_ROUNDS,
(int) Math.round(Math.log(iterations) / Math.log(2) + 1)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.bayamsell.keycloak;

import org.keycloak.Config;
import org.keycloak.credential.hash.PasswordHashProvider;
import org.keycloak.credential.hash.PasswordHashProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ServerInfoAwareProviderFactory;

import java.util.HashMap;
import java.util.Map;

/**
* @author <a href="mailto:pro.guillaume.leroy@gmail.com">Guillaume Leroy</a>
*/
public class BCryptPasswordHashProviderFactory implements PasswordHashProviderFactory, ServerInfoAwareProviderFactory {
public static final String ID = "bcrypt";
public static final int DEFAULT_ITERATIONS = 8192;

private static final Map<String, String> infos = new HashMap<>();

static {
infos.put("version", "1.0.0");
}

@Override
public PasswordHashProvider create(KeycloakSession session) {
return new BCryptPasswordHashProvider(ID, DEFAULT_ITERATIONS);
}

@Override
public void init(Config.Scope config) {
}

@Override
public void postInit(KeycloakSessionFactory factory) {
}

@Override
public String getId() {
return ID;
}

@Override
public void close() {
}

/**
* Return actual info about the provider. This info contains informations about providers configuration and operational conditions (eg. errors in connection to remote systems etc) which is
* shown on "Server Info" page then.
*
* @return Map with keys describing value and relevant values itself
*/
@Override
public Map<String, String> getOperationalInfo() {
return infos;
}
}
Loading

0 comments on commit e846c1e

Please sign in to comment.