/OIDC_IdP_Mock.java Secret
Created
January 9, 2025 06:36
SAML&OIDC IdP Demo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.HashMap; | |
import java.util.Map; | |
import org.springframework.util.Assert; | |
import com.aliyuncs.CommonRequest; | |
import com.aliyuncs.CommonResponse; | |
import com.aliyuncs.DefaultAcsClient; | |
import com.aliyuncs.IAcsClient; | |
import com.aliyuncs.http.ProtocolType; | |
import com.aliyuncs.profile.DefaultProfile; | |
import com.nimbusds.jose.Algorithm; | |
import com.nimbusds.jose.JWSAlgorithm; | |
import com.nimbusds.jose.JWSHeader; | |
import com.nimbusds.jose.JWSObject; | |
import com.nimbusds.jose.JWSSigner; | |
import com.nimbusds.jose.Payload; | |
import com.nimbusds.jose.crypto.RSASSASigner; | |
import com.nimbusds.jose.jwk.KeyUse; | |
import com.nimbusds.jose.jwk.RSAKey; | |
import com.nimbusds.jose.jwk.gen.RSAKeyGenerator; | |
public class OIDC_IdP_Mock { | |
// 证书的json串,避免每次重启的时候都重新生成证书 | |
private static final String RasKeyJsonString = "{\"p\":\"-ns8s9wqHlGQq7Cw*****w\"}"; | |
public static void main(String[] args) throws Exception { | |
String idToken = buildIDToken("https://oidctest.chengchao.name", "chengchao", "aliyunoss001"); | |
String stsResult = getSTSTokenWithOIDC(idToken); | |
System.out.println(stsResult); | |
} | |
public static RSAKey generateRSAKey() throws Exception { | |
RSAKey raskey = new RSAKeyGenerator(2048) | |
.keyID("demokey_001") | |
.keyUse(new KeyUse("sig")) | |
.algorithm(new Algorithm("RS256")) | |
.generate(); | |
return raskey; | |
} | |
public static String commonInvoke(IAcsClient client, String domain, String version, String action, | |
Map<String, String> params) throws Exception { | |
Assert.notNull(client, "client can not be null!"); | |
Assert.hasText(domain, "domain can not be blank!"); | |
Assert.hasText(version, "version can not be blank!"); | |
Assert.hasText(action, "action can not be blank!"); | |
CommonRequest request = new CommonRequest(); | |
request.setSysDomain(domain); | |
request.setSysVersion(version); | |
request.setSysAction(action); | |
if (params != null && !params.isEmpty()) { | |
for (Map.Entry<String, String> entry : params.entrySet()) { | |
request.putQueryParameter(entry.getKey(), entry.getValue()); | |
} | |
} | |
request.setSysProtocol(ProtocolType.HTTPS); | |
CommonResponse response = client.getCommonResponse(request); | |
return response.getData(); | |
} | |
public static String getSTSTokenWithOIDC(String idToken) throws Exception { | |
DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou"); | |
IAcsClient client = new DefaultAcsClient(profile); | |
String action = "AssumeRoleWithOIDC"; | |
Map<String, String> params = new HashMap<>(); | |
params.put("OIDCProviderArn", "acs:ram::17642*****:oidc-provider/OIDCTest001"); | |
params.put("RoleArn", "acs:ram::17642*****:role/role-oidc001"); | |
params.put("OIDCToken", idToken); | |
params.put("RoleSessionName", "chengchao"); | |
String result = commonInvoke(client, "sts.cn-hangzhou.aliyuncs.com", "2015-04-01", action, params); | |
return result; | |
} | |
public static String buildIDToken(String issuer, String subject, String audience) throws Exception { | |
RSAKey MyRasKey = RSAKey.parse(RasKeyJsonString); | |
String nonce = ""; | |
int oneweekseconds = 7 * 24 * 3600; | |
int t = (int)(System.currentTimeMillis() / 1000); | |
Map<String, Object> payload = new HashMap<>(); | |
payload.put("iss", issuer); | |
payload.put("aud", audience); | |
payload.put("exp", t + oneweekseconds); | |
payload.put("iat", t); | |
payload.put("sub", subject); | |
payload.put("nonce", nonce); | |
payload.put("jti", subject); | |
JWSSigner signer = new RSASSASigner(MyRasKey); | |
JWSObject jwsObject = new JWSObject( | |
new JWSHeader.Builder(JWSAlgorithm.RS256).keyID(MyRasKey.getKeyID()).build(), | |
new Payload(JsonUtils.toJsonString(payload))); | |
jwsObject.sign(signer); | |
String result = jwsObject.serialize(); | |
return result; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
<modelVersion>4.0.0</modelVersion> | |
<groupId>name.chengchao</groupId> | |
<artifactId>demo-saml</artifactId> | |
<version>0.0.1-SNAPSHOT</version> | |
<properties> | |
<maven.compiler.source>17</maven.compiler.source> | |
<maven.compiler.target>17</maven.compiler.target> | |
</properties> | |
<dependencies> | |
<dependency> | |
<groupId>org.bouncycastle</groupId> | |
<artifactId>bcprov-jdk15on</artifactId> | |
<version>1.70</version> | |
</dependency> | |
<dependency> | |
<groupId>org.bouncycastle</groupId> | |
<artifactId>bcpkix-jdk15on</artifactId> | |
<version>1.70</version> | |
</dependency> | |
<dependency> | |
<groupId>org.opensaml</groupId> | |
<artifactId>opensaml-core</artifactId> | |
<version>4.0.1</version> | |
</dependency> | |
<dependency> | |
<groupId>org.opensaml</groupId> | |
<artifactId>opensaml-saml-api</artifactId> | |
<version>4.0.1</version> | |
</dependency> | |
<dependency> | |
<groupId>org.opensaml</groupId> | |
<artifactId>opensaml-saml-impl</artifactId> | |
<version>4.0.1</version> | |
</dependency> | |
<dependency> | |
<groupId>ognl</groupId> | |
<artifactId>ognl</artifactId> | |
<version>3.1.12</version> | |
</dependency> | |
<dependency> | |
<groupId>com.aliyun</groupId> | |
<artifactId>aliyun-java-sdk-core</artifactId> | |
<version>4.7.1</version> | |
</dependency> | |
<dependency> | |
<groupId>commons-io</groupId> | |
<artifactId>commons-io</artifactId> | |
<version>2.10.0</version> | |
</dependency> | |
<dependency> | |
<groupId>org.opensaml</groupId> | |
<artifactId>opensaml-security-api</artifactId> | |
<version>4.0.1</version> | |
</dependency> | |
<!-- jwt lib --> | |
<dependency> | |
<groupId>com.nimbusds</groupId> | |
<artifactId>nimbus-jose-jwt</artifactId> | |
<version>9.10.1</version> | |
</dependency> | |
<dependency> | |
<groupId>com.alibaba</groupId> | |
<artifactId>fastjson</artifactId> | |
<version>1.2.83</version> | |
</dependency> | |
</dependencies> | |
</project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.ByteArrayOutputStream; | |
import java.io.File; | |
import java.math.BigInteger; | |
import java.nio.charset.StandardCharsets; | |
import java.security.KeyFactory; | |
import java.security.KeyPair; | |
import java.security.KeyPairGenerator; | |
import java.security.PrivateKey; | |
import java.security.PublicKey; | |
import java.security.Security; | |
import java.security.cert.X509Certificate; | |
import java.security.spec.PKCS8EncodedKeySpec; | |
import java.security.spec.X509EncodedKeySpec; | |
import java.time.Instant; | |
import java.time.temporal.ChronoUnit; | |
import java.util.Base64; | |
import java.util.Date; | |
import java.util.HashMap; | |
import java.util.Map; | |
import javax.xml.transform.Transformer; | |
import javax.xml.transform.TransformerFactory; | |
import javax.xml.transform.dom.DOMSource; | |
import javax.xml.transform.stream.StreamResult; | |
import org.apache.commons.io.FileUtils; | |
import org.bouncycastle.asn1.x500.X500Name; | |
import org.bouncycastle.cert.X509CertificateHolder; | |
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; | |
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; | |
import org.bouncycastle.jce.provider.BouncyCastleProvider; | |
import org.bouncycastle.operator.ContentSigner; | |
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; | |
import org.opensaml.core.config.InitializationService; | |
import org.opensaml.saml.common.xml.SAMLConstants; | |
import org.opensaml.saml.saml2.core.Response; | |
import org.opensaml.saml.saml2.core.impl.ResponseMarshaller; | |
import org.opensaml.saml.saml2.metadata.EntityDescriptor; | |
import org.opensaml.saml.saml2.metadata.IDPSSODescriptor; | |
import org.opensaml.saml.saml2.metadata.KeyDescriptor; | |
import org.opensaml.saml.saml2.metadata.SingleSignOnService; | |
import org.opensaml.saml.saml2.metadata.impl.EntityDescriptorBuilder; | |
import org.opensaml.saml.saml2.metadata.impl.EntityDescriptorMarshaller; | |
import org.opensaml.saml.saml2.metadata.impl.IDPSSODescriptorBuilder; | |
import org.opensaml.saml.saml2.metadata.impl.KeyDescriptorBuilder; | |
import org.opensaml.saml.saml2.metadata.impl.SingleSignOnServiceBuilder; | |
import org.opensaml.security.credential.UsageType; | |
import org.opensaml.security.x509.BasicX509Credential; | |
import org.opensaml.xmlsec.signature.KeyInfo; | |
import org.opensaml.xmlsec.signature.X509Data; | |
import org.opensaml.xmlsec.signature.impl.KeyInfoBuilder; | |
import org.opensaml.xmlsec.signature.impl.X509CertificateBuilder; | |
import org.opensaml.xmlsec.signature.impl.X509DataBuilder; | |
import org.w3c.dom.Element; | |
public class SAML_IdP_Mock { | |
public static final String ALIYUN_ROLE_SSO_IDENTIFIER = "urn:alibaba:cloudcomputing"; | |
public static final String ALIYUN_ROLE_SSO_REPLYURL = "https://signin.aliyun.com/saml-role/sso"; | |
public static final String IDP_ENTITY_ID = "chengchaoIdP-Demo"; | |
public static final String IDP_LOGIN_LOCATION = "https://test.com/samlLogin.do"; | |
public static void main(String[] args) throws Exception { | |
InitializationService.initialize(); | |
String publicKeyString = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AM******"; | |
String privateKeyString = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASC*******"; | |
KeyPair keyPair = paserKeypair(publicKeyString, privateKeyString); | |
X509Certificate certificate = generateCertificate(keyPair); | |
String idpMetaXMLString = generateIdpMetaXML(certificate); | |
//生成IdP Metadata.xml文件 | |
FileUtils.write(new File("/Users/charles/Desktop/chengchaoIdPMetadata.xml"), idpMetaXMLString, StandardCharsets.UTF_8); | |
//生成SAML Response | |
Map<String, String> attributeMap = new HashMap<>(); | |
attributeMap.put("https://www.aliyun.com/SAML-Role/Attributes/RoleSessionName", "chengchao"); | |
attributeMap.put("https://www.aliyun.com/SAML-Role/Attributes/Role", "acs:ram::17642*****:role/rolefordemoidp,acs:ram::17642*****:saml-provider/DemoIdP"); | |
BasicX509Credential basicX509Credential = new BasicX509Credential(certificate, keyPair.getPrivate()); | |
String samlResponseStr = buildSamlResponse(basicX509Credential,attributeMap); | |
String base64EncodeSAMLResponse = java.util.Base64.getEncoder().encodeToString(samlResponseStr.getBytes()); | |
System.out.println(base64EncodeSAMLResponse); | |
} | |
public static String buildSamlResponse(BasicX509Credential basicX509Credential,Map<String, String> attributeMap) throws Exception { | |
Response responseInitial = SamlUtils.createSAMLResponse(null, ALIYUN_ROLE_SSO_IDENTIFIER, | |
ALIYUN_ROLE_SSO_REPLYURL, "demoNameID", attributeMap, basicX509Credential); | |
ResponseMarshaller marshaller = new ResponseMarshaller(); | |
Element element = marshaller.marshall(responseInitial); | |
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |
Transformer transformer = TransformerFactory.newInstance().newTransformer(); | |
transformer.transform(new DOMSource(element), new StreamResult(baos)); | |
String responseStr = new String(baos.toByteArray()); | |
return responseStr; | |
} | |
public static X509Certificate generateCertificate(KeyPair keyPair) throws Exception { | |
Security.addProvider(new BouncyCastleProvider()); | |
X500Name issuer = new X500Name("CN=Chengchao CA"); | |
X500Name subject = new X500Name("CN=Chengchao SSO Demo"); | |
BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis()); | |
Date notBefore = Date.from(Instant.now().minus(1, ChronoUnit.DAYS)); | |
Date notAfter = Date.from(Instant.now().plus(365, ChronoUnit.DAYS)); | |
JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder( | |
issuer, | |
serialNumber, | |
notBefore, | |
notAfter, | |
subject, | |
keyPair.getPublic()); | |
ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSAEncryption") | |
.setProvider("BC") | |
.build(keyPair.getPrivate()); | |
X509CertificateHolder certHolder = certBuilder.build(signer); | |
return new JcaX509CertificateConverter() | |
.setProvider("BC") | |
.getCertificate(certHolder); | |
} | |
public static KeyPair paserKeypair(String publicKeyString, String privateKeyString) throws Exception { | |
byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyString); | |
byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyString); | |
KeyFactory keyFactory = KeyFactory.getInstance("RSA"); | |
PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyBytes)); | |
PrivateKey privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes)); | |
return new KeyPair(publicKey, privateKey); | |
} | |
public static void generateKeyPair() throws Exception { | |
// 1. 创建 KeyPairGenerator 对象,指定 RSA 算法 | |
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); | |
// 2. 初始化 KeyPairGenerator,设置密钥长度(2048 位) | |
keyPairGen.initialize(2048); | |
// 3. 生成密钥对 | |
KeyPair keyPair = keyPairGen.generateKeyPair(); | |
// 3. 将密钥编码为 Base64 字符串 | |
String publicKeyString = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded()); | |
String privateKeyString = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded()); | |
// 输出密钥字符串 | |
System.out.println("Public Key: " + publicKeyString); | |
System.out.println("Private Key: " + privateKeyString); | |
} | |
public static String generateIdpMetaXML(X509Certificate certificate) throws Exception { | |
EntityDescriptorBuilder entityDescriptorBuilder = new EntityDescriptorBuilder(); | |
EntityDescriptor entityDescriptor = entityDescriptorBuilder.buildObject(); | |
entityDescriptor.setEntityID(IDP_ENTITY_ID); | |
IDPSSODescriptorBuilder idpssoDescriptorBuilder = new IDPSSODescriptorBuilder(); | |
IDPSSODescriptor idpssoDescriptor = idpssoDescriptorBuilder.buildObject(); | |
idpssoDescriptor.setWantAuthnRequestsSigned(false); | |
idpssoDescriptor.addSupportedProtocol(SAMLConstants.SAML20P_NS); | |
entityDescriptor.getRoleDescriptors().add(idpssoDescriptor); | |
KeyInfoBuilder keyInfoBuilder = new KeyInfoBuilder(); | |
KeyInfo keyInfo = keyInfoBuilder.buildObject(); | |
X509CertificateBuilder x509CertificateBuilder = new X509CertificateBuilder(); | |
org.opensaml.xmlsec.signature.X509Certificate x509Certificate = x509CertificateBuilder.buildObject(); | |
x509Certificate.setValue(java.util.Base64.getEncoder().encodeToString(certificate.getEncoded())); | |
X509DataBuilder x509DataBuilder = new X509DataBuilder(); | |
X509Data x509Data = x509DataBuilder.buildObject(); | |
x509Data.getX509Certificates().add(x509Certificate); | |
keyInfo.getX509Datas().add(x509Data); | |
KeyDescriptorBuilder keyDescriptorBuilder = new KeyDescriptorBuilder(); | |
KeyDescriptor keyDescriptor = keyDescriptorBuilder.buildObject(); | |
keyDescriptor.setUse(UsageType.SIGNING); | |
keyDescriptor.setKeyInfo(keyInfo); | |
idpssoDescriptor.getKeyDescriptors().add(keyDescriptor); | |
SingleSignOnServiceBuilder singleSignOnServiceBuilder = new SingleSignOnServiceBuilder(); | |
SingleSignOnService singleSignOnService = singleSignOnServiceBuilder.buildObject(); | |
singleSignOnService.setBinding(SAMLConstants.SAML2_POST_BINDING_URI); | |
singleSignOnService.setLocation(IDP_LOGIN_LOCATION); | |
idpssoDescriptor.getSingleSignOnServices().add(singleSignOnService); | |
// output EntityDescriptor | |
EntityDescriptorMarshaller marshaller = new EntityDescriptorMarshaller(); | |
Element element = marshaller.marshall(entityDescriptor); | |
ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |
Transformer transformer = TransformerFactory.newInstance().newTransformer(); | |
transformer.transform(new DOMSource(element), new StreamResult(baos)); | |
// XMLHelper.writeNode(element, baos); | |
String metaXMLStr = new String(baos.toByteArray()); | |
return metaXMLStr; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.time.Instant; | |
import java.time.temporal.ChronoUnit; | |
import java.util.Map; | |
import java.util.UUID; | |
import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; | |
import org.opensaml.core.xml.io.Marshaller; | |
import org.opensaml.core.xml.schema.XSString; | |
import org.opensaml.core.xml.schema.impl.XSStringBuilder; | |
import org.opensaml.saml.common.SAMLVersion; | |
import org.opensaml.saml.saml2.core.Assertion; | |
import org.opensaml.saml.saml2.core.Attribute; | |
import org.opensaml.saml.saml2.core.AttributeStatement; | |
import org.opensaml.saml.saml2.core.AttributeValue; | |
import org.opensaml.saml.saml2.core.Audience; | |
import org.opensaml.saml.saml2.core.AudienceRestriction; | |
import org.opensaml.saml.saml2.core.AuthnContext; | |
import org.opensaml.saml.saml2.core.AuthnContextClassRef; | |
import org.opensaml.saml.saml2.core.AuthnStatement; | |
import org.opensaml.saml.saml2.core.Conditions; | |
import org.opensaml.saml.saml2.core.Issuer; | |
import org.opensaml.saml.saml2.core.NameID; | |
import org.opensaml.saml.saml2.core.NameIDType; | |
import org.opensaml.saml.saml2.core.Response; | |
import org.opensaml.saml.saml2.core.Status; | |
import org.opensaml.saml.saml2.core.StatusCode; | |
import org.opensaml.saml.saml2.core.Subject; | |
import org.opensaml.saml.saml2.core.SubjectConfirmation; | |
import org.opensaml.saml.saml2.core.SubjectConfirmationData; | |
import org.opensaml.saml.saml2.core.impl.AssertionBuilder; | |
import org.opensaml.saml.saml2.core.impl.AttributeBuilder; | |
import org.opensaml.saml.saml2.core.impl.AttributeStatementBuilder; | |
import org.opensaml.saml.saml2.core.impl.AudienceBuilder; | |
import org.opensaml.saml.saml2.core.impl.AudienceRestrictionBuilder; | |
import org.opensaml.saml.saml2.core.impl.AuthnContextBuilder; | |
import org.opensaml.saml.saml2.core.impl.AuthnContextClassRefBuilder; | |
import org.opensaml.saml.saml2.core.impl.AuthnStatementBuilder; | |
import org.opensaml.saml.saml2.core.impl.ConditionsBuilder; | |
import org.opensaml.saml.saml2.core.impl.IssuerBuilder; | |
import org.opensaml.saml.saml2.core.impl.NameIDBuilder; | |
import org.opensaml.saml.saml2.core.impl.ResponseBuilder; | |
import org.opensaml.saml.saml2.core.impl.StatusBuilder; | |
import org.opensaml.saml.saml2.core.impl.StatusCodeBuilder; | |
import org.opensaml.saml.saml2.core.impl.SubjectBuilder; | |
import org.opensaml.saml.saml2.core.impl.SubjectConfirmationBuilder; | |
import org.opensaml.saml.saml2.core.impl.SubjectConfirmationDataBuilder; | |
import org.opensaml.security.credential.Credential; | |
import org.opensaml.security.x509.BasicX509Credential; | |
import org.opensaml.xmlsec.EncryptionConfiguration; | |
import org.opensaml.xmlsec.SecurityConfigurationSupport; | |
import org.opensaml.xmlsec.keyinfo.KeyInfoGenerator; | |
import org.opensaml.xmlsec.keyinfo.KeyInfoGeneratorFactory; | |
import org.opensaml.xmlsec.keyinfo.KeyInfoGeneratorManager; | |
import org.opensaml.xmlsec.keyinfo.NamedKeyInfoGeneratorManager; | |
import org.opensaml.xmlsec.signature.KeyInfo; | |
import org.opensaml.xmlsec.signature.Signature; | |
import org.opensaml.xmlsec.signature.impl.SignatureBuilder; | |
import org.opensaml.xmlsec.signature.support.SignatureConstants; | |
import org.opensaml.xmlsec.signature.support.Signer; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
import org.springframework.util.Assert; | |
public class SamlUtils { | |
private static Logger logger = LoggerFactory.getLogger(SamlUtils.class); | |
public static Response createSAMLResponse(String samlRequestID, String identifier, String replyUrl, String nameID, | |
Map<String, String> attributes,BasicX509Credential basicX509Credential) throws Exception { | |
Assert.hasText(identifier, "identifier can not be blank!"); | |
Assert.hasText(replyUrl, "replyUrl can not be blank!"); | |
Assert.hasText(nameID, "nameID can not be blank!"); | |
// Assert.notEmpty(attributes, "attributes can not be empty!"); | |
logger.info("**********************************SAML INFO**********************************"); | |
logger.info("samlRequestID: " + samlRequestID); | |
logger.info("identifier: " + identifier); | |
logger.info("replyUrl: " + replyUrl); | |
logger.info("nameID: " + nameID); | |
logger.info("attributes: " + attributes); | |
logger.info("*****************************************************************************"); | |
// ****************默认参数*************** | |
Instant authenticationTime = Instant.now(); | |
String issuer = Test.IDP_ENTITY_ID; | |
Integer samlAssertionDays = 2; | |
// ****************默认参数*************** | |
Signature signature = createSignature(basicX509Credential); | |
Status status = createStatus(); | |
Issuer responseIssuer = null; | |
Issuer assertionIssuer = null; | |
Subject subject = null; | |
AttributeStatement attributeStatement = null; | |
if (issuer != null) { | |
responseIssuer = createIssuer(issuer); | |
assertionIssuer = createIssuer(issuer); | |
} | |
if (attributes != null) { | |
attributeStatement = createAttributeStatement(attributes); | |
} | |
if (nameID != null) { | |
subject = createSubject(replyUrl, nameID, samlAssertionDays, samlRequestID); | |
} | |
AuthnStatement authnStatement = createAuthnStatement(authenticationTime); | |
Assertion assertion = createAssertion(authenticationTime, subject, assertionIssuer, authnStatement, | |
attributeStatement, samlAssertionDays, identifier); | |
Response response = createResponse(authenticationTime, responseIssuer, status, assertion); | |
if (null != samlRequestID) { | |
response.setInResponseTo(samlRequestID); | |
} | |
// aliyun cloud sso 开启新的校验 | |
response.setDestination(replyUrl); | |
// id不能以数字开头,所以统一加"id" | |
response.setID("id" + response.getID()); | |
response.getAssertions().get(0).setID("id" + response.getAssertions().get(0).getID()); | |
// aliyun 两种都可以,aws需要把signature放在assertion里 | |
// response.setSignature(signature); | |
response.getAssertions().get(0).setSignature(signature); | |
Marshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory() | |
.getMarshaller(response.getElementQName()); | |
marshaller.marshall(response); | |
if (signature != null) { | |
Signer.signObject(signature); | |
} | |
return response; | |
} | |
private static Conditions createConditions(Instant notOnOrAfter, final String audienceUri) { | |
ConditionsBuilder conditionsBuilder = new ConditionsBuilder(); | |
final Conditions conditions = conditionsBuilder.buildObject(); | |
conditions.setNotOnOrAfter(notOnOrAfter); | |
AudienceRestrictionBuilder audienceRestrictionBuilder = new AudienceRestrictionBuilder(); | |
final AudienceRestriction audienceRestriction = audienceRestrictionBuilder.buildObject(); | |
AudienceBuilder audienceBuilder = new AudienceBuilder(); | |
final Audience audience = audienceBuilder.buildObject(); | |
audience.setURI(audienceUri); | |
audienceRestriction.getAudiences().add(audience); | |
conditions.getAudienceRestrictions().add(audienceRestriction); | |
return conditions; | |
} | |
private static Response createResponse(Instant issueDate, Issuer issuer, Status status, Assertion assertion) { | |
ResponseBuilder responseBuilder = new ResponseBuilder(); | |
Response response = responseBuilder.buildObject(); | |
response.setID(UUID.randomUUID().toString()); | |
response.setIssueInstant(issueDate); | |
response.setVersion(SAMLVersion.VERSION_20); | |
response.setIssuer(issuer); | |
response.setStatus(status); | |
response.getAssertions().add(assertion); | |
return response; | |
} | |
private static Assertion createAssertion(Instant issueDate, Subject subject, Issuer issuer, | |
AuthnStatement authnStatement, AttributeStatement attributeStatement, final Integer samlAssertionDays, | |
final String identifier) { | |
AssertionBuilder assertionBuilder = new AssertionBuilder(); | |
Assertion assertion = assertionBuilder.buildObject(); | |
assertion.setID(UUID.randomUUID().toString()); | |
assertion.setIssueInstant(issueDate); | |
assertion.setSubject(subject); | |
assertion.setIssuer(issuer); | |
Instant currentDate = Instant.now(); | |
if (samlAssertionDays != null) { | |
currentDate = currentDate.plus(samlAssertionDays, ChronoUnit.DAYS); | |
} | |
Conditions conditions = createConditions(currentDate, identifier); | |
assertion.setConditions(conditions); | |
if (authnStatement != null) { | |
assertion.getAuthnStatements().add(authnStatement); | |
} | |
if (attributeStatement != null) { | |
assertion.getAttributeStatements().add(attributeStatement); | |
} | |
return assertion; | |
} | |
private static Issuer createIssuer(final String issuerName) { | |
// create Issuer object | |
IssuerBuilder issuerBuilder = new IssuerBuilder(); | |
Issuer issuer = issuerBuilder.buildObject(); | |
issuer.setValue(issuerName); | |
return issuer; | |
} | |
private static Subject createSubject(final String replyUrl, final String nameID, final Integer samlAssertionDays, | |
String samlRequestID) { | |
Instant currentDate = Instant.now(); | |
if (samlAssertionDays != null) { | |
currentDate = currentDate.plus(samlAssertionDays, ChronoUnit.DAYS); | |
} | |
// create name element | |
NameIDBuilder nameIdBuilder = new NameIDBuilder(); | |
NameID nameId = nameIdBuilder.buildObject(); | |
nameId.setValue(nameID); | |
nameId.setFormat(NameIDType.EMAIL); | |
SubjectConfirmationDataBuilder dataBuilder = new SubjectConfirmationDataBuilder(); | |
SubjectConfirmationData subjectConfirmationData = dataBuilder.buildObject(); | |
subjectConfirmationData.setNotOnOrAfter(currentDate); | |
subjectConfirmationData.setRecipient(replyUrl); | |
if (null != samlRequestID) { | |
subjectConfirmationData.setInResponseTo(samlRequestID); | |
} | |
SubjectConfirmationBuilder subjectConfirmationBuilder = new SubjectConfirmationBuilder(); | |
SubjectConfirmation subjectConfirmation = subjectConfirmationBuilder.buildObject(); | |
subjectConfirmation.setMethod("urn:oasis:names:tc:SAML:2.0:cm:bearer"); | |
subjectConfirmation.setSubjectConfirmationData(subjectConfirmationData); | |
// create subject element | |
SubjectBuilder subjectBuilder = new SubjectBuilder(); | |
Subject subject = subjectBuilder.buildObject(); | |
subject.setNameID(nameId); | |
subject.getSubjectConfirmations().add(subjectConfirmation); | |
return subject; | |
} | |
private static AuthnStatement createAuthnStatement(Instant issueDate) { | |
// create authcontextclassref object | |
AuthnContextClassRefBuilder classRefBuilder = new AuthnContextClassRefBuilder(); | |
AuthnContextClassRef classRef = classRefBuilder.buildObject(); | |
// classRef.setAuthnContextClassRef("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"); | |
classRef.setURI("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"); | |
// create authcontext object | |
AuthnContextBuilder authContextBuilder = new AuthnContextBuilder(); | |
AuthnContext authnContext = authContextBuilder.buildObject(); | |
authnContext.setAuthnContextClassRef(classRef); | |
// create authenticationstatement object | |
AuthnStatementBuilder authStatementBuilder = new AuthnStatementBuilder(); | |
AuthnStatement authnStatement = authStatementBuilder.buildObject(); | |
authnStatement.setAuthnInstant(issueDate); | |
authnStatement.setAuthnContext(authnContext); | |
return authnStatement; | |
} | |
private static AttributeStatement createAttributeStatement(Map<String, String> attributes) { | |
// create authenticationstatement object | |
AttributeStatementBuilder attributeStatementBuilder = new AttributeStatementBuilder(); | |
AttributeStatement attributeStatement = attributeStatementBuilder.buildObject(); | |
AttributeBuilder attributeBuilder = new AttributeBuilder(); | |
if (attributes != null) { | |
for (Map.Entry<String, String> entry : attributes.entrySet()) { | |
Attribute attribute = attributeBuilder.buildObject(); | |
attribute.setName(entry.getKey()); | |
// 暂时修改成不支持数组的方式, | |
XSStringBuilder stringBuilder = new XSStringBuilder(); | |
XSString attributeValue = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, | |
XSString.TYPE_NAME); | |
attributeValue.setValue(entry.getValue()); | |
attribute.getAttributeValues().add(attributeValue); | |
attributeStatement.getAttributes().add(attribute); | |
} | |
} | |
return attributeStatement; | |
} | |
private static Status createStatus() { | |
StatusCodeBuilder statusCodeBuilder = new StatusCodeBuilder(); | |
StatusCode statusCode = statusCodeBuilder.buildObject(); | |
statusCode.setValue(StatusCode.SUCCESS); | |
StatusBuilder statusBuilder = new StatusBuilder(); | |
Status status = statusBuilder.buildObject(); | |
status.setStatusCode(statusCode); | |
return status; | |
} | |
private static Signature createSignature(BasicX509Credential basicX509Credential) throws Exception { | |
SignatureBuilder builder = new SignatureBuilder(); | |
Signature signature = builder.buildObject(); | |
signature.setSigningCredential(basicX509Credential); | |
signature.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1); | |
signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS); | |
// cloudfare need keyinfo test | |
signature.setKeyInfo(getKeyInfo(signature.getSigningCredential())); | |
return signature; | |
} | |
private static KeyInfo getKeyInfo(Credential credential) throws Exception { | |
EncryptionConfiguration secConfiguration = SecurityConfigurationSupport.getGlobalEncryptionConfiguration(); | |
NamedKeyInfoGeneratorManager namedKeyInfoGeneratorManager = secConfiguration.getDataKeyInfoGeneratorManager(); | |
KeyInfoGeneratorManager keyInfoGeneratorManager = namedKeyInfoGeneratorManager.getDefaultManager(); | |
KeyInfoGeneratorFactory keyInfoGeneratorFactory = keyInfoGeneratorManager.getFactory(credential); | |
KeyInfoGenerator keyInfoGenerator = keyInfoGeneratorFactory.newInstance(); | |
KeyInfo keyInfo = keyInfoGenerator.generate(credential); | |
// keyInfo.getX509Datas().clear(); | |
return keyInfo; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment