Metadata PEM support

This commit is contained in:
MaxKey 2022-02-18 15:21:29 +08:00
parent 743037b6cc
commit ee8b7536e1
5 changed files with 152 additions and 38 deletions

View File

@ -25,6 +25,7 @@ import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey; import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec; import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -38,6 +39,8 @@ public final class RSAUtils {
public static final String PUBLIC_KEY = "RSAPublicKey"; public static final String PUBLIC_KEY = "RSAPublicKey";
public static final String PRIVATE_KEY = "RSAPrivateKey"; public static final String PRIVATE_KEY = "RSAPrivateKey";
public static final int BASE64ARRAY_SIZE = 64;
public static Map<String, Object> genKeyPair() throws Exception { public static Map<String, Object> genKeyPair() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORTHM); KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORTHM);
@ -173,5 +176,41 @@ public final class RSAUtils {
return cipher.doFinal(data); return cipher.doFinal(data);
} }
public static String getPublicKeyPEM(byte[] encoded) {
StringBuffer base64String =
new StringBuffer("");
base64String.append("-----BEGIN PUBLIC KEY-----").append("\n");
base64String.append(getBase64PEM(encoded));
base64String.append("-----END PUBLIC KEY-------").append("\n");
return base64String.toString();
}
public static String getPrivateKeyPEM(byte[] encoded) {
StringBuffer base64String =
new StringBuffer("");
base64String.append("-----BEGIN RSA PRIVATE KEY-----").append("\n");
base64String.append(getBase64PEM(encoded));
base64String.append("-----END RSA PRIVATE KEY-------").append("\n");
return base64String.toString();
}
public static String getBase64PEM(byte[] encoded) {
String base64String = Base64.getEncoder().encodeToString(encoded);
StringBuffer base64ArrayString = new StringBuffer("");
int startPosition = 0;
int endPosition = BASE64ARRAY_SIZE;
while(endPosition < base64String.length()) {
base64ArrayString.append(base64String.substring(startPosition, endPosition)).append("\n");
startPosition = endPosition;
endPosition = endPosition + BASE64ARRAY_SIZE;
}
if(startPosition < base64String.length()) {
base64ArrayString.append(base64String.substring(startPosition)).append("\n");
}
return base64ArrayString.toString();
}
} }

View File

@ -18,12 +18,20 @@ package org.maxkey.crypto.jose.keystore;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
import com.google.common.io.CharStreams; import com.google.common.io.CharStreams;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.jwk.JWK; import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jose.jwk.JWKSet; import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.security.PublicKey;
import java.text.ParseException; import java.text.ParseException;
import java.util.List; import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.maxkey.crypto.RSAUtils;
import org.maxkey.pretty.PrettyFactory;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
@ -131,5 +139,49 @@ public class JWKSetKeyStore {
} }
return jwkSet.getKeys(); return jwkSet.getKeys();
} }
public String toString(String mediaType){
StringBuffer metaDataString = new StringBuffer("");
if(StringUtils.isNotBlank(mediaType) && mediaType.equalsIgnoreCase("XML")) {
metaDataString.append("<RSAKeyValue>").append("\n");
for(JWK jwk : jwkSet.getKeys()) {
RSAKey rsaKey = jwk.toRSAKey();
PublicKey publicKey;
try {
publicKey = rsaKey.toPublicKey();
metaDataString.append("<Modulus>").append("\n");
metaDataString.append(RSAUtils.getPublicKeyPEM(publicKey.getEncoded()));
metaDataString.append("</Modulus>").append("\n");
//keyID
metaDataString.append("<Algorithm>");
metaDataString.append(rsaKey.getAlgorithm());
metaDataString.append("</Algorithm>").append("\n");
metaDataString.append("<KeyID>");
metaDataString.append(rsaKey.getKeyID());
metaDataString.append("</KeyID>").append("\n");
metaDataString.append("<KeyType>");
metaDataString.append(rsaKey.getKeyType());
metaDataString.append("</KeyType>").append("\n");
metaDataString.append("<Format>");
metaDataString.append(publicKey.getFormat());
metaDataString.append("</Format>");
metaDataString.append("<PublicExponent>");
metaDataString.append(rsaKey.getPublicExponent());
metaDataString.append("</PublicExponent>").append("\n");
} catch (JOSEException e) {
_logger.error("JOSEException ", mediaType);
}
}
metaDataString.append("</RSAKeyValue>");
}else {
metaDataString.append(PrettyFactory.getJsonPretty().format(
jwkSet.toPublicJWKSet().toString()));
}
return metaDataString.toString();
}
} }

View File

