Skip to content

Instantly share code, notes, and snippets.

@ichengchao
Created January 9, 2025 06:36

Revisions

  1. ichengchao created this gist Jan 9, 2025.
    101 changes: 101 additions & 0 deletions OIDC_IdP_Mock.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,101 @@
    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;

    }

    }
    198 changes: 198 additions & 0 deletions SAML_IdP_Mock.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,198 @@
    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;
    }

    }
    315 changes: 315 additions & 0 deletions SamlUtils.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,315 @@
    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;
    }
    }
    71 changes: 71 additions & 0 deletions pom.xml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,71 @@
    <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>