saml 数字签名校验

private String publicKey;
44. private String privateKey;
45.
46. @Before
47. public void setUp() throws Exception {
48. Map<String, Object> keyMap = RSACoder.initKey();
49.
50. publicKey = RSACoder.getPublicKey(keyMap);
51. privateKey = RSACoder.getPrivateKey(keyMap);
52. System.err.println("公钥: \n\r" + publicKey);
53. System.err.println("私钥: \n\r" + privateKey);
54. }
55.
56. @Test
57. public void test() throws Exception {
58. System.err.println("公钥加密——私钥解密");
59. String inputStr = "abc";
60. byte[] data = inputStr.getBytes();
61. byte[] encodedData = RSACoder.encryptByPublicKey(data, publicKey);
62. byte[] decodedData = RSACoder.decryptByPrivateKey(encodedData, privateKey);
63.
64. String outputStr = new String(decodedData);
65. System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);
66. assertEquals(inputStr, outputStr);
67.
68. }
69.
70. @Test
71. public void testSign() throws Exception {
72. System.err.println("私钥加密——公钥解密");
73. String inputStr = "sign";
74. byte[] data = inputStr.getBytes();
75. byte[] encodedData = RSACoder.encryptByPrivateKey(data, privateKey);
76. byte[] decodedData = RSACoder.decryptByPublicKey(encodedData, publicKey);
77.
78. String outputStr = new String(decodedData);
79. System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);
80. assertEquals(inputStr, outputStr);
81.
82. System.err.println("私钥签名——公钥验证签名");
83. // 产生签名
84. String sign = RSACoder.sign(encodedData, privateKey);
85. System.err.println("签名:\r" + sign);
86.
87. // 验证签名
88. boolean status = RSACoder.verify(encodedData, publicKey, sign);
89. System.err.println("状态:\r" + status);
90. assertTrue(status);
91.
92. }
93.
94.}
95.
96.abstract class RSACoder extends Coder {
97. public static final String KEY_ALGORITHM = "RSA";
98. public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
99.
100. private static final String PUBLIC_KEY = "RSAPublicKey";
101. private static final String PRIVATE_KEY = "RSAPrivateKey";
102.
103. /**
104. * 用私钥对信息生成数字签名
105. *
106. * @param data
107. * 加密数据
108. * @param privateKey
109. * 私钥
110. *
111. * @return
112. * @throws Exception
113. */
114. public static String sign(byte[] data, String privateKey) throws Exception {
115. // 解密由base64编码的私钥
116. byte[] keyBytes = decryptBASE64(privateKey);
117.
118. // 构造PKCS8EncodedKeySpec对象
119. PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
120.
121. // KEY_ALGORITHM 指定的加密算法
122. KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
123.
124. // 取私钥匙对象
125. PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
126.
127. // 用私钥对信息生成数字签名
128. Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
129. signature.initSign(priKey);
130. signature.update(data);
131.
132. return encryptBASE64(signature.sign());
133. }
134.
135. /**
136. * 校验数字签名
137. *
138. * @param data
139. * 加密数据
140. * @param publicKey
141. * 公钥
142. * @param sign
143. * 数字签名
144. *
145. * @return 校验成功返回true 失败返回false
146. * @throws Exception
147. *
148. */
149. public static boolean verify(byte[] data, String publicKey, String sign)
150. throws Exception {
151.
152. // 解密由base64编码的公钥
153. byte[] keyBytes = decryptBASE64(publicKey);
154.
155. // 构造X509EncodedKeySpec对象
156. X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
157.
158. // KEY_ALGORITHM 指定的加密算法
159. KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
160.
161. // 取公钥匙对象
162. PublicKey pubKey = keyFactory.generatePublic(keySpec);
163.
164. Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
165. signature.initVerify(pubKey);
166. signature.update(data);
167.
168. // 验证签名是否正常
169. return signature.verify(decryptBASE64(sign));
170. }
171.
172. /**
173. * 解密<br>
174. * 用私钥解密 http://www.5a520.cn http://www.feng123.com
175. *
176. * @param data
177. * @param key
178. * @return
179. * @throws Exception
180. */
181. public static byte[] decryptByPrivateKey(byte[] data, String key)
182. throws Exception {
183. // 对密钥解密
184. byte[] keyBytes = decryptBASE64(key);
185.
186. // 取得私钥
187. PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
188. KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
189. Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
190.
191. // 对数据解密
192. Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
193. cipher.init(Cipher.DECRYPT_MODE, privateKey);
194.
195. return cipher.doFinal(data);
196. }
197.
198. /**
199. * 解密<br>
200. * 用私钥解密
201. *
202. * @param data
203. * @param key
204. * @return
205. * @throws Exception
206. */
207. public static byte[] decryptByPublicKey(byte[] data, String key)
208. throws Exception {
209. // 对密钥解密
210. byte[] keyBytes = decryptBASE64(key);
211.
212. // 取得公钥
213. X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
214. KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
215. Key publicKey = keyFactory.generatePublic(x509KeySpec);
216.
217. // 对数据解密
218. Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
219. cipher.init(Cipher.DECRYPT_MODE, publicKey);
220.
221. return cipher.doFinal(data);
222. }
223.
224. /**
225. * 加密<br>
226. * 用公钥加密
227. *
228. * @param data
229. * @param key
230. * @return
231. * @throws Exception
232. */
233. public static byte[] encryptByPublicKey(byte[] data, String key)
234. throws Exception {
235. // 对公钥解密
236. byte[] keyBytes = decryptBASE64(key);
237.
238. // 取得公钥
239. X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
240. KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
241. Key publicKey = keyFactory.generatePublic(x509KeySpec);
242.
243. // 对数据加密
244. Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
245. cipher.init(Cipher.ENCRYPT_MODE, publicKey);
246.
247. return cipher.doFinal(data);
248. }
249.
250. /**
251. * 加密<br>
252. * 用私钥加密
253. *
254. * @param data
255. * @param key
256. * @return
257. * @throws Exception
258. */
259. public static byte[] encryptByPrivateKey(byte[] data, String key)
260. throws Exception {
261. // 对密钥解密
262. byte[] keyBytes = decryptBASE64(key);
263.
264. // 取得私钥
265. PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
266. KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
267. Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
268.
269. // 对数据加密
270. Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
271. cipher.init(Cipher.ENCRYPT_MODE, privateKey);
272.
273. return cipher.doFinal(data);
274. }
275.
276. /**
277. * 取得私钥
278. *
279. * @param keyMap
280. * @return
281. * @throws Exception
282. */
283. public static String getPrivateKey(Map<String, Object> keyMap)
284. throws Exception {
285. Key key = (Key) keyMap.get(PRIVATE_KEY);
286.
287. return encryptBASE64(key.getEncoded());
288. }
289.
290. /**
291. * 取得公钥
292. *
293. * @param keyMap
294. * @return
295. * @throws Exception
296. */
297. public static String getPublicKey(Map<String, Object> keyMap)
298. throws Exception {
299. Key key = (Key) keyMap.get(PUBLIC_KEY);
300.
301. return encryptBASE64(key.getEncoded());
302. }
303.
304. /**
305. * 初始化密钥
306. *
307. * @return
308. * @throws Exception
309. */
310. public static Map<String, Object> initKey() throws Exception {
311. KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
312. keyPairGen.initialize(1024);
313.
314. KeyPair keyPair = keyPairGen.generateKeyPair();
315.
316. // 公钥
317. RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
318.
319. // 私钥
320. RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
321.
322. Map<String, Object> keyMap = new HashMap<String, Object>(2);
323.
324. keyMap.put(PUBLIC_KEY, publicKey);
325. keyMap.put(PRIVATE_KEY, privateKey);
326. return keyMap;
327. }
328.}


