Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,13 @@
import org.sasanlabs.service.exception.ExceptionStatusCodeEnum;
import org.sasanlabs.service.exception.ServiceApplicationException;

/** @author KSASAN preetkaran20@gmail.com */
public class JWTUtils {
/** Utility class for JWT operations. */
public final class JWTUtils {

// Utility class → private constructor to prevent instantiation
private JWTUtils() {
throw new IllegalStateException("Utility class");
}

public static final char JWT_TOKEN_PERIOD_CHARACTER = '.';

Expand All @@ -37,7 +42,7 @@ public class JWTUtils {
public static final String JWT_EC_ALGORITHM_IDENTIFIER = "EC";
public static final String JWT_OCTET_ALGORITHM_IDENTIFIER = "ED";
public static final String JWT_HMAC_SHA_256_ALGORITHM = "HS256";
// TODO need to make it better.

public static final String HS256_TOKEN_TO_BE_SIGNED =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9."
+ "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ";
Expand All @@ -50,17 +55,10 @@ public class JWTUtils {
"eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG"
+ "4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0";

// Location of keys
public static final String KEYS_LOCATION = "static/templates/JWTVulnerability/keys/";

/**
* This is the Begining and Ending token of Public and Private Keys encoded with PKCS#8
* encoding. In case you find PEM file not having these tokens and instead have -----BEGIN RSA
* PRIVATE KEY----- or -----BEGIN RSA PUBLIC KEY----- then those are encoded by old format. To
* Generate new Keys please use following <a
* href="https://en.wikibooks.org/wiki/Cryptography/Generate_a_keypair_using_OpenSSL">link</a>
*/
public static final String BEGIN_PRIVATE_KEY_TOKEN = "-----BEGIN PRIVATE KEY-----";

public static final String END_PRIVATE_KEY_TOKEN = "-----END PRIVATE KEY-----";
public static final String BEGIN_PUBLIC_KEY_TOKEN = "-----BEGIN PUBLIC KEY-----";
public static final String END_PUBLIC_KEY_TOKEN = "-----END PUBLIC KEY-----";
Expand All @@ -76,54 +74,25 @@ public static String getString(byte[] tokenBytes) throws UnsupportedEncodingExce
public static final Map<String, String> JWT_HMAC_ALGO_TO_JAVA_ALGORITHM_MAPPING =
createJWTHmacAlgoToJavaAlgoMapping();

static Map<String, String> createJWTHmacAlgoToJavaAlgoMapping() {
Map<String, String> jwtAlgoToJavaAlgoMapping = new HashMap<String, String>();
private static Map<String, String> createJWTHmacAlgoToJavaAlgoMapping() {
Map<String, String> jwtAlgoToJavaAlgoMapping = new HashMap<>();
jwtAlgoToJavaAlgoMapping.put(JWT_HMAC_SHA_256_ALGORITHM, "HmacSHA256");
jwtAlgoToJavaAlgoMapping.put("HS384", "HmacSHA384");
jwtAlgoToJavaAlgoMapping.put("HS512", "HmacSHA512");
return jwtAlgoToJavaAlgoMapping;
}

/**
* we are using <a href="https://en.wikipedia.org/wiki/Base64#URL_applications">base64 Url Safe
* encoding</a>. because of JWT specifications <br>
* Also we are removing the padding as per <a
* href="https://www.rfc-editor.org/rfc/rfc7515.txt">RFC 7515</a> padding is not there in JWT.
*
* @param token
* @return
* @throws UnsupportedEncodingException
*/
public static String getBase64UrlSafeWithoutPaddingEncodedString(String token)
throws UnsupportedEncodingException {
return JWTUtils.getBase64UrlSafeWithoutPaddingEncodedString(getBytes(token));
return getBase64UrlSafeWithoutPaddingEncodedString(getBytes(token));
}

/**
* we are using <a href="https://en.wikipedia.org/wiki/Base64#URL_applications">base64 Url Safe
* encoding</a>. because of JWT specifications <br>
* Also we are removing the padding as per <a
* href="https://www.rfc-editor.org/rfc/rfc7515.txt">RFC 7515</a> padding is not there in JWT.
*
* @param token
* @return
* @throws UnsupportedEncodingException
*/
public static String getBase64UrlSafeWithoutPaddingEncodedString(byte[] token)
throws UnsupportedEncodingException {
return JWTUtils.getString(Base64.getUrlEncoder().encode(token))
return getString(Base64.getUrlEncoder().encode(token))
.replaceAll(BASE64_PADDING_CHARACTER_REGEX, "");
}

/**
* Utility method for reading the PEM file and building RSAPrivateKey from it. Note: This method
* assumes that PEM file contains PKCS#8 encoded Key format please check the format.
*
* @param pemFile InputStream of PEM file containing RSA Private Key
* @return RSAPrivateKey by reading PEM file containing the RSA Private Key.
* @throws JWTException if unable to read the provided file path or key specification is
* incorrect etc.
*/
public static RSAPrivateKey getRSAPrivateKeyFromProvidedPEMFilePath(InputStream pemFile)
throws ServiceApplicationException {
try {
Expand All @@ -138,15 +107,6 @@ public static RSAPrivateKey getRSAPrivateKeyFromProvidedPEMFilePath(InputStream
}
}

/**
* Utility method for reading the PEM file and building RSAPublicKey from it. Note: This method
* assumes that PEM file contains PKCS#8 encoded Key format please check the format.
*
* @param pemFile InputStream of PEM file containing RSA Public Key.
* @return RSAPublicKey by reading PEM file containing the RSA Private Key.
* @throws JWTException if unable to read the provided file path or key specification is
* incorrect etc.
*/
public static RSAPublicKey getRSAPublicKeyFromProvidedPEMFilePath(InputStream pemFile)
throws ServiceApplicationException {
try {
Expand All @@ -161,33 +121,11 @@ public static RSAPublicKey getRSAPublicKeyFromProvidedPEMFilePath(InputStream pe
}
}

/**
* Checks if the provided tokens i.e. {@param beginToken} or {@param endToken} are present in
* the provided {@param contents}.
*
* @param contents
* @param beginToken
* @param endToken
* @return true if provided token params are present in contents else false.
*/
private static boolean containsSection(String contents, String beginToken, String endToken) {
int idxToken;
if ((idxToken = contents.indexOf(beginToken)) == -1
|| contents.indexOf(endToken) < idxToken) {
return false;
}
return true;
int idxToken = contents.indexOf(beginToken);
return idxToken != -1 && contents.indexOf(endToken) > idxToken;
}

/**
* Converts PEM to DER format i.e. removes the Begin and End tokens and decode the base64
* encoded certificate.
*
* @param pem
* @param beginDelimiter
* @param endDelimiter
* @return DER format Key
*/
private static byte[] parseDERFromPEM(String pem, String beginDelimiter, String endDelimiter) {
if (!containsSection(pem, beginDelimiter, endDelimiter)) {
return new byte[0];
Expand Down