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.io.File;
20 import java.io.IOException;
21 import java.math.BigInteger;
22 import java.security.GeneralSecurityException;
23 import java.security.Key;
24 import java.security.KeyException;
25 import java.security.KeyFactory;
26 import java.security.NoSuchAlgorithmException;
27 import java.security.PrivateKey;
28 import java.security.PublicKey;
29 import java.security.cert.X509Certificate;
30 import java.security.interfaces.DSAParams;
31 import java.security.interfaces.DSAPrivateKey;
32 import java.security.interfaces.RSAPrivateCrtKey;
33 import java.security.spec.DSAPublicKeySpec;
34 import java.security.spec.RSAPublicKeySpec;
35 import java.util.HashSet;
36 import java.util.Set;
37
38 import javax.crypto.KeyGenerator;
39 import javax.crypto.SecretKey;
40
41 import org.apache.commons.ssl.PKCS8Key;
42 import org.apache.xml.security.Init;
43 import org.apache.xml.security.algorithms.JCEMapper;
44 import org.opensaml.xml.Configuration;
45 import org.opensaml.xml.encryption.EncryptionParameters;
46 import org.opensaml.xml.encryption.KeyEncryptionParameters;
47 import org.opensaml.xml.security.credential.BasicCredential;
48 import org.opensaml.xml.security.credential.Credential;
49 import org.opensaml.xml.security.keyinfo.KeyInfoGenerator;
50 import org.opensaml.xml.security.keyinfo.KeyInfoGeneratorFactory;
51 import org.opensaml.xml.security.keyinfo.NamedKeyInfoGeneratorManager;
52 import org.opensaml.xml.security.x509.BasicX509Credential;
53 import org.opensaml.xml.signature.KeyInfo;
54 import org.opensaml.xml.signature.Signature;
55 import org.opensaml.xml.signature.SignatureConstants;
56 import org.opensaml.xml.util.DatatypeHelper;
57 import org.opensaml.xml.util.LazySet;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
60
61
62
63
64 public final class SecurityHelper {
65
66
67 private static Logger log = LoggerFactory.getLogger(SecurityHelper.class);
68
69
70 private static Set<String> rsaAlgorithmURIs;
71
72
73 private static Set<String> dsaAlgorithmURIs;
74
75
76 private static Set<String> ecdsaAlgorithmURIs;
77
78
79 private SecurityHelper() {
80 }
81
82
83
84
85
86
87
88 public static String getAlgorithmIDFromURI(String algorithmURI) {
89 return DatatypeHelper.safeTrimOrNullString(JCEMapper.translateURItoJCEID(algorithmURI));
90 }
91
92
93
94
95
96
97
98 public static boolean isHMAC(String signatureAlgorithm) {
99 String algoClass = DatatypeHelper.safeTrimOrNullString(JCEMapper.getAlgorithmClassFromURI(signatureAlgorithm));
100 return ApacheXMLSecurityConstants.ALGO_CLASS_MAC.equals(algoClass);
101 }
102
103
104
105
106
107
108
109 public static String getKeyAlgorithmFromURI(String algorithmURI) {
110
111
112 String apacheValue = DatatypeHelper.safeTrimOrNullString(JCEMapper.getJCEKeyAlgorithmFromURI(algorithmURI));
113 if (apacheValue != null) {
114 return apacheValue;
115 }
116
117
118 if (isHMAC(algorithmURI)) {
119 return null;
120 }
121
122
123 if (rsaAlgorithmURIs.contains(algorithmURI)) {
124 return "RSA";
125 }
126 if (dsaAlgorithmURIs.contains(algorithmURI)) {
127 return "DSA";
128 }
129 if (ecdsaAlgorithmURIs.contains(algorithmURI)) {
130 return "ECDSA";
131 }
132
133 return null;
134 }
135
136
137
138
139
140
141
142
143 public static Integer getKeyLengthFromURI(String algorithmURI) {
144 String algoClass = DatatypeHelper.safeTrimOrNullString(JCEMapper.getAlgorithmClassFromURI(algorithmURI));
145
146 if (ApacheXMLSecurityConstants.ALGO_CLASS_BLOCK_ENCRYPTION.equals(algoClass)
147 || ApacheXMLSecurityConstants.ALGO_CLASS_SYMMETRIC_KEY_WRAP.equals(algoClass)) {
148
149 try {
150 int keyLength = JCEMapper.getKeyLengthFromURI(algorithmURI);
151 return new Integer(keyLength);
152 } catch (NumberFormatException e) {
153 log.warn("XML Security config contained invalid key length value for algorithm URI: " + algorithmURI);
154 }
155 }
156
157 log.info("Mapping from algorithm URI {} to key length not available", algorithmURI);
158 return null;
159 }
160
161
162
163
164
165
166
167
168
169 public static SecretKey generateSymmetricKey(String algoURI) throws NoSuchAlgorithmException, KeyException {
170 String jceAlgorithmName = getKeyAlgorithmFromURI(algoURI);
171 if (DatatypeHelper.isEmpty(jceAlgorithmName)) {
172 log.error("Mapping from algorithm URI '" + algoURI
173 + "' to key algorithm not available, key generation failed");
174 throw new NoSuchAlgorithmException("Algorithm URI'" + algoURI + "' is invalid for key generation");
175 }
176 Integer keyLength = getKeyLengthFromURI(algoURI);
177 if (keyLength == null) {
178 log.error("Key length could not be determined from algorithm URI, can't generate key");
179 throw new KeyException("Key length not determinable from algorithm URI, could not generate new key");
180 }
181 KeyGenerator keyGenerator = KeyGenerator.getInstance(jceAlgorithmName);
182 keyGenerator.init(keyLength);
183 return keyGenerator.generateKey();
184 }
185
186
187
188
189
190
191
192 public static Key extractEncryptionKey(Credential credential) {
193 if (credential == null) {
194 return null;
195 }
196 if (credential.getPublicKey() != null) {
197 return credential.getPublicKey();
198 } else {
199 return credential.getSecretKey();
200 }
201 }
202
203
204
205
206
207
208
209 public static Key extractDecryptionKey(Credential credential) {
210 if (credential == null) {
211 return null;
212 }
213 if (credential.getPrivateKey() != null) {
214 return credential.getPrivateKey();
215 } else {
216 return credential.getSecretKey();
217 }
218 }
219
220
221
222
223
224
225
226 public static Key extractSigningKey(Credential credential) {
227 if (credential == null) {
228 return null;
229 }
230 if (credential.getPrivateKey() != null) {
231 return credential.getPrivateKey();
232 } else {
233 return credential.getSecretKey();
234 }
235 }
236
237
238
239
240
241
242
243 public static Key extractVerificationKey(Credential credential) {
244 if (credential == null) {
245 return null;
246 }
247 if (credential.getPublicKey() != null) {
248 return credential.getPublicKey();
249 } else {
250 return credential.getSecretKey();
251 }
252 }
253
254
255
256
257
258
259
260 public static Integer getKeyLength(Key key) {
261
262
263 if (key instanceof SecretKey && "RAW".equals(key.getFormat())) {
264 return key.getEncoded().length * 8;
265 }
266 log.debug("Unable to determine length in bits of specified Key instance");
267 return null;
268 }
269
270
271
272
273
274
275
276 public static BasicCredential getSimpleCredential(SecretKey secretKey) {
277 if (secretKey == null) {
278 throw new IllegalArgumentException("A secret key is required");
279 }
280 BasicCredential cred = new BasicCredential();
281 cred.setSecretKey(secretKey);
282 return cred;
283 }
284
285
286
287
288
289
290
291
292 public static BasicCredential getSimpleCredential(PublicKey publicKey, PrivateKey privateKey) {
293 if (publicKey == null) {
294 throw new IllegalArgumentException("A public key is required");
295 }
296 BasicCredential cred = new BasicCredential();
297 cred.setPublicKey(publicKey);
298 cred.setPrivateKey(privateKey);
299 return cred;
300 }
301
302
303
304
305
306
307
308
309 public static BasicX509Credential getSimpleCredential(X509Certificate cert, PrivateKey privateKey) {
310 if (cert == null) {
311 throw new IllegalArgumentException("A certificate is required");
312 }
313 BasicX509Credential cred = new BasicX509Credential();
314 cred.setEntityCertificate(cert);
315 cred.setPrivateKey(privateKey);
316 return cred;
317 }
318
319
320
321
322
323
324
325
326
327
328
329
330
331 public static SecretKey decodeSecretKey(byte[] key, char[] password) throws KeyException {
332
333 throw new UnsupportedOperationException("This method is not yet supported");
334 }
335
336
337
338
339
340
341
342
343
344
345
346 public static PublicKey decodePublicKey(byte[] key, char[] password) throws KeyException {
347
348 throw new UnsupportedOperationException("This method is not yet supported");
349 }
350
351
352
353
354
355
356
357
358
359
360
361 public static PublicKey derivePublicKey(PrivateKey key) throws KeyException {
362 KeyFactory factory;
363 if (key instanceof DSAPrivateKey) {
364 DSAPrivateKey dsaKey = (DSAPrivateKey) key;
365 DSAParams keyParams = dsaKey.getParams();
366 BigInteger y = keyParams.getQ().modPow(dsaKey.getX(), keyParams.getP());
367 DSAPublicKeySpec pubKeySpec = new DSAPublicKeySpec(y, keyParams.getP(), keyParams.getQ(), keyParams.getG());
368
369 try {
370 factory = KeyFactory.getInstance("DSA");
371 return factory.generatePublic(pubKeySpec);
372 } catch (GeneralSecurityException e) {
373 throw new KeyException("Unable to derive public key from DSA private key", e);
374 }
375 } else if (key instanceof RSAPrivateCrtKey) {
376 RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey) key;
377 RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(rsaKey.getModulus(), rsaKey.getPublicExponent());
378
379 try {
380 factory = KeyFactory.getInstance("RSA");
381 return factory.generatePublic(pubKeySpec);
382 } catch (GeneralSecurityException e) {
383 throw new KeyException("Unable to derive public key from RSA private key", e);
384 }
385 } else {
386 throw new KeyException("Private key was not a DSA or RSA key");
387 }
388 }
389
390
391
392
393
394
395
396
397
398
399
400 public static PrivateKey decodePrivateKey(File key, char[] password) throws KeyException {
401 if (!key.exists()) {
402 throw new KeyException("Key file " + key.getAbsolutePath() + " does not exist");
403 }
404
405 if (!key.canRead()) {
406 throw new KeyException("Key file " + key.getAbsolutePath() + " is not readable");
407 }
408
409 try {
410 return decodePrivateKey(DatatypeHelper.fileToByteArray(key), password);
411 } catch (IOException e) {
412 throw new KeyException("Error reading Key file " + key.getAbsolutePath(), e);
413 }
414 }
415
416
417
418
419
420
421
422
423
424
425
426 public static PrivateKey decodePrivateKey(byte[] key, char[] password) throws KeyException {
427 try {
428 PKCS8Key deocodedKey = new PKCS8Key(key, password);
429 return deocodedKey.getPrivateKey();
430 } catch (GeneralSecurityException e) {
431 throw new KeyException("Unable to decode private key", e);
432 }
433 }
434
435
436
437
438
439
440
441
442
443 public static boolean matchKeyPair(PublicKey pubKey, PrivateKey privKey) throws SecurityException {
444
445
446 if (pubKey == null || privKey == null) {
447 throw new SecurityException("Either public or private key was null");
448 }
449
450
451
452
453 SecurityConfiguration secConfig = Configuration.getGlobalSecurityConfiguration();
454 if (secConfig == null) {
455 throw new SecurityException("Global security configuration was null, could not resolve signing algorithm");
456 }
457 String algoURI = secConfig.getSignatureAlgorithmURI(privKey.getAlgorithm());
458 if (algoURI == null) {
459 throw new SecurityException("Can't determine algorithm URI from key algorithm: " + privKey.getAlgorithm());
460 }
461 String jcaAlgoID = getAlgorithmIDFromURI(algoURI);
462 if (jcaAlgoID == null) {
463 throw new SecurityException("Can't determine JCA algorithm ID from algorithm URI: " + algoURI);
464 }
465
466 if (log.isDebugEnabled()) {
467 log.debug("Attempting to match key pair containing key algorithms public '{}' private '{}', "
468 + "using JCA signature algorithm '{}'", new Object[] { pubKey.getAlgorithm(),
469 privKey.getAlgorithm(), jcaAlgoID });
470 }
471
472 byte[] data = "This is the data to sign".getBytes();
473 byte[] signature = SigningUtil.sign(privKey, jcaAlgoID, data);
474 return SigningUtil.verify(pubKey, jcaAlgoID, signature, data);
475 }
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
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 public static void prepareSignatureParams(Signature signature, Credential signingCredential,
524 SecurityConfiguration config, String keyInfoGenName) throws SecurityException {
525
526 SecurityConfiguration secConfig;
527 if (config != null) {
528 secConfig = config;
529 } else {
530 secConfig = Configuration.getGlobalSecurityConfiguration();
531 }
532
533
534 String signAlgo = signature.getSignatureAlgorithm();
535 if (signAlgo == null) {
536 signAlgo = secConfig.getSignatureAlgorithmURI(signingCredential);
537 signature.setSignatureAlgorithm(signAlgo);
538 }
539
540
541 if (SecurityHelper.isHMAC(signAlgo)) {
542 if (signature.getHMACOutputLength() == null) {
543 signature.setHMACOutputLength(secConfig.getSignatureHMACOutputLength());
544 }
545 }
546
547 if (signature.getCanonicalizationAlgorithm() == null) {
548 signature.setCanonicalizationAlgorithm(secConfig.getSignatureCanonicalizationAlgorithm());
549 }
550
551 if (signature.getKeyInfo() == null) {
552 KeyInfoGenerator kiGenerator = getKeyInfoGenerator(signingCredential, secConfig, keyInfoGenName);
553 if (kiGenerator != null) {
554 try {
555 KeyInfo keyInfo = kiGenerator.generate(signingCredential);
556 signature.setKeyInfo(keyInfo);
557 } catch (SecurityException e) {
558 log.error("Error generating KeyInfo from credential", e);
559 throw e;
560 }
561 } else {
562 log.info("No factory for named KeyInfoGenerator {} was found for credential type {}", keyInfoGenName,
563 signingCredential.getCredentialType().getName());
564 log.info("No KeyInfo will be generated for Signature");
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 EncryptionParameters buildDataEncryptionParams(Credential encryptionCredential,
607 SecurityConfiguration config, String keyInfoGenName) {
608
609 SecurityConfiguration secConfig;
610 if (config != null) {
611 secConfig = config;
612 } else {
613 secConfig = Configuration.getGlobalSecurityConfiguration();
614 }
615
616 EncryptionParameters encParams = new EncryptionParameters();
617 encParams.setEncryptionCredential(encryptionCredential);
618
619 if (encryptionCredential == null) {
620 encParams.setAlgorithm(secConfig.getAutoGeneratedDataEncryptionKeyAlgorithmURI());
621 } else {
622 encParams.setAlgorithm(secConfig.getDataEncryptionAlgorithmURI(encryptionCredential));
623
624 KeyInfoGenerator kiGenerator = getKeyInfoGenerator(encryptionCredential, secConfig, keyInfoGenName);
625 if (kiGenerator != null) {
626 encParams.setKeyInfoGenerator(kiGenerator);
627 } else {
628 log.info("No factory for named KeyInfoGenerator {} was found for credential type{}", keyInfoGenName,
629 encryptionCredential.getCredentialType().getName());
630 log.info("No KeyInfo will be generated for EncryptedData");
631 }
632 }
633
634 return encParams;
635 }
636
637
638
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
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680 public static KeyEncryptionParameters buildKeyEncryptionParams(Credential encryptionCredential,
681 String wrappedKeyAlgorithm, SecurityConfiguration config, String keyInfoGenName, String recipient)
682 throws SecurityException {
683
684 SecurityConfiguration secConfig;
685 if (config != null) {
686 secConfig = config;
687 } else {
688 secConfig = Configuration.getGlobalSecurityConfiguration();
689 }
690
691 KeyEncryptionParameters kekParams = new KeyEncryptionParameters();
692 kekParams.setEncryptionCredential(encryptionCredential);
693
694 if (encryptionCredential == null) {
695 throw new SecurityException("Key encryption credential may not be null");
696 }
697
698 kekParams.setAlgorithm(secConfig.getKeyTransportEncryptionAlgorithmURI(encryptionCredential,
699 wrappedKeyAlgorithm));
700
701 KeyInfoGenerator kiGenerator = getKeyInfoGenerator(encryptionCredential, secConfig, keyInfoGenName);
702 if (kiGenerator != null) {
703 kekParams.setKeyInfoGenerator(kiGenerator);
704 } else {
705 log.info("No factory for named KeyInfoGenerator {} was found for credential type {}", keyInfoGenName,
706 encryptionCredential.getCredentialType().getName());
707 log.info("No KeyInfo will be generated for EncryptedKey");
708 }
709
710 kekParams.setRecipient(recipient);
711
712 return kekParams;
713 }
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737 public static KeyInfoGenerator getKeyInfoGenerator(Credential credential, SecurityConfiguration config,
738 String keyInfoGenName) {
739
740 SecurityConfiguration secConfig;
741 if (config != null) {
742 secConfig = config;
743 } else {
744 secConfig = Configuration.getGlobalSecurityConfiguration();
745 }
746
747 NamedKeyInfoGeneratorManager kiMgr = secConfig.getKeyInfoGeneratorManager();
748 if (kiMgr != null) {
749 KeyInfoGeneratorFactory kiFactory = null;
750 if (DatatypeHelper.isEmpty(keyInfoGenName)) {
751 kiFactory = kiMgr.getDefaultManager().getFactory(credential);
752 } else {
753 kiFactory = kiMgr.getFactory(keyInfoGenName, credential);
754 }
755 if (kiFactory != null) {
756 return kiFactory.newInstance();
757 }
758 }
759 return null;
760 }
761
762 static {
763
764
765 if (!Init.isInitialized()) {
766 Init.init();
767 }
768
769
770
771 dsaAlgorithmURIs = new LazySet<String>();
772 dsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_DSA);
773
774 ecdsaAlgorithmURIs = new LazySet<String>();
775 ecdsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_ECDSA_SHA1);
776
777 rsaAlgorithmURIs = new HashSet<String>(10);
778 rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1);
779 rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
780 rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA384);
781 rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512);
782 rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512);
783 rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_RIPEMD160);
784 rsaAlgorithmURIs.add(SignatureConstants.ALGO_ID_SIGNATURE_NOT_RECOMMENDED_RSA_MD5);
785 }
786 }