OpenAM之SAML2配置

What’s SAML2

SAML是安全斷言標記語言(英語:Security Assertion Markup Language,簡稱SAML,發音sam-el),一種xml格式的語言。 有兩個點: 第一是安全(Security), 第二是斷言(assertion)

先看它的核心概念斷言(assertion)。 斷言是什麼? 就是做出判斷的語言。比如一個例子:小紅沒有權限讀取根目錄。這就是一個斷言。 這種“做出判斷的語句”我們在很多場合都需要用到。 比如你在網上嘗試登陸一個服務的時候, 這個服務需要知道你是不是合法的用戶。 這個時候如果你能提供一個“安全,可靠,可信任”的斷言:“***有權登陸XX服務”, 那麼這個服務就知道你合法了, 於是就能爲你提供服務了。 這個例子比較抽象,但基本上能表達斷言在實際用例中的作用了。 實際上SAML2的大部分內容就在於證明你是誰,你擁有什麼權限等等了。

接下來第二個概念就是安全(Security)了。 你能提供一個斷言, 別人能不能假冒你提供一個斷言從而騙取服務端的信任呢? 另外服務端爲什麼會信任你給的斷言呢? 這就涉及到安全的問題了。爲了防止斷言被假冒,篡改。SAML中加入了安全措施。 當然現今能抵禦假冒,篡改,重放攻擊的利器就是公鑰-私鑰/keystore系統了。 通過給斷言加上簽名和加密,再結合數字證書系統就確保了SAML不受攻擊。

Config With OpenAM SAML2

OpenAM配置SAML2分2種方式

  • 默認方式(本文采用默認方式,Sign Assertion)
  • 開發方式(自定義AttributeMapper,TokenProvider)

默認方式和開發方式都是分3步走,只是配置有所不同而已:

  • 配置SAML2 STS Instance
  • 配置SAML2 Service Provider(Configure OAuth2 authorization server)
  • 驗證SAML2

配置SAML2 STS Instance

1.進入Top Level Realm,找到STS,這裏的STSSecurity Token Service的意思。
在這裏插入圖片描述
2.new一個STSSupported Token Transforms選擇OPENAM->SAML2,don't invalidate interim OpenAM session
在這裏插入圖片描述
3.Deployment Url Element就是這個STS的名稱,這裏要記住,後面會用到,拼接REST請求的時候,URL爲rest-sts/stsName,那麼我們就是rest-sts/mysts
在這裏插入圖片描述
4.這裏定義一下issuer IdEntity Id
在這裏插入圖片描述
5.詳細設置SAML2 Sign相關

  • Sign Assertion ,打勾√,啓用斷言簽證,不籤的話,報文很少
  • KeyStorePath位於C:\Users\你的用戶\openampro\openampro\keystore.jks (如果你的項目叫openam則爲C:\Users\你的用戶\openam\openam\keystore.jks)
  • Keystore Password,記事本打開.storepass文件,明文copy即可
  • Signature Key Password,記事本打開.keypass文件,明文copy即可
    在這裏插入圖片描述
    在這裏插入圖片描述
    6.保存

配置SAML2 Service Provider

1.返回Dashboard,扎到Common Tasks下面的Configure SAMLv2 Provider
在這裏插入圖片描述
2.由於是本地Localhost的Provider,所以是選擇Create Hosted Service Provider
在這裏插入圖片描述
3.默認配置即可,metadata會自動填寫,如果沒有就寫上項目路徑,Circle Of Trust隨便寫個cot或者mycot即可。
在這裏插入圖片描述
4.保存並返回查看
在這裏插入圖片描述
驗證SAML2

1.POST請求authenticate接口獲得以下authenticate報文,無需任何參數

http://localhost:8099/openampro/json/authenticate

