1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.opensaml.xml.security;
18
19 import java.math.BigInteger;
20 import java.security.GeneralSecurityException;
21 import java.security.Key;
22 import java.security.KeyException;
23 import java.security.KeyFactory;
24 import java.security.NoSuchAlgorithmException;
25 import java.security.PrivateKey;
26 import java.security.PublicKey;
27 import java.security.cert.X509Certificate;
28 import java.security.interfaces.DSAParams;
29 import java.security.interfaces.DSAPrivateKey;
30 import java.security.interfaces.RSAPrivateCrtKey;
31 import java.security.spec.DSAPublicKeySpec;
32 import java.security.spec.RSAPublicKeySpec;
33 import java.util.HashSet;
34 import java.util.Set;
35
36 import javax.crypto.KeyGenerator;
37 import javax.crypto.SecretKey;
38
39 import org.apache.commons.ssl.PKCS8Key;
40 import org.apache.xml.security.Init;
41 import org.apache.xml.security.algorithms.JCEMapper;
42 import org.opensaml.xml.Configuration;
43 import org.opensaml.xml.encryption.Encrypter;
44 import org.opensaml.xml.encryption.EncryptionParameters;
45 import org.opensaml.xml.encryption.KeyEncryptionParameters;
46 import org.opensaml.xml.security.credential.BasicCredential;
47 import org.opensaml.xml.security.credential.Credential;
48 import org.opensaml.xml.security.keyinfo.KeyInfoGenerator;
49 import org.opensaml.xml.security.keyinfo.KeyInfoGeneratorFactory;
50 import org.opensaml.xml.security.keyinfo.NamedKeyInfoGeneratorManager;
51 import org.opensaml.xml.security.x509.BasicX509Credential;
52 import org.opensaml.xml.signature.KeyInfo;
53 import org.opensaml.xml.signature.Signature;
54 import org.opensaml.xml.signature.SignatureConstants;
55 import org.opensaml.xml.util.DatatypeHelper;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58
59
60
61
62 public final class SecurityHelper {
63
64
65 private static Logger log = LoggerFactory.getLogger(SecurityHelper.class);
66
67
68 private static Set<String> rsaAlgorithmURIs;
69
70
71 private static Set<String> dsaAlgorithmURIs;
72
73
74 private static Set<String> ecdsaAlgorithmURIs;
75
76
77 private SecurityHelper() {
78 }
79
80
81
82
83
84
85
86 public static String getAlgorithmIDFromURI(String algorithmURI) {
87 return DatatypeHelper.safeTrimOrNullString(JCEMapper.translateURItoJCEID(algorithmURI));
88 }
89
90
91
92
93
94
95
96 public static boolean isHMAC(String signatureAlgorithm) {
97 String algoClass = DatatypeHelper.safeTrimOrNullString(JCEMapper.getAlgorithmClassFromURI(signatureAlgorithm));
98 return ApacheXMLSecurityConstants.ALGO_CLASS_MAC.equals(algoClass);
99 }
100
101
102
103
104
105
106
107 public static String getKeyAlgorithmFromURI(String algorithmURI) {
108
109
110 String apacheValue = DatatypeHelper.safeTrimOrNullString(JCEMapper.getJCEKeyAlgorithmFromURI(algorithmURI));
111 if (apacheValue != null) {
112 return apacheValue;
113 }
114
115
116 if (isHMAC(algorithmURI)) {
117 return null;
118 }
119
120
121 if (rsaAlgorithmURIs.contains(algorithmURI)) {
122 return "RSA";
123 }
124 if (dsaAlgorithmURIs.contains(algorithmURI)) {
125 return "DSA";
126 }
127 if (ecdsaAlgorithmURIs.contains(algorithmURI)) {
128 return "ECDSA";
129 }
130
131 return null;
132 }
133
134
135
136
137
138
139
140
141 public static Integer getKeyLengthFromURI(String algorithmURI) {
142 String algoClass = DatatypeHelper.safeTrimOrNullString(JCEMapper.getAlgorithmClassFromURI(algorithmURI));
143
144 if (ApacheXMLSecurityConstants.ALGO_CLASS_BLOCK_ENCRYPTION.equals(algoClass)
145 || ApacheXMLSecurityConstants.ALGO_CLASS_SYMMETRIC_KEY_WRAP.equals(algoClass)) {
146
147 try {
148 int keyLength = JCEMapper.getKeyLengthFromURI(algorithmURI);
149 return new Integer(keyLength);
150 } catch (NumberFormatException e) {
151 log.warn("XML Security config contained invalid key length value for algorithm URI: " + algorithmURI);
152 }
153 }
154
155 log.info("Mapping from algorithm URI {} to key length not available", algorithmURI);
156 return null;
157 }
158
159
160
161
162
163
164
165
166
167 public static SecretKey generateSymmetricKey(String algoURI) throws NoSuchAlgorithmException, KeyException {
168 String jceAlgorithmName = getKeyAlgorithmFromURI(algoURI);
169 if (DatatypeHelper.isEmpty(jceAlgorithmName)) {
170 log.error("Mapping from algorithm URI '" + algoURI
171 + "' to key algorithm not available, key generation failed");
172 throw new NoSuchAlgorithmException("Algorithm URI'" + algoURI + "' is invalid for key generation");
173 }
174 Integer keyLength = getKeyLengthFromURI(algoURI);
175 if (keyLength == null) {
176 log.error("Key length could not be determined from algorithm URI, can't generate key");
177 throw new KeyException("Key length not determinable from algorithm URI, could not generate new key");
178 }
179 KeyGenerator keyGenerator = KeyGenerator.getInstance(jceAlgorithmName);
180 keyGenerator.init(keyLength);
181 return keyGenerator.generateKey();
182 }
183
184
185
186
187
188
189
190 public static Key extractEncryptionKey(Credential credential) {
191 if (credential == null) {
192 return null;
193 }
194 if (credential.getPublicKey() != null) {
195 return credential.getPublicKey();
196 } else {
197 return credential.getSecretKey();
198 }
199 }
200
201
202
203
204
205
206
207 public static Key extractDecryptionKey(Credential credential) {
208 if (credential == null) {
209 return null;
210 }
211 if (credential.getPrivateKey() != null) {
212 return credential.getPrivateKey();
213 } else {
214 return credential.getSecretKey();
215 }
216 }
217
218
219
220
221
222
223
224 public static Key extractSigningKey(Credential credential) {
225 if (credential == null) {
226 return null;
227 }
228 if (credential.getPrivateKey() != null) {
229 return credential.getPrivateKey();
230 } else {
231 return credential.getSecretKey();
232 }
233 }
234
235
236
237
238
239
240
241 public static Key extractVerificationKey(Credential credential) {
242 if (credential == null) {
243 return null;
244 }
245 if (credential.getPublicKey() != null) {
246 return credential.getPublicKey();
247 } else {
248 return credential.getSecretKey();
249 }
250 }
251
252
253
254
255
256
257
258 public static Integer getKeyLength(Key key) {
259
260
261 if (key instanceof SecretKey && "RAW".equals(key.getFormat())) {
262 return key.getEncoded().length * 8;
263 }
264 log.debug("Unable to determine length in bits of specified Key instance");
265 return null;
266 }
267
268
269
270
271
272
273
274 public static BasicCredential getSimpleCredential(SecretKey secretKey) {
275 if (secretKey == null) {
276 throw new IllegalArgumentException("A secret key is required");
277 }
278 BasicCredential cred = new BasicCredential();
279 cred.setSecretKey(secretKey);
280 return cred;
281 }
282
283
284
285
286
287
288
289
290 public static BasicCredential getSimpleCredential(PublicKey publicKey, PrivateKey privateKey) {
291 if (publicKey == null) {
292 throw new IllegalArgumentException("A public key is required");
293 }
294 BasicCredential cred = new BasicCredential();
295 cred.setPublicKey(publicKey);
296 cred.setPrivateKey(privateKey);
297 return cred;
298 }
299
300
301
302
303
304
305
306
307 public static BasicX509Credential getSimpleCredential(X509Certificate cert, PrivateKey privateKey) {
308 if (cert == null) {
309 throw new IllegalArgumentException("A certificate is required");
310 }
311 BasicX509Credential cred = new BasicX509Credential();
312 cred.setEntityCertificate(cert);
313 cred.setPrivateKey(privateKey);
314 return cred;
315 }
316
317
318
319
320
321
322
323
324
325
326
327
328
329 public static SecretKey decodeSecretKey(byte[] key, char[] password) throws KeyException {
330
331 throw new UnsupportedOperationException("This method is not yet supported");
332 }
333
334
335
336
337
338
339
340
341
342
343
344 public static PublicKey decodePublicKey(byte[] key, char[] password) throws KeyException {
345
346 throw new UnsupportedOperationException("This method is not yet supported");
347 }
348
349
350
351
352
353
354
355
356
357
358
359 public static PublicKey derivePublicKey(PrivateKey key) throws KeyException {
360 KeyFactory factory;
361 if (key instanceof DSAPrivateKey) {
362 DSAPrivateKey dsaKey = (DSAPrivateKey) key;
363 DSAParams keyParams = dsaKey.getParams();
364 BigInteger y = keyParams.getQ().modPow(dsaKey.getX(), keyParams.getP());
365 DSAPublicKeySpec pubKeySpec = new DSAPublicKeySpec(y, keyParams.getP(), keyParams.getQ(), keyParams.getG());
366
367 try {
368 factory = KeyFactory.getInstance("DSA");
369 return factory.generatePublic(pubKeySpec);
370 } catch (GeneralSecurityException e) {
371 throw new KeyException("Unable to derive public key from DSA private key", e);
372 }
373 } else if (key instanceof RSAPrivateCrtKey) {
374 RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey) key;
375 RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(rsaKey.getModulus(), rsaKey.getPublicExponent());
376
377 try {
378 factory = KeyFactory.getInstance("RSA");
379 return factory.generatePublic(pubKeySpec);
380 } catch (GeneralSecurityException e) {
381 throw new KeyException("Unable to derive public key from RSA private key", e);
382 }
383 } else {
384 throw new KeyException("Private key was not a DSA or RSA key");
385 }
386 }
387
388
389
390
391
392
393
394
395
396
397
398 public static PrivateKey decodePrivateKey(byte[] key, char[] password) throws KeyException {
399 try {
400 PKCS8Key deocodedKey = new PKCS8Key(key, password);
401 return deocodedKey.getPrivateKey();
402 } catch (GeneralSecurityException e) {
403 throw new KeyException("Unable to decode private key", e);
404 }
405 }
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451 public static void prepareSignatureParams(Signature signature, Credential signingCredential,
452 SecurityConfiguration config, String keyInfoGenName) throws SecurityException {
453
454 SecurityConfiguration secConfig;
455 if (config != null) {
456 secConfig = config;
457 } else {
458 secConfig = Configuration.getGlobalSecurityConfiguration();
459 }
460
461
462 String signAlgo = signature.getSignatureAlgorithm();
463 if (signAlgo == null) {
464 signAlgo = secConfig.getSignatureAlgorithmURI(signingCredential);
465 signature.setSignatureAlgorithm(signAlgo);
466 }
467
468
469 if (SecurityHelper.isHMAC(signAlgo)) {
470 if (signature.getHMACOutputLength() == null) {
471 signature.setHMACOutputLength(secConfig.getSignatureHMACOutputLength());
472 }
473 }
474
475 if (signature.getCanonicalizationAlgorithm() == null) {
476 signature.setCanonicalizationAlgorithm(secConfig.getSignatureCanonicalizationAlgorithm());
477 }
478
479 if (signature.getKeyInfo() == null) {
480 KeyInfoGenerator kiGenerator = getKeyInfoGenerator(signingCredential, secConfig, keyInfoGenName);
481 if (kiGenerator != null) {
482 try {
483 KeyInfo keyInfo = kiGenerator.generate(signingCredential);
484 signature.setKeyInfo(keyInfo);
485 } catch (SecurityException e) {
486 log.error("Error generating KeyInfo from credential", e);
487 throw e;
488 }
489 } else {
490 log.info("No factory for named KeyInfoGenerator {} was found for credential type {}", keyInfoGenName,
491 signingCredential.getCredentialType().getName());
492 log.info("No KeyInfo will be generated for Signature");
493 }
494 }
495 }
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533 public static EncryptionParameters buildDataEncryptionParams(Credential encryptionCredential,
534 SecurityConfiguration config, String keyInfoGenName) {
535
536 SecurityConfiguration secConfig;
537 if (config != null) {
538 secConfig = config;
539 } else {
540 secConfig = Configuration.getGlobalSecurityConfiguration();
541 }
542
543 EncryptionParameters encParams = new EncryptionParameters();
544 encParams.setEncryptionCredential(encryptionCredential);
545
546 if (encryptionCredential == null) {
547 encParams.setAlgorithm(secConfig.getAutoGeneratedDataEncryptionKeyAlgorithmURI());
548 } else {
549 encParams.setAlgorithm(secConfig.getDataEncryptionAlgorithmURI(encryptionCredential));
550
551 KeyInfoGenerator kiGenerator = getKeyInfoGenerator(encryptionCredential, secConfig, keyInfoGenName);
552 if (kiGenerator != null) {
553 encParams.setKeyInfoGenerator(kiGenerator);
554 } else {
555 log.info("No factory for named KeyInfoGenerator {} was found for credential type{}", keyInfoGenName,
556 encryptionCredential.getCredentialType().getName());
557 log.info("No KeyInfo will be generated for EncryptedData");
558 }
559 }
560
561 return encParams;
562 }
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606 public static KeyEncryptionParameters buildKeyEncryptionParams(Credential encryptionCredential,
607 String wrappedKeyAlgorithm, SecurityConfiguration config, String keyInfoGenName, String recipient)
608 throws SecurityException {
609
610 SecurityConfiguration secConfig;
611 if (config != null) {
612 secConfig = config;
613 } else {
614 secConfig = Configuration.getGlobalSecurityConfiguration();
615 }
616
617 KeyEncryptionParameters kekParams = new KeyEncryptionParameters();
618 kekParams.setEncryptionCredential(encryptionCredential);
619
620 if (encryptionCredential == null) {
621 throw new SecurityException("Key encryption credential may not be null");
622 }
623
624 kekParams.setAlgorithm(secConfig.getKeyTransportEncryptionAlgorithmURI(encryptionCredential,
625 wrappedKeyAlgorithm));
626
627 KeyInfoGenerator kiGenerator = getKeyInfoGenerator(encryptionCredential, secConfig, keyInfoGenName);
628 if (kiGenerator != null) {
629 kekParams.setKeyInfoGenerator(kiGenerator);
630 } else {
631 log.info("No factory for named KeyInfoGenerator {} was found for credential type {}", keyInfoGenName,
632 encryptionCredential.getCredentialType().getName());
633 log.info("No KeyInfo will be generated for EncryptedKey");
634 }
635
636 kekParams.setRecipient(recipient);
637
638 return kekParams;
639 }
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663 public static KeyInfoGenerator getKeyInfoGenerator(Credential credential, SecurityConfiguration config,
664 String keyInfoGenName) {
665
666 SecurityConfiguration secConfig;
667 if (config != null) {
668 secConfig = config;
669 } else {
670 secConfig = Configuration.getGlobalSecurityConfiguration();
671 }
672
673 NamedKeyInfoGeneratorManager kiMgr = secConfig.getKeyInfoGeneratorManager();
674 if (kiMgr != null) {
675 KeyInfoGeneratorFactory kiFactory = null;
676 if (DatatypeHelper.isEmpty(keyInfoGenName)) {
677 kiFactory = kiMgr.getDefaultManager().getFactory(credential);
678 } else {
679 kiFactory = kiMgr.getFactory(keyInfoGenName, credential);
680 }
681 if (kiFactory != null) {
682 return kiFactory.newInstance();
683 }
684 }
685 return null;
686 }
687
688 static {
689
690
691 if (!Init.isInitialized()) {
692 Init.init();
693 }
694
695
696
697 dsaAlgorithmURIs = new HashSet<String>();
698 dsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_DSA);
699
700 ecdsaAlgorithmURIs = new HashSet<String>();
701 ecdsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_ECDSA_SHA1);
702
703 rsaAlgorithmURIs = new HashSet<String>();
704 rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1);
705 rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
706 rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA384);
707 rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512);
708 rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512);
709 rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_RIPEMD160);
710 rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_NOT_RECOMMENDED_RSA_MD5);
711 }
712 }