@ -21,7 +21,6 @@
package org.maxkey.authz.token.endpoint; package org.maxkey.authz.token.endpoint;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import javax.servlet.http.Cookie; import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -34,12 +33,13 @@ import org.maxkey.authz.endpoint.adapter.AbstractAuthorizeAdapter;
import org.maxkey.authz.jwt.endpoint.adapter.JwtAdapter; import org.maxkey.authz.jwt.endpoint.adapter.JwtAdapter;
import org.maxkey.configuration.ApplicationConfig; import org.maxkey.configuration.ApplicationConfig;
import org.maxkey.constants.ConstsBoolean; import org.maxkey.constants.ConstsBoolean;
import org.maxkey.constants.ContentType;
import org.maxkey.crypto.jose.keystore.JWKSetKeyStore; import org.maxkey.crypto.jose.keystore.JWKSetKeyStore;
import org.maxkey.entity.apps.Apps; import org.maxkey.entity.apps.Apps;
import org.maxkey.entity.apps.AppsJwtDetails; import org.maxkey.entity.apps.AppsJwtDetails;
import org.maxkey.persistence.service.AppsJwtDetailsService; import org.maxkey.persistence.service.AppsJwtDetailsService;
import org.maxkey.pretty.PrettyFactory;
import org.maxkey.util.Instance; import org.maxkey.util.Instance;
import org.maxkey.web.HttpRequestAdapter;
import org.maxkey.web.WebConstants; import org.maxkey.web.WebConstants;
import org.maxkey.web.WebContext; import org.maxkey.web.WebContext;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -138,27 +138,38 @@ public class JwtAuthorizeEndpoint extends AuthorizeBaseEndpoint{
} }
@Operation(summary = "JWT JWK元数据接口", description = "参数mxk_metadata_APPID",method="GET") @Operation(summary = "JWT JWK元数据接口", description = "参数mxk_metadata_APPID",method="GET")
@RequestMapping(value = "/metadata/jwt/{appid}.json",produces = "application/json", method={RequestMethod.POST, RequestMethod.GET}) @RequestMapping(
value = "/metadata/jwt/" + WebConstants.MXK_METADATA_PREFIX + "{appid}.{mediaType}",
method={RequestMethod.POST, RequestMethod.GET})
@ResponseBody @ResponseBody
public String metadata(HttpServletRequest request, public String metadata(HttpServletRequest request,
HttpServletResponse response, @PathVariable("appid") String appId) { HttpServletResponse response,
appId = appId.substring(WebConstants.MXK_METADATA_PREFIX.length(), appId.length()); @PathVariable("appid") String appId,
@PathVariable("mediaType") String mediaType) {
AppsJwtDetails jwtDetails = jwtDetailsService.getAppDetails(appId); AppsJwtDetails jwtDetails = jwtDetailsService.getAppDetails(appId);
String jwkSetString = ""; if(jwtDetails != null) {
if(!jwtDetails.getSignature().equalsIgnoreCase("none")) { String jwkSetString = "";
jwkSetString = jwtDetails.getSignatureKey(); if(!jwtDetails.getSignature().equalsIgnoreCase("none")) {
} jwkSetString = jwtDetails.getSignatureKey();
if(!jwtDetails.getAlgorithm().equalsIgnoreCase("none")) {
if(StringUtils.isBlank(jwkSetString)) {
jwkSetString = jwtDetails.getAlgorithmKey();
}else {
jwkSetString = jwkSetString + "," +jwtDetails.getAlgorithmKey();
} }
if(!jwtDetails.getAlgorithm().equalsIgnoreCase("none")) {
if(StringUtils.isBlank(jwkSetString)) {
jwkSetString = jwtDetails.getAlgorithmKey();
}else {
jwkSetString = jwkSetString + "," +jwtDetails.getAlgorithmKey();
}
}
JWKSetKeyStore jwkSetKeyStore = new JWKSetKeyStore("{\"keys\": [" + jwkSetString + "]}");
if(StringUtils.isNotBlank(mediaType)
&& mediaType.equalsIgnoreCase(HttpRequestAdapter.MediaType.XML)) {
response.setContentType(ContentType.APPLICATION_XML_UTF8);
}else {
response.setContentType(ContentType.APPLICATION_JSON_UTF8);
}
return jwkSetKeyStore.toString(mediaType);
} }
return appId + " not exist.";
JWKSetKeyStore jwkSetKeyStore = new JWKSetKeyStore("{\"keys\": [" + jwkSetString + "]}");
return PrettyFactory.getJsonPretty().format(
jwkSetKeyStore.getJwkSet().toPublicJWKSet().toString());
} }
} }

View File