{
  "authId": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJvdGsiOiJraWM1cmM3aGVjN2dsbWIzYWZzczE4MHQ0cCIsInJlYWxtIjoiZGM9b3BlbmFtLGRjPWZvcmdlcm9jayxkYz1vcmciLCJzZXNzaW9uSWQiOiJBUUlDNXdNMkxZNFNmY3c0OFhTUDNlR2NIZVB1VUN3bW9IdGRyVWpsdHZsX1BIby4qQUFKVFNRQUNNREVBQWxOTEFCTTBOVFEzTURVMU1URTBOVGMwTXpFMU1EZzVBQUpUTVFBQSoifQ.Xq4okO5FjamU62bu0xeZbUSE15pCaG_fKw9XseMJi64",
  "template": "",
  "stage": "DataStore1",
  "header": "Sign in",
  "callbacks": [
    {
      "type": "NameCallback",
      "output": [
        {
          "name": "prompt",
          "value": "User Name:"
        }
      ],
      "input": [
        {
          "name": "IDToken1",
          "value": ""
        }
      ]
    },
    {
      "type": "PasswordCallback",
      "output": [
        {
          "name": "prompt",
          "value": "Password:"
        }
      ],
      "input": [
        {
          "name": "IDToken2",
          "value": ""
        }
      ]
    }
  ]
}

 

2.Copy上一部獲取到的報文在body/raw裏面,然後補全authenticate報文的賬號(IDToken1-value)和密碼(IDToken2-value),繼續請求該接口

http://localhost:8099/openampro/json/authenticate

"callbacks": [
    {
      "type": "NameCallback",
      "output": [
        {
          "name": "prompt",
          "value": "User Name:"
        }
      ],
      "input": [
        {
          "name": "IDToken1",
          "value": "這裏填寫賬號"
        }
      ]
    },
    {
      "type": "PasswordCallback",
      "output": [
        {
          "name": "prompt",
          "value": "Password:"
        }
      ],
      "input": [
        {
          "name": "IDToken2",
          "value": "這裏填寫密碼"
        }
      ]
    }
  ]

 

 

在這裏插入圖片描述
3.得到tokenId

AQIC5wM2LY4SfczBElQ-gyrwsTOLXPZscWBxk776W1IYfS4.AAJTSQACMDEAAlNLABM0ODEyODAzNTQxNzU4MDE3MDA3AAJTMQAA

4.拼接報文,把tokenId填入session_id後面的值,然後POST請求translate接口,進行OPENAM->SAML2報文轉換

http://localhost:8099/openampro/rest-sts/mysts?_action=translate

{
    "input_token_state":
        {
            "token_type": "OPENAM",
            "session_id": "AQIC5wM2LY4SfczBElQ-gyrwsTOLXPZscWBxk776W1IYfS4.*AAJTSQACMDEAAlNLABM0ODEyODAzNTQxNzU4MDE3MDA3AAJTMQAA*"
        },
    "output_token_state":
        {
            "token_type": "SAML2",
            "subject_confirmation": "BEARER"
        }
}

 

 

在這裏插入圖片描述
5.得到Signed後的報文,這就是SAML2報文啦