示例2
【例:校验数字签名】

/**
* @(#) VerifySign.java
*/
import java.io.*;
import java.security.*;
import java.security.spec.*;
class VerifySign{
public static void main(String[] args){
if (args.length != 3){
System.out.println(
"Usage: VerifySign
");
System.out.println("Options:");
System.out.println(":The file name of the public key.");
System.out.println(": The file name that want to signature.");
System.out.println(": The file name containing signature data.");
System.out.println("Examples:");
System.out.println(" java Verifysign Publickey-my info.txt Sign-my");
System.exit(0);
}
try{
//从文件得到编码公钥字节
FileInputStream fileIn = new FileInputStream(args[0]);
//已编码的公钥字节
byte[] encodedpubkey = new byte[fileIn.available()];
fileIn.read(encodedpubkey);
fileIn.close();
//用编码公钥字节创建一个X509EncodedkeySpec对象
X509EncodedkeySpec pubkeySpec =
new X509ExcodedkeySpec(encodedpubkey);
//使用DSA算法创建一个keyFactory对象
keyFactory keyFactory = keyFactory.getInstance("DSA");
//用公钥规范生成一个公钥对象
PublicKey pubkey = keyFactory.generatePublic(pubkeySpec);
//从文件中得到数字签名
FileInputStream sigStream = new FileInputStream(args[2]);
Byte[] signature = new byte[sigStream.available()];
SigStream.read(signature);
SigStream.close();
//创建数字签名对象
Signature sigObj = Signature.getInstance("SHAlwithDSA");
//初始化Signature对象,以便用公钥检验
sigObj.initVerify(pubkey);
foleIn = new FoleInputStream(args[1]);
byte b;
while (fileIn.available() != 0){
b = (byte) fileIn.read();
sigObj.update(b);
};
fileIn.close();
Boolean verifies = sigObj.verify(signature);
System.out.puintln("sognature verifies:" + verifies);
}catch (Exception e) {System.err.puintln("" + e);
}
}
}
<think>好的,用户之前让我介绍了OAuth 2.0,现在又要求介绍SAML协议。我需要先确定用户的需求。可能用户是在学习身份验证和授权相关的协议,或者是需要为项目选择合适的安全方案。用户可能对OAuth和SAML的区别不太清楚,所以需要对比两者的不同,或者用户想深入了解SAML的具体细节。 首先,我应该回忆SAML的基本概念,核心组成部分,以及它的应用场景。SAML是基于XML的标准,主要用于单点登录(SSO),这一点和OAuth不同,OAuth主要用于授权,而SAML侧重身份认证。需要明确两者的区别,避免用户混淆。 接下来,用户可能需要了解SAML的工作流程,比如SP和IdP的角色,以及断言(Assertion)的类型。这里需要结构清晰,分步骤解释。同时,用户可能关心SAML的安全性,比如签名和加密机制,以及常见的漏洞和防护措施。 另外,用户可能想了解SAML的实际应用案例,比如企业内部的SSO系统,或者与OAuth的联合使用场景。这时候举一些例子会有帮助,比如使用SAML的公司或服务。 还需要注意用户的技术背景。如果用户是开发人员,可能需要更详细的技术细节,比如XML结构、绑定方式(HTTP POST/Redirect)。如果是管理人员,可能更关注应用场景和优势。 在回答结构上,可以按照核心概念、工作流程、安全机制、与OAuth的对比、应用案例和常见问题来组织。同时,使用之前用户接受的格式,比如分点、代码块、公式等,保持一致性。 需要检查是否有遗漏的关键点,比如SAML的版本差异(2.0是最常用的),或者与其他协议如OpenID Connect的关系。另外,可能用户会问到SAML的优缺点,比如XML的复杂性是否影响性能,或者在现代应用中的使用情况。 最后,确保语言简洁明了,避免过于技术化的术语,但必要时给出解释。保持回答的深度,但不过于冗长,帮助用户全面理解SAML协议。</think>$$SAML\ 协议框架$$ **SAML**(Security Assertion Markup Language)是一种基于**XML**的开放标准,专为实现跨域**单点登录(SSO)**和身份联合而设计。以下是深度解析: --- ### 一、核心目标与特性 1. **核心问题解决** $$ \exists \text{多系统} \Rightarrow \text{用户需重复登录} \Rightarrow \text{SAML提供:} \forall \text{系统} \Rightarrow \text{一次认证} $$ - 实现**身份提供者(IdP)**与**服务提供者(SP)**间的信任传递 2. **关键特性** ```markdown - XML数据格式(SAML 2.0为主流) - 支持HTTP POST/Redirect绑定 - 强依赖数字签名(防篡改) - 适用于企业级SSO(如Office 365登录) ``` --- ### 二、协议角色定义 1. **三大参与方** $$ \begin{cases} \text{主体 (Principal)} & \text{(用户)} \\ \text{身份提供者 (Identity Provider, IdP)} & \text{(如AD FS)} \\ \text{服务提供者 (Service Provider, SP)} & \text{(如Salesforce)} \end{cases} $$ 2. **断言(Assertion)结构** ```xml <!-- SAML断言示例 --> <saml:Assertion ID="_a75ad..." IssueInstant="2023-08-20T09:30:00Z"> <saml:Issuer>https://idp.example.com</saml:Issuer> <saml:Subject> <saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">user@example.com</saml:NameID> </saml:Subject> <saml:Conditions NotBefore="2023-08-20T09:30:00Z" NotOnOrAfter="2023-08-20T09:35:00Z"/> <saml:AuthnStatement AuthnInstant="2023-08-20T09:28:00Z"> <saml:AuthnContext> <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef> </saml:AuthnContext> </saml:AuthnStatement> </saml:Assertion> ``` - **断言类型**:认证声明、属性声明、授权决策 --- ### 三、标准交互流程(Web SSO) ```mermaid sequenceDiagram participant User as 用户 participant SP as 服务提供者 participant IdP as 身份提供者 User->>SP: 请求访问资源 SP->>User: 生成SAML请求 + 重定向到IdP User->>IdP: 提交认证(如AD凭证) IdP->>User: 生成SAML响应(含签名断言) User->>SP: 提交SAML响应 SP->>SP: 验证签名/时效性/颁发会话Cookie SP->>User: 授权访问资源 ``` --- ### 四、关键安全机制 1. **XML数字签名** - 签名流程: $$ \text{断言哈希} \rightarrow \text{私钥加密} \rightarrow \text{XML签名块} $$ ```xml <ds:Signature> <ds:SignedInfo>...</ds:SignedInfo> <ds:SignatureValue>MEUCIQD...</ds:SignatureValue> <ds:KeyInfo><ds:X509Data>...</ds:X509Data></ds:KeyInfo> </ds:Signature> ``` 2. **防御措施** ```markdown - 强制HTTPS传输 - 断言时效控制(NotBefore/NotOnOrAfter) - 防范重放攻击(ID唯一性校验) - SP严格验证IdP证书链 ``` --- ### 五、与OAuth 2.0对比 | 维度 | SAML 2.0 | OAuth 2.0 | |--------------------|-----------------------------------|-------------------------------------| | **核心目标** | 身份认证与SSO | 资源访问授权 | | **数据格式** | XML | JSON | | **传输协议** | 基于HTTP/SOAP | 基于HTTPS/RESTful | | **典型场景** | 企业内网SSO | 移动应用/API授权 | | **令牌类型** | SAML断言(长期有效) | Access Token(短期) | | **现代扩展** | SAML+WS-Federation | OpenID Connect | > **联合使用案例**: > $$ \text{企业SSO} \Rightarrow \text{SAML认证} \rightarrow \text{OAuth授权API访问} $$ > 如:先用SAML登录Azure AD,再获取OAuth令牌调用Microsoft Graph API --- ### 六、典型应用场景 1. **企业级SSO** - 微软AD FS配置SAML: ```powershell # 创建信赖方信任 Add-ADFSRelyingPartyTrust -Name "Salesforce" -MetadataUrl "https://login.salesforce.com/.xml" ``` 2. **跨域身份联合** - 高校联盟登录系统(如InCommon Federation) - 政府机构间身份互认 3. **云服务集成** - AWS IAM SAML配置: ```json { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": {"Federated": "arn:aws:iam::123456789012:saml-provider/MySAMLProvider"}, "Action": "sts:AssumeRoleWithSAML", "Condition": {"StringEquals": {"SAML:aud": "https://signin.aws.amazon.com/saml"}} }] } ``` --- ### 七、常见漏洞与缓解 1. **XML签名包装攻击** - 攻击原理: $$ \exists \text{恶意XML} \land \text{有效签名} \Rightarrow \text{SP误解析错误节点} $$ - 防护方案: ```markdown - 启用严格XML解析(禁用外部实体) - 校验断言位置与签名范围一致性 ``` 2. **断言重放攻击** - 防御措施: $$ \forall \text{断言ID} \Rightarrow \text{记录} \land \text{时效检查} $$ 3. **证书管理风险** - 最佳实践: ```markdown - IdP私钥存储HSM中 - SP定期轮换信任证书 - 禁用SHA-1算法 ``` --- ### 八、协议演进与替代方案 1. **SAML 2.0局限** ```markdown - XML处理复杂度高 - 移动端支持较弱 - 无法直接用于REST API ``` 2. **现代替代方案** - **OpenID Connect**:基于OAuth 2.0的身份层 $$ \text{OIDC} \approx \text{JSON Web Token} + \text{标准化声明} $$ - **WS-Federation**:微软主导的SOAP方案 --- SAML仍广泛用于企业级SSO场景,但新系统设计建议优先评估OIDC。实施时需重点关注**XML安全解析**和**证书生命周期管理**,推荐使用经过审计的库(如Spring Security SAML、OneLogin工具包)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值