@ -42,11 +42,12 @@ import org.maxkey.authz.oauth2.provider.approval.UserApprovalHandler;
import org.maxkey.authz.oauth2.provider.code.AuthorizationCodeServices; import org.maxkey.authz.oauth2.provider.code.AuthorizationCodeServices;
import org.maxkey.authz.oauth2.provider.implicit.ImplicitTokenRequest; import org.maxkey.authz.oauth2.provider.implicit.ImplicitTokenRequest;
import org.maxkey.authz.oauth2.provider.request.DefaultOAuth2RequestValidator; import org.maxkey.authz.oauth2.provider.request.DefaultOAuth2RequestValidator;
import org.maxkey.constants.ContentType;
import org.maxkey.crypto.jose.keystore.JWKSetKeyStore; import org.maxkey.crypto.jose.keystore.JWKSetKeyStore;
import org.maxkey.util.HttpEncoder; import org.maxkey.util.HttpEncoder;
import org.maxkey.entity.apps.Apps; import org.maxkey.entity.apps.Apps;
import org.maxkey.entity.apps.oauth2.provider.ClientDetails; import org.maxkey.entity.apps.oauth2.provider.ClientDetails;
import org.maxkey.pretty.PrettyFactory; import org.maxkey.web.HttpRequestAdapter;
import org.maxkey.web.WebConstants; import org.maxkey.web.WebConstants;
import org.maxkey.web.WebContext; import org.maxkey.web.WebContext;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -291,27 +292,39 @@ public class AuthorizationEndpoint extends AbstractEndpoint {
} }
@Operation(summary = "OAuth JWk 元数据接口", description = "参数mxk_metadata_APPID",method="GET") @Operation(summary = "OAuth JWk 元数据接口", description = "参数mxk_metadata_APPID",method="GET")
@RequestMapping(value = "/metadata/oauth/v20/{appid}.json",produces = "application/json", method={RequestMethod.POST, RequestMethod.GET}) @RequestMapping(
value = "/metadata/oauth/v20/" + WebConstants.MXK_METADATA_PREFIX + "{appid}.{mediaType}",
method={RequestMethod.POST, RequestMethod.GET})
@ResponseBody @ResponseBody
public String metadata(HttpServletRequest request, public String metadata(HttpServletRequest request,
HttpServletResponse response, @PathVariable("appid") String appId) { HttpServletResponse response,
appId = appId.substring(WebConstants.MXK_METADATA_PREFIX.length(), appId.length()); @PathVariable("appid") String appId,
@PathVariable("mediaType") String mediaType) {
ClientDetails clientDetails = getClientDetailsService().loadClientByClientId(appId,true); ClientDetails clientDetails = getClientDetailsService().loadClientByClientId(appId,true);
String jwkSetString = ""; if(clientDetails != null) {
if(!clientDetails.getSignature().equalsIgnoreCase("none")) { String jwkSetString = "";
jwkSetString = clientDetails.getSignatureKey(); if(!clientDetails.getSignature().equalsIgnoreCase("none")) {
} jwkSetString = clientDetails.getSignatureKey();
if(!clientDetails.getAlgorithm().equalsIgnoreCase("none")) {
if(!StringUtils.hasText(jwkSetString)) {
jwkSetString = clientDetails.getAlgorithmKey();
}else {
jwkSetString = jwkSetString + "," +clientDetails.getAlgorithmKey();
} }
if(!clientDetails.getAlgorithm().equalsIgnoreCase("none")) {
if(!StringUtils.hasText(jwkSetString)) {
jwkSetString = clientDetails.getAlgorithmKey();
}else {
jwkSetString = jwkSetString + "," +clientDetails.getAlgorithmKey();
}
}
JWKSetKeyStore jwkSetKeyStore = new JWKSetKeyStore("{\"keys\": [" + jwkSetString + "]}");
if(StringUtils.hasText(mediaType)
&& mediaType.equalsIgnoreCase(HttpRequestAdapter.MediaType.XML)) {
response.setContentType(ContentType.APPLICATION_XML_UTF8);
}else {
response.setContentType(ContentType.APPLICATION_JSON_UTF8);
}
return jwkSetKeyStore.toString(mediaType);
} }
JWKSetKeyStore jwkSetKeyStore = new JWKSetKeyStore("{\"keys\": [" + jwkSetString + "]}");
return PrettyFactory.getJsonPretty().format( return appId + " not exist.";
jwkSetKeyStore.getJwkSet().toPublicJWKSet().toString());
} }
// We need explicit approval from the user. // We need explicit approval from the user.

View File

@ -76,12 +76,11 @@ public class SamlMetadataEndpoint {
private Credential signingCredential; private Credential signingCredential;
@Operation(summary = "SAML 2.0 元数据接口", description = "参数mxk_metadata_APPID",method="GET") @Operation(summary = "SAML 2.0 元数据接口", description = "参数mxk_metadata_APPID",method="GET")
@RequestMapping(value = "/{appid}.xml",produces = "application/xml", method={RequestMethod.POST, RequestMethod.GET}) @RequestMapping(value = "/" + WebConstants.MXK_METADATA_PREFIX + "{appid}.xml",produces = "application/xml", method={RequestMethod.POST, RequestMethod.GET})
@ResponseBody @ResponseBody
public String metadata(HttpServletRequest request, public String metadata(HttpServletRequest request,
HttpServletResponse response, @PathVariable("appid") String appId) { HttpServletResponse response, @PathVariable("appid") String appId) {
response.setContentType(ContentType.APPLICATION_XML_UTF8); response.setContentType(ContentType.APPLICATION_XML_UTF8);
appId = appId.substring(WebConstants.MXK_METADATA_PREFIX.length(), appId.length());
if(signingCredential == null){ if(signingCredential == null){
TrustResolver trustResolver = new TrustResolver(); TrustResolver trustResolver = new TrustResolver();
CredentialResolver credentialResolver=(CredentialResolver)trustResolver.buildKeyStoreCredentialResolver( CredentialResolver credentialResolver=(CredentialResolver)trustResolver.buildKeyStoreCredentialResolver(