{"issued_token":"
<saml:Assertion xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"s2a8a19d7e7801a6243ad1b34d5066852eca7da372\" IssueInstant=\"2019-05-31T06:25:50Z\" Version=\"2.0\">\r\n
    <saml:Issuer>stsid</saml:Issuer>
    <ds:Signature xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">\r\n
        <ds:SignedInfo>\r\n
            <ds:CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>\r\n
            <ds:SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\"/>\r\n
            <ds:Reference URI=\"#s2a8a19d7e7801a6243ad1b34d5066852eca7da372\">\r\n
                <ds:Transforms>\r\n
                    <ds:Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/>\r\n
                    <ds:Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>\r\n
                </ds:Transforms>\r\n
                <ds:DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/>\r\n
                <ds:DigestValue>AZShA8hm3+BuU8SkTQtbvkjpl9o=</ds:DigestValue>\r\n
            </ds:Reference>\r\n
        </ds:SignedInfo>\r\n
        <ds:SignatureValue>\r\nRkuIgun5A6sInaD3HWZ7CbQXkiWDxTR2zJ6o/h4IOf7jutSl6lCLEHUs1qSjyILO1xeOMS3VsDpn\r\nplpYfZF3tHornzdDm++9x538qDnxlzIHVN3WQKu9yLoqrkw0arU1I+KZb8dKnZHIPf9RnK96RLuz\r\nO4yEsjQrPpB3hRBF0oI=\r\n</ds:SignatureValue>\r\n
        <ds:KeyInfo>\r\n
            <ds:X509Data>\r\n
                <ds:X509Certificate>\r\nMIICQDCCAakCBEeNB0swDQYJKoZIhvcNAQEEBQAwZzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNh\r\nbGlmb3JuaWExFDASBgNVBAcTC1NhbnRhIENsYXJhMQwwCgYDVQQKEwNTdW4xEDAOBgNVBAsTB09w\r\nZW5TU08xDTALBgNVBAMTBHRlc3QwHhcNMDgwMTE1MTkxOTM5WhcNMTgwMTEyMTkxOTM5WjBnMQsw\r\nCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEUMBIGA1UEBxMLU2FudGEgQ2xhcmExDDAK\r\nBgNVBAoTA1N1bjEQMA4GA1UECxMHT3BlblNTTzENMAsGA1UEAxMEdGVzdDCBnzANBgkqhkiG9w0B\r\nAQEFAAOBjQAwgYkCgYEArSQc/U75GB2AtKhbGS5piiLkmJzqEsp64rDxbMJ+xDrye0EN/q1U5Of+\r\nRkDsaN/igkAvV1cuXEgTL6RlafFPcUX7QxDhZBhsYF9pbwtMzi4A4su9hnxIhURebGEmxKW9qJNY\r\nJs0Vo5+IgjxuEWnjnnVgHTs1+mq5QYTA7E6ZyL8CAwEAATANBgkqhkiG9w0BAQQFAAOBgQB3Pw/U\r\nQzPKTPTYi9upbFXlrAKMwtFf2OW4yvGWWvlcwcNSZJmTJ8ARvVYOMEVNbsT4OFcfu2/PeYoAdiDA\r\ncGy/F2Zuj8XJJpuQRSE6PtQqBuDEHjjmOQJ0rV/r8mO1ZCtHRhpZ5zYRjhRC9eCbjx9VrFax0JDC\r\n/FfwWigmrW0Y0Q==\r\n</ds:X509Certificate>\r\n
            </ds:X509Data>\r\n
        </ds:KeyInfo>\r\n
    </ds:Signature>
    <saml:Subject>\r\n
        <saml:NameID Format=\"urn:oasis:names:tc:SAML:1.0:nameid-format:unspecified\">amadmin</saml:NameID>
        <saml:SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\">\r\n
            <saml:SubjectConfirmationData NotOnOrAfter=\"2019-05-31T06:35:50Z\"/>
        </saml:SubjectConfirmation>\r\n
    </saml:Subject>
    <saml:Conditions NotBefore=\"2019-05-31T06:25:50Z\" NotOnOrAfter=\"2019-05-31T06:35:50Z\">\r\n
        <saml:AudienceRestriction>\r\n
            <saml:Audience>stsid</saml:Audience>\r\n
        </saml:AudienceRestriction>\r\n
    </saml:Conditions>\r\n
    <saml:AuthnStatement AuthnInstant=\"2019-05-31T06:25:50Z\">
        <saml:AuthnContext>
            <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PreviousSession</saml:AuthnContextClassRef>
        </saml:AuthnContext>
    </saml:AuthnStatement>
</saml:Assertion>"}

以下部分爲shell+json自動化配置,部分核心配置

使用JSON配置

{
	"invocation_context": "invocation_context_client_sdk",
	"instance_state": {
		"persist-issued-tokens-in-cts": "false",
		"supported-token-transforms": [{
			"inputTokenType": "OPENAM",
			"outputTokenType": "SAML2",
			"invalidateInterimOpenAMSession": false
		}],
		"custom-token-validators": [],
		"custom-token-providers": [{
			"customTokenName":"SAML2_XXX",
			"customOperationClassName":"com.xxxcompany.sts.tokengeneration.saml2.SamlTokenProvider"
		}],
		"custom-token-transforms": [{
			"inputTokenType": "OPENAM",
			"outputTokenType": "SAML2_HSM",
			"invalidateInterimOpenAMSession": false
		}],
		"deployment-config": {
			"deployment-realm": "/sdex_global",
			"deployment-url-element": "ssotoken_transformer_xxxapp",
			"deployment-auth-target-mappings": {},
			"deployment-offloaded-two-way-tls-header-key": null,
			"deployment-tls-offload-engine-hosts": {}
		},
		"saml2-config": {
			"issuer-name": "xxx",
			"saml2-sp-entity-id": "xxxapp_url_tbc",
			"saml2-sp-acs-url": "xxxapp_url_tbc",
			"saml2-name-id-format": "urn:oasis:names:tc:SAML:1.0:nameid-format:unspecified",
			"saml2-token-lifetime-seconds": "30",
			"saml2-custom-conditions-provider-class-name": null,
			"saml2-custom-subject-provider-class-name": null,
			"saml2-custom-authentication-statements-provider-class-name": null,
			"saml2-custom-attribute-statements-provider-class-name": null,
			"saml2-custom-authz-decision-statements-provider-class-name": null,
			"saml2-custom-attribute-mapper-class-name": "com.xxxcompany.sts.tokengeneration.saml2.statements.CustomAttributeMapper",
			"saml2-custom-authn-context-mapper-class-name": null,
			"saml2-attribute-map": {
				"customerId": "session|XXXCom-Customer-Id",
				"guid":"session|guid"				
			},
			"saml2-sign-assertion": "true",
			"saml2-encrypt-assertion": "false",
			"saml2-encrypt-attributes": "false",
			"saml2-encrypt-nameid": "false",
			"saml2-encryption-algorithm": "http://www.w3.org/2001/04/xmlenc#aes128-cbc",
			"saml2-encryption-algorithm-strength": "128",
			"saml2-keystore-filename": "/opt/xxxcompany/xxxapp/xxxapp_SAML_xxx_DEV.keystore",
			"saml2-keystore-password": "changeit",
			"saml2-encryption-key-alias": "",
			"saml2-signature-key-alias": "xxxapp_SAML_CMB_DEV",
			"saml2-signature-key-password": "changeit"
		}
	}
}

 

sts-setup.sh

#!/bin/bash

# Constants for text color
color_off='\033[0m'
color_red='\033[0;31m'
color_green='\033[0;32m'
color_yellow='\033[0;33m'

script_path="$( cd "$(dirname "$0")" ; pwd -P )"

# Load bootstrap file
bootstrap_file=${script_path}/bootstrap.properties
[ ! -f $bootstrap_file ] && { echo "Error: Missing $bootstrap_file"; exit 1; }
source $bootstrap_file
[[ -z "$am_instance_name" ]] && { echo "Error: 'am_instance_name' undefined in $bootstrap_file"; exit 1; }
[[ -z "$am_server_url" ]] && { echo "Error: 'am_server_url' undefined in $bootstrap_file"; exit 1; }
[[ -z "$admin_user" ]] && { echo "Error: 'admin_user' undefined in $bootstrap_file"; exit 1; }
[[ -z "$admin_password_file" ]] && { echo "Error: 'admin_password_file' undefined in $bootstrap_file"; exit 1; }
[ ! -f $admin_password_file ] && { echo "Error: Missing $admin_password_file"; exit 1; }
[[ -z "$com_iplanet_am_cookie_name" ]] && { echo "Error: 'com_iplanet_am_cookie_name' undefined in $bootstrap_file"; exit 1; }
[[ -z "$openam_tools_dir" ]] && { echo "Error: 'openam_tools_dir' undefined in $bootstrap_file"; exit 1; }
[[ -z "$ssoadm" ]] && { echo "Error: 'ssoadm' undefined in $bootstrap_file"; exit 1; }
[[ -z "$realms" ]] && { echo "Error: 'realms' undefined in $bootstrap_file"; exit 1; }

create_sts_instance() {
	realm=$1

	# Validate data file
	data_file=${script_path}/${realm#/}/rest-sts-instance-config.json
	[ ! -f $data_file ] && { echo -e "${color_red}Error: Missing data file ${data_file}${color_off}"; return; }
	deployment_realm=$(cat $data_file 2>/dev/null | python -c 'import sys, json; print json.load(sys.stdin)["instance_state"]["deployment-config"]["deployment-realm"]' 2>/dev/null)
	[[ -z "$deployment_realm" ]] && { echo -e "${color_red}Error: 'deployment-realm' undefined in ${data_file}${color_off}"; return; }
	[ "$deployment_realm" != "$realm" ] && { echo -e "${color_red}Error: Deployment realm '${deployment_realm}' is different than the configuration realm '${realm}'${color_off}"; return; }
	deployment_url_element=$(cat $data_file 2>/dev/null | python -c 'import sys, json; print json.load(sys.stdin)["instance_state"]["deployment-config"]["deployment-url-element"]' 2>/dev/null)
	[[ -z "$deployment_url_element" ]] && { echo -e "${color_red}Error: 'deployment-url-element' undefined in ${data_file}${color_off}"; return; }

	echo -n "Create STS instance ${deployment_realm}/${deployment_url_element} in realm ${realm}... "
	admin_password=$(cat $admin_password_file)
	auth_response=$(curl -k -s -X POST \
					-H "Content-type: application/json" \
					-H "X-OpenAM-Username:${admin_user}" \
					-H "X-OpenAM-Password:${admin_password}" \
					${am_server_url}/json/authenticate)
	am_token=$(echo $auth_response | python -c 'import sys, json; print json.load(sys.stdin)["tokenId"]' 2>/dev/null)
	[[ -z "$am_token" ]] && { echo -e "${color_red}FAILED!\nAuthentication failed.\nAuthentication response: ${auth_response}${color_off}"; return; }
	sts_response=$(curl -k -s -X POST \
					-H "Content-Type: application/json" \
					-H "${com_iplanet_am_cookie_name}: ${am_token}" \
					-d @$data_file \
					${am_server_url}/sts-publish/rest?_action=create)
	sts_response_result=$(echo $sts_response | python -c 'import sys, json; print json.load(sys.stdin)["result"]' 2>/dev/null)
	[ "$sts_response_result" = "success" ] && echo -e "${color_green}SUCCESS!" || echo -e "${color_red}FAILED!"
	echo -e "${sts_response}${color_off}"
}

for realm in "${realms[@]}"
do
	echo "================================================="
	echo "Configuring realm ${realm}:"

	create_sts_instance $realm

	echo "================================================="
done

 

bootstrap.properties

# AM settings
am_instance_name=xxx-dsp-openam
am_server_url=https://yourserver/${am_instance_name}
admin_user=amadmin
admin_password_file=/opt/openam/openam-tools/admin/.pwd
com_iplanet_am_cookie_name=AMToken

# SSO Admin Tools settings
openam_tools_dir=/opt/openam/openam-tools/admin
ssoadm=${openam_tools_dir}/${am_instance_name}/bin/ssoadm

# Realms to configure
realms=("/sdex_global")

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章