refactor signing of saml2 authentication request
This commit is contained in:
parent
1a2a93a95f
commit
b6505cda4e
|
@ -5,6 +5,7 @@ import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.sam
|
|||
import eu.eudat.logic.security.validators.configurableProvider.Saml2SSOUtils;
|
||||
import eu.eudat.logic.services.ApiContext;
|
||||
import eu.eudat.models.data.helpers.responses.ResponseItem;
|
||||
import eu.eudat.models.data.saml2.AuthnRequestModel;
|
||||
import eu.eudat.types.ApiMessageCode;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
|
@ -56,14 +57,14 @@ public class Saml2MetadataController extends BaseController {
|
|||
}
|
||||
@RequestMapping(method = RequestMethod.GET, value = {"authnRequest/{configurableProviderId}"})
|
||||
public @ResponseBody
|
||||
ResponseEntity<ResponseItem<String>> getAuthnRequest(@PathVariable String configurableProviderId) {
|
||||
ResponseEntity getAuthnRequest(@PathVariable String configurableProviderId) {
|
||||
Saml2ConfigurableProvider saml2ConfigurableProvider = (Saml2ConfigurableProvider) this.configLoader.getConfigurableProviders().getProviders().stream()
|
||||
.filter(prov -> prov.getConfigurableLoginId().equals(configurableProviderId))
|
||||
.findFirst().orElse(null);
|
||||
if (saml2ConfigurableProvider != null) {
|
||||
try {
|
||||
String authnRequestXml = Saml2SSOUtils.getAuthnRequest(saml2ConfigurableProvider);
|
||||
return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<String>().status(ApiMessageCode.SUCCESS_MESSAGE).message("Created").payload(authnRequestXml));
|
||||
AuthnRequestModel authnRequest = Saml2SSOUtils.getAuthnRequest(saml2ConfigurableProvider);
|
||||
return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<AuthnRequestModel>().status(ApiMessageCode.SUCCESS_MESSAGE).message("Created").payload(authnRequest));
|
||||
}
|
||||
catch (Exception e) {
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseItem<String>().status(ApiMessageCode.ERROR_MESSAGE).message("Failed to create authentication request."));
|
||||
|
|
|
@ -3,6 +3,7 @@ package eu.eudat.logic.security.validators.configurableProvider;
|
|||
import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.saml2.CertificateInfo;
|
||||
import eu.eudat.logic.security.customproviders.ConfigurableProvider.entities.saml2.Saml2ConfigurableProvider;
|
||||
import eu.eudat.logic.utilities.builders.XmlBuilder;
|
||||
import eu.eudat.models.data.saml2.AuthnRequestModel;
|
||||
import jakarta.xml.soap.*;
|
||||
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
|
||||
import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
|
||||
|
@ -34,7 +35,6 @@ import org.opensaml.core.xml.config.XMLObjectProviderRegistry;
|
|||
import org.opensaml.core.xml.io.*;
|
||||
import org.opensaml.core.xml.schema.*;
|
||||
import org.opensaml.saml.common.SAMLObject;
|
||||
import org.opensaml.saml.common.SAMLObjectContentReference;
|
||||
import org.opensaml.saml.common.SAMLVersion;
|
||||
import org.opensaml.saml.common.xml.SAMLConstants;
|
||||
import org.opensaml.saml.criterion.EntityRoleCriterion;
|
||||
|
@ -64,8 +64,6 @@ import org.opensaml.xmlsec.keyinfo.impl.X509KeyInfoGeneratorFactory;
|
|||
import org.opensaml.xmlsec.signature.KeyInfo;
|
||||
import org.opensaml.xmlsec.signature.Signature;
|
||||
import org.opensaml.xmlsec.signature.X509Data;
|
||||
import org.opensaml.xmlsec.signature.impl.SignatureBuilder;
|
||||
import org.opensaml.xmlsec.signature.support.SignatureConstants;
|
||||
import org.opensaml.xmlsec.signature.support.SignatureValidator;
|
||||
import org.opensaml.xmlsec.signature.support.Signer;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -88,6 +86,7 @@ import javax.xml.transform.TransformerFactory;
|
|||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import java.io.*;
|
||||
import java.net.URLEncoder;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.*;
|
||||
|
@ -794,47 +793,68 @@ public class Saml2SSOUtils {
|
|||
|
||||
}
|
||||
|
||||
public static String getAuthnRequest(Saml2ConfigurableProvider provider) throws Exception {
|
||||
public static AuthnRequestModel getAuthnRequest(Saml2ConfigurableProvider provider) throws Exception {
|
||||
|
||||
AuthnRequest authnRequest = buildAuthnRequest(provider);
|
||||
String relayState = "spId=" + provider.getSpEntityId() + "&configurableLoginId=" + provider.getConfigurableLoginId();
|
||||
|
||||
String authnRequestXml = null;
|
||||
DocumentBuilder builder;
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
String signatureBase64 = null;
|
||||
try {
|
||||
Signature signature = (Signature) buildXMLObject(Signature.DEFAULT_ELEMENT_NAME);
|
||||
if(provider.isAuthnRequestsSigned()){
|
||||
|
||||
Credential credential = getCredential(provider.getSigningCert());
|
||||
signature.setSigningCredential(credential);
|
||||
signature.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
|
||||
signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
|
||||
|
||||
X509KeyInfoGeneratorFactory keyInfoGeneratorFactory = new X509KeyInfoGeneratorFactory();
|
||||
keyInfoGeneratorFactory.setEmitEntityCertificate(true);
|
||||
KeyInfoGenerator keyInfoGenerator = keyInfoGeneratorFactory.newInstance();
|
||||
signature.setKeyInfo(keyInfoGenerator.generate(getCredential(provider.getSigningCert())));
|
||||
|
||||
authnRequest.setSignature(signature);
|
||||
}
|
||||
|
||||
builder = factory.newDocumentBuilder();
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
Document document = builder.newDocument();
|
||||
Marshaller out = registry.getMarshallerFactory().getMarshaller(authnRequest);
|
||||
out.marshall(authnRequest, document);
|
||||
|
||||
if(provider.isAuthnRequestsSigned()) {
|
||||
Signer.signObject(signature);
|
||||
}
|
||||
|
||||
authnRequestXml = XmlBuilder.generateXml(document);
|
||||
|
||||
if(provider.isAuthnRequestsSigned()) {
|
||||
signatureBase64 = buildSignature(authnRequestXml, relayState, provider.getSigningCert());
|
||||
}
|
||||
}
|
||||
catch (MarshallingException | ParserConfigurationException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
return authnRequestXml;
|
||||
|
||||
AuthnRequestModel authnRequestModel = new AuthnRequestModel();
|
||||
authnRequestModel.setAuthnRequestXml(authnRequestXml);
|
||||
authnRequestModel.setRelayState(relayState);
|
||||
authnRequestModel.setAlgorithm("http://www.w3.org/2000/09/xmldsig#rsa-sha1");
|
||||
authnRequestModel.setSignature(signatureBase64);
|
||||
return authnRequestModel;
|
||||
|
||||
}
|
||||
|
||||
private static String buildSignature(String authnRequest, String relayState, CertificateInfo signingCertInfo) throws Exception{
|
||||
|
||||
KeyStore ks = (signingCertInfo.getKeyFormat().getType().equals("JKS")) ? KeyStore.getInstance("JKS") : KeyStore.getInstance("PKCS12");
|
||||
String archivePassword = signingCertInfo.getKeystorePassword();
|
||||
char[] pwdArray = (archivePassword != null) ? archivePassword.toCharArray() : "changeit".toCharArray();
|
||||
ks.load(new FileInputStream(signingCertInfo.getKeystorePath()), pwdArray);
|
||||
PrivateKey pk = (PrivateKey) ks.getKey(signingCertInfo.getAlias(), signingCertInfo.getPassword().toCharArray());
|
||||
|
||||
String signAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
|
||||
String message = "SAMLRequest=" + URLEncoder.encode(authnRequest, "UTF-8")
|
||||
+ "&RelayState=" + URLEncoder.encode(relayState, "UTF-8")
|
||||
+ "&SigAlg=" + URLEncoder.encode(signAlgorithm, "UTF-8");
|
||||
|
||||
String signature = null;
|
||||
try{
|
||||
signature = new String(org.apache.commons.codec.binary.Base64.encodeBase64(sign(message, pk)), StandardCharsets.UTF_8);
|
||||
}
|
||||
catch(InvalidKeyException | SignatureException | NoSuchAlgorithmException e){
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
return signature;
|
||||
}
|
||||
|
||||
private static byte[] sign(String message, PrivateKey key) throws InvalidKeyException, SignatureException, NoSuchAlgorithmException {
|
||||
java.security.Signature instance = java.security.Signature.getInstance("SHA1withRSA");
|
||||
instance.initSign(key);
|
||||
instance.update(message.getBytes());
|
||||
return instance.sign();
|
||||
}
|
||||
|
||||
private static AuthnRequest buildAuthnRequest(Saml2ConfigurableProvider provider) throws Exception {
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
package eu.eudat.models.data.saml2;
|
||||
|
||||
public class AuthnRequestModel {
|
||||
|
||||
String authnRequestXml;
|
||||
String relayState;
|
||||
String algorithm;
|
||||
String signature;
|
||||
|
||||
public AuthnRequestModel() {}
|
||||
|
||||
public AuthnRequestModel(String authnRequestXml, String relayState, String algorithm, String signature) {
|
||||
this.authnRequestXml = authnRequestXml;
|
||||
this.relayState = relayState;
|
||||
this.algorithm = algorithm;
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public String getAuthnRequestXml() {
|
||||
return authnRequestXml;
|
||||
}
|
||||
public void setAuthnRequestXml(String authnRequestXml) {
|
||||
this.authnRequestXml = authnRequestXml;
|
||||
}
|
||||
|
||||
public String getRelayState() {
|
||||
return relayState;
|
||||
}
|
||||
public void setRelayState(String relayState) {
|
||||
this.relayState = relayState;
|
||||
}
|
||||
|
||||
public String getAlgorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
public void setAlgorithm(String algorithm) {
|
||||
this.algorithm = algorithm;
|
||||
}
|
||||
|
||||
public String getSignature() {
|
||||
return signature;
|
||||
}
|
||||
public void setSignature(String signature) {
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
export interface AuthnRequestModel {
|
||||
authnRequestXml: string;
|
||||
relayState: string;
|
||||
algorithm: string;
|
||||
signature: string;
|
||||
}
|
|
@ -4,6 +4,7 @@ import { ConfigurationService } from './configuration/configuration.service';
|
|||
import { BaseHttpService } from './http/base-http.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { BaseComponent } from '@common/base/base.component';
|
||||
import { AuthnRequestModel } from '../model/saml2/AuthnRequestModel';
|
||||
|
||||
@Injectable()
|
||||
export class SamlLoginService extends BaseComponent {
|
||||
|
@ -27,8 +28,8 @@ export class SamlLoginService extends BaseComponent {
|
|||
return routeParams.has('spId') ? routeParams.get('spId') : '';
|
||||
}
|
||||
|
||||
getAuthnRequest(configurableLoginId: string): Observable<string> {
|
||||
return this.http.get<string>(this.actionUrl + 'authnRequest/' + configurableLoginId, { headers: this.headers });
|
||||
getAuthnRequest(configurableLoginId: string): Observable<AuthnRequestModel> {
|
||||
return this.http.get<AuthnRequestModel>(this.actionUrl + 'authnRequest/' + configurableLoginId, { headers: this.headers });
|
||||
}
|
||||
|
||||
}
|
|
@ -90,25 +90,18 @@ export class ConfigurableLoginComponent extends BaseComponent implements OnInit
|
|||
this.samlLoginService.getAuthnRequest(provider.configurableLoginId).pipe(takeUntil(this._destroyed))
|
||||
.subscribe(
|
||||
authenticationRequest => {
|
||||
const uint = new Uint8Array(authenticationRequest.length);
|
||||
for (let i = 0, j = authenticationRequest.length; i < j; ++i) {
|
||||
uint[i] = authenticationRequest.charCodeAt(i);
|
||||
const uint = new Uint8Array(authenticationRequest.authnRequestXml.length);
|
||||
for (let i = 0, j = authenticationRequest.authnRequestXml.length; i < j; ++i) {
|
||||
uint[i] = authenticationRequest.authnRequestXml.charCodeAt(i);
|
||||
}
|
||||
const base64String = btoa(pk.deflateRaw(uint, { to: 'string' }));
|
||||
const relayState = this.buildRelayState(provider.spEntityId, provider.configurableLoginId);
|
||||
const url = provider.idpUrl + '?RelayState=' + relayState + '&SAMLRequest=' + encodeURIComponent(base64String);
|
||||
const url = provider.idpUrl + '?SAMLRequest=' + encodeURIComponent(base64String) + '&RelayState=' + encodeURIComponent(authenticationRequest.relayState) + '&SigAlg=' + encodeURIComponent(authenticationRequest.algorithm) + '&Signature=' + encodeURIComponent(authenticationRequest.signature);
|
||||
window.location.href = url;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
buildRelayState(spId: string, configurableLoginId: string): string {
|
||||
let uri = 'spId=' + spId;
|
||||
uri += '&configurableLoginId=' + configurableLoginId;
|
||||
return encodeURIComponent(uri);
|
||||
}
|
||||
|
||||
public configurableLoginUser(code: string, state: string) {
|
||||
if (state !== (<Oauth2ConfigurableProvider>this.provider).state) {
|
||||
this.router.navigate(['/login'])
|
||||
|
|
Loading…
Reference in New Issue