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 public static boolean matchKeyPair(PublicKey pubKey, PrivateKey privKey) throws SecurityException {
418
419
420 if (pubKey == null || privKey == null) {
421 throw new SecurityException("Either public or private key was null");
422 }
423
424
425
426
427 SecurityConfiguration secConfig = Configuration.getGlobalSecurityConfiguration();
428 if (secConfig == null) {
429 throw new SecurityException("Global security configuration was null, could not resolve signing algorithm");
430 }
431 String algoURI = secConfig.getSignatureAlgorithmURI(privKey.getAlgorithm());
432 if (algoURI == null) {
433 throw new SecurityException("Can't determine algorithm URI from key algorithm: " + privKey.getAlgorithm());
434 }
435 String jcaAlgoID = getAlgorithmIDFromURI(algoURI);
436 if (jcaAlgoID == null) {
437 throw new SecurityException("Can't determine JCA algorithm ID from algorithm URI: " + algoURI);
438 }
439
440 if (log.isDebugEnabled()) {
441 log.debug("Attempting to match key pair containing key algorithms public '{}' private '{}', "
442 + "using JCA signature algorithm '{}'",
443 new Object[] {pubKey.getAlgorithm(), privKey.getAlgorithm(), jcaAlgoID});
444 }
445
446 byte[] data = "This is the data to sign".getBytes();
447 byte[] signature = SigningUtil.sign(privKey, jcaAlgoID, data);
448 return SigningUtil.verify(pubKey, jcaAlgoID, signature, data);
449 }
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495 public static void prepareSignatureParams(Signature signature, Credential signingCredential,
496 SecurityConfiguration config, String keyInfoGenName) throws SecurityException {
497
498 SecurityConfiguration secConfig;
499 if (config != null) {
500 secConfig = config;
501 } else {
502 secConfig = Configuration.getGlobalSecurityConfiguration();
503 }
504
505
506 String signAlgo = signature.getSignatureAlgorithm();
507 if (signAlgo == null) {
508 signAlgo = secConfig.getSignatureAlgorithmURI(signingCredential);
509 signature.setSignatureAlgorithm(signAlgo);
510 }
511
512
513 if (SecurityHelper.isHMAC(signAlgo)) {
514 if (signature.getHMACOutputLength() == null) {
515 signature.setHMACOutputLength(secConfig.getSignatureHMACOutputLength());
516 }
517 }
518
519 if (signature.getCanonicalizationAlgorithm() == null) {
520 signature.setCanonicalizationAlgorithm(secConfig.getSignatureCanonicalizationAlgorithm());
521 }
522
523 if (signature.getKeyInfo() == null) {
524 KeyInfoGenerator kiGenerator = getKeyInfoGenerator(signingCredential, secConfig, keyInfoGenName);
525 if (kiGenerator != null) {
526 try {
527 KeyInfo keyInfo = kiGenerator.generate(signingCredential);
528 signature.setKeyInfo(keyInfo);
529 } catch (SecurityException e) {
530 log.error("Error generating KeyInfo from credential", e);
531 throw e;
532 }
533 } else {
534 log.info("No factory for named KeyInfoGenerator {} was found for credential type {}", keyInfoGenName,
535 signingCredential.getCredentialType().getName());
536 log.info("No KeyInfo will be generated for Signature");
537 }
538 }
539 }
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577 public static EncryptionParameters buildDataEncryptionParams(Credential encryptionCredential,
578 SecurityConfiguration config, String keyInfoGenName) {
579
580 SecurityConfiguration secConfig;
581 if (config != null) {
582 secConfig = config;
583 } else {
584 secConfig = Configuration.getGlobalSecurityConfiguration();
585 }
586
587 EncryptionParameters encParams = new EncryptionParameters();
588 encParams.setEncryptionCredential(encryptionCredential);
589
590 if (encryptionCredential == null) {
591 encParams.setAlgorithm(secConfig.getAutoGeneratedDataEncryptionKeyAlgorithmURI());
592 } else {
593 encParams.setAlgorithm(secConfig.getDataEncryptionAlgorithmURI(encryptionCredential));
594
595 KeyInfoGenerator kiGenerator = getKeyInfoGenerator(encryptionCredential, secConfig, keyInfoGenName);
596 if (kiGenerator != null) {
597 encParams.setKeyInfoGenerator(kiGenerator);
598 } else {
599 log.info("No factory for named KeyInfoGenerator {} was found for credential type{}", keyInfoGenName,
600 encryptionCredential.getCredentialType().getName());
601 log.info("No KeyInfo will be generated for EncryptedData");
602 }
603 }
604
605 return encParams;
606 }
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650 public static KeyEncryptionParameters buildKeyEncryptionParams(Credential encryptionCredential,
651 String wrappedKeyAlgorithm, SecurityConfiguration config, String keyInfoGenName, String recipient)
652 throws SecurityException {
653
654 SecurityConfiguration secConfig;
655 if (config != null) {
656 secConfig = config;
657 } else {
658 secConfig = Configuration.getGlobalSecurityConfiguration();
659 }
660
661 KeyEncryptionParameters kekParams = new KeyEncryptionParameters();
662 kekParams.setEncryptionCredential(encryptionCredential);
663
664 if (encryptionCredential == null) {
665 throw new SecurityException("Key encryption credential may not be null");
666 }
667
668 kekParams.setAlgorithm(secConfig.getKeyTransportEncryptionAlgorithmURI(encryptionCredential,
669 wrappedKeyAlgorithm));
670
671 KeyInfoGenerator kiGenerator = getKeyInfoGenerator(encryptionCredential, secConfig, keyInfoGenName);
672 if (kiGenerator != null) {
673 kekParams.setKeyInfoGenerator(kiGenerator);
674 } else {
675 log.info("No factory for named KeyInfoGenerator {} was found for credential type {}", keyInfoGenName,
676 encryptionCredential.getCredentialType().getName());
677 log.info("No KeyInfo will be generated for EncryptedKey");
678 }
679
680 kekParams.setRecipient(recipient);
681
682 return kekParams;
683 }
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707 public static KeyInfoGenerator getKeyInfoGenerator(Credential credential, SecurityConfiguration config,
708 String keyInfoGenName) {
709
710 SecurityConfiguration secConfig;
711 if (config != null) {
712 secConfig = config;
713 } else {
714 secConfig = Configuration.getGlobalSecurityConfiguration();
715 }
716
717 NamedKeyInfoGeneratorManager kiMgr = secConfig.getKeyInfoGeneratorManager();
718 if (kiMgr != null) {
719 KeyInfoGeneratorFactory kiFactory = null;
720 if (DatatypeHelper.isEmpty(keyInfoGenName)) {
721 kiFactory = kiMgr.getDefaultManager().getFactory(credential);
722 } else {
723 kiFactory = kiMgr.getFactory(keyInfoGenName, credential);
724 }
725 if (kiFactory != null) {
726 return kiFactory.newInstance();
727 }
728 }
729 return null;
730 }
731
732 static {
733
734
735 if (!Init.isInitialized()) {
736 Init.init();
737 }
738
739
740
741 dsaAlgorithmURIs = new HashSet<String>();
742 dsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_DSA);
743
744 ecdsaAlgorithmURIs = new HashSet<String>();
745 ecdsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_ECDSA_SHA1);
746
747 rsaAlgorithmURIs = new HashSet<String>();
748 rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1);
749 rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
750 rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA384);
751 rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512);
752 rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512);
753 rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_RIPEMD160);
754 rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_NOT_RECOMMENDED_RSA_MD5);
755 }
756 }