org.keycloak.models.OTPPolicy Maven / Gradle / Ivy
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.models;
import org.jboss.logging.Logger;
import org.keycloak.models.utils.Base32;
import org.keycloak.models.utils.HmacOTP;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
/**
* @author Bill Burke
* @version $Revision: 1 $
*/
public class OTPPolicy implements Serializable {
protected static final Logger logger = Logger.getLogger(OTPPolicy.class);
protected String type;
protected String algorithm;
protected int initialCounter;
protected int digits;
protected int lookAheadWindow;
protected int period;
private static final Map algToKeyUriAlg = new HashMap<>();
static {
algToKeyUriAlg.put(HmacOTP.HMAC_SHA1, "SHA1");
algToKeyUriAlg.put(HmacOTP.HMAC_SHA256, "SHA256");
algToKeyUriAlg.put(HmacOTP.HMAC_SHA512, "SHA512");
}
public OTPPolicy() {
}
public OTPPolicy(String type, String algorithm, int initialCounter, int digits, int lookAheadWindow, int period) {
this.type = type;
this.algorithm = algorithm;
this.initialCounter = initialCounter;
this.digits = digits;
this.lookAheadWindow = lookAheadWindow;
this.period = period;
}
public static OTPPolicy DEFAULT_POLICY = new OTPPolicy(UserCredentialModel.TOTP, HmacOTP.HMAC_SHA1, 0, 6, 1, 30);
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getAlgorithm() {
return algorithm;
}
public void setAlgorithm(String algorithm) {
this.algorithm = algorithm;
}
public int getInitialCounter() {
return initialCounter;
}
public void setInitialCounter(int initialCounter) {
this.initialCounter = initialCounter;
}
public int getDigits() {
return digits;
}
public void setDigits(int digits) {
this.digits = digits;
}
public int getLookAheadWindow() {
return lookAheadWindow;
}
public void setLookAheadWindow(int lookAheadWindow) {
this.lookAheadWindow = lookAheadWindow;
}
public int getPeriod() {
return period;
}
public void setPeriod(int period) {
this.period = period;
}
/**
* Constructs the otpauth://
URI based on the Key-Uri-Format.
* @param realm
* @param user
* @param secret
* @return the otpauth://
URI
*/
public String getKeyURI(RealmModel realm, UserModel user, String secret) {
try {
String displayName = realm.getDisplayName() != null && !realm.getDisplayName().isEmpty() ? realm.getDisplayName() : realm.getName();
String accountName = URLEncoder.encode(user.getUsername(), "UTF-8");
String issuerName = URLEncoder.encode(displayName, "UTF-8") .replaceAll("\\+", "%20");
/*
* The issuerName component in the label is usually shown in a authenticator app, such as
* Google Authenticator or FreeOTP, as a hint for the user to which system an username
* belongs to.
*/
String label = issuerName + ":" + accountName;
String parameters = "secret=" + Base32.encode(secret.getBytes()) //
+ "&digits=" + digits //
+ "&algorithm=" + algToKeyUriAlg.get(algorithm) //
+ "&issuer=" + issuerName;
if (type.equals(UserCredentialModel.HOTP)) {
parameters += "&counter=" + initialCounter;
} else if (type.equals(UserCredentialModel.TOTP)) {
parameters += "&period=" + period;
}
return "otpauth://" + type + "/" + label+ "?" + parameters;
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}