001/* 002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 003 * 004 * Copyright (c) 2007 Sun Microsystems Inc. All Rights Reserved 005 * 006 * The contents of this file are subject to the terms 007 * of the Common Development and Distribution License 008 * (the License). You may not use this file except in 009 * compliance with the License. 010 * 011 * You can obtain a copy of the License at 012 * https://opensso.dev.java.net/public/CDDLv1.0.html or 013 * opensso/legal/CDDLv1.0.txt 014 * See the License for the specific language governing 015 * permission and limitations under the License. 016 * 017 * When distributing Covered Code, include this CDDL 018 * Header Notice in each file and include the License file 019 * at opensso/legal/CDDLv1.0.txt. 020 * If applicable, add the following below the CDDL Header, 021 * with the fields enclosed by brackets [] replaced by 022 * your own identifying information: 023 * "Portions Copyrighted [year] [name of copyright owner]" 024 * 025 * $Id: SecureAttrs.java,v 1.12 2009/03/31 17:18:10 exu Exp $ 026 * 027 * Portions Copyrighted 2016 ForgeRock AS. 028 */ 029 030package com.sun.identity.sae.api; 031 032 033import static org.forgerock.openam.utils.Time.*; 034 035import java.util.*; 036import java.io.*; 037import java.io.UnsupportedEncodingException; 038import java.security.MessageDigest; 039import java.security.NoSuchAlgorithmException; 040import sun.misc.CharacterEncoder; 041import com.sun.identity.shared.encode.Base64; 042import com.sun.identity.security.DataEncryptor; 043import java.security.*; 044import java.security.cert.X509Certificate; 045 046/** 047 * <code>SecureAttrs</code> class forms the core api of "Secure Attributes 048 * Exchange" (SAE) feature. The class uses off the shelf digital 049 * signing and encryption algorithms to generate tamperproof/nonrepudiable 050 * strings representing attribute maps and to verify these strings. 051 * Typical SAE usage is to securely send attributes (authentication & 052 * use profile data) from an asserting application (eg running on an IDP) to 053 * a relying application (eg running on an SP). In this scenario the 054 * asserting party uses the "signing" interfaces to generate secure 055 * data and the relying application uses "verification" interfaces 056 * to ascertain the authenticity of the data. 057 * Current implementation provides two mechanisms to secure attributes : 058 * Symmetric : uses simple shared secrets between the two ends. 059 * Asymmetric : uses PKI based signing using public-private keys. 060 * Freshness is provided by a varying seed generated from the 061 * current timestamp and a configurable expiry period within which 062 * the relying party must validate the token. 063 * @supported.api 064 */ 065public class SecureAttrs 066{ 067 /** 068 * HTTP parameter name used to send and receive secure attribute data. 069 * IDP : sends secure attrs in this parameter. 070 * SP : receives secure attrs in this parameter. 071 * @supported.api 072 */ 073 public static final String SAE_PARAM_DATA = "sun.data"; 074 075 /** 076 * SAE Parameter representing a command. 077 * Currently only "logout" needs to be explicitly provided. SSO is implied. 078 * IDP : Uses this parameter to instruct FM to issue a global logout. 079 * SP : Receives this parameter from FM. 080 * @supported.api 081 */ 082 083 public static final String SAE_PARAM_CMD = "sun.cmd"; 084 085 /** 086 * SAE Parameter representing the authenticated user. 087 * IDP : Uses this parameter to send authenticated userid to FM. 088 * SP : Receives userid in this parameter. 089 * @supported.api 090 */ 091 public static final String SAE_PARAM_USERID = "sun.userid"; 092 093 /** 094 * SAE Parameter representing the session's authentication level. 095 * IDP : Uses this parameter to send authentication level to FM. 096 * SP : Receives authentication level in this parameter. 097 * @supported.api 098 */ 099 public static final String SAE_PARAM_AUTHLEVEL = "sun.authlevel"; 100 101 /** 102 * SAE Parameter used to pass IDP entity ID to SP app. 103 * IDP: Not Applicable 104 * SP: populates this parameter to identify IDP used in SSO. 105 */ 106 public static final String SAE_PARAM_IDPENTITYID = "sun.idpentityid"; 107 108 /** 109 * SAE Parameter used to pass SP entity ID to SP app. 110 * IDP: Not Applicable 111 * SP: populates this parameter to identify SP used in SSO. 112 */ 113 public static final String SAE_PARAM_SPENTITYID = "sun.spentityid"; 114 115 /** 116 * SAE Parameter representing the requested SP app to be invoked. 117 * IDP : populates this parameter with SP side app to be invoked. 118 * SP : Not Applicable. 119 * @supported.api 120 */ 121 public static final String SAE_PARAM_SPAPPURL = "sun.spappurl"; 122 123 /** 124 * SAE Parameter used to identify the IDP app (Asserting party) 125 * IDP : populates this parameter to identify itself. 126 * SP : Not Applicable. 127 * @supported.api 128 */ 129 public static final String SAE_PARAM_IDPAPPURL = "sun.idpappurl"; 130 131 /** 132 * SAE Parameter : Deprecated. 133 * @supported.api 134 */ 135 public static final String SAE_PARAM_APPID = "sun.appid"; 136 137 /** 138 * SAE Parameter internally used by FM for storing token timestamp. 139 * @supported.api 140 */ 141 public static final String SAE_PARAM_TS = "sun.ts"; 142 143 /** 144 * SAE Parameter internally used by FM for storing signature data. 145 * @supported.api 146 */ 147 public static final String SAE_PARAM_SIGN = "sun.sign"; 148 149 /** 150 * SAE Parameter used to comunicate errors. 151 * @supported.api 152 */ 153 public static final String SAE_PARAM_ERROR = "sun.error"; 154 155 /** 156 * SAE Parameter used to communicate to SP to return to specified url 157 * upon Logout completion. 158 * IDP : Not applicable 159 * SP : expected to redirect to the value upon processing logout req. 160 * @supported.api 161 */ 162 public static final String SAE_PARAM_APPSLORETURNURL = "sun.returnurl"; 163 164 /** 165 * SAE Parameter used to comunicate to FM where to redirect after a 166 * global logout is completed. 167 * IDP : sends this param as part of logout command. 168 * SP : N/A. 169 * @supported.api 170 */ 171 public static final String SAE_PARAM_APPRETURN = "sun.appreturn"; 172 173 /** 174 * SAE command <code>SAE_PARAM_CMD</code> 175 * @supported.api 176 */ 177 public static final String SAE_CMD_LOGOUT = "logout"; 178 179 /** 180 * Crypto types supported. 181 * @supported.api 182 */ 183 public static final String SAE_CRYPTO_TYPE = "type"; 184 185 /** 186 * Crypto type : Symmetric : shared secret based trust between parties. 187 * @supported.api 188 */ 189 public static final String SAE_CRYPTO_TYPE_ASYM = "asymmetric"; 190 191 /** 192 * Crypto type : Asymmetric : PKI based trust. 193 * @supported.api 194 */ 195 public static final String SAE_CRYPTO_TYPE_SYM = "symmetric"; 196 197 /** 198 * SAE Config : classame implementing <code>Cert</code>. 199 * If not specified, a JKS keystore default impl is used. 200 */ 201 public static final String SAE_CONFIG_CERT_CLASS = "certclassimpl"; 202 203 /** 204 * SAE Config : Location of the keystore to access keys from for 205 * asymmetric crypto. 206 * @supported.api 207 */ 208 public static final String SAE_CONFIG_KEYSTORE_FILE = "keystorefile"; 209 210 /** 211 * SAE Config : keystore type. Default : JKS 212 * @supported.api 213 */ 214 public static final String SAE_CONFIG_KEYSTORE_TYPE = "keystoretype"; 215 216 /** 217 * SAE Config : Password to open the keystrore. 218 * @supported.api 219 */ 220 public static final String SAE_CONFIG_KEYSTORE_PASS = "keystorepass"; 221 222 /** 223 * SAE Config : Private key alias for asymmetric signing. Alias 224 * is used to retrive the key from the keystore. 225 * @supported.api 226 */ 227 public static final String SAE_CONFIG_PRIVATE_KEY_ALIAS = "privatekeyalias"; 228 229 /** 230 * SAE Config : Public key for asymmetric signature verification. Alias 231 * is used to retrive the key from the keystore. 232 * @supported.api 233 */ 234 public static final String SAE_CONFIG_PUBLIC_KEY_ALIAS = "pubkeyalias"; 235 236 /** 237 * SAE Config : Private key for asymmetric signing. 238 * @supported.api 239 */ 240 public static final String SAE_CONFIG_PRIVATE_KEY = "privatekey"; 241 242 /** 243 * SAE Config : Password to access the private key. 244 * @supported.api 245 */ 246 public static final String SAE_CONFIG_PRIVATE_KEY_PASS = "privatekeypass"; 247 248 /** 249 * SAE Config : Flag to indicate whether keys should be cached in memory 250 * once retrieved from the keystore. 251 * @supported.api 252 */ 253 public static final String SAE_CONFIG_CACHE_KEYS = "cachekeys"; 254 255 /** 256 * SAE Config : shared secret constant - used internally in FM. 257 * @supported.api 258 */ 259 public static final String SAE_CONFIG_SHARED_SECRET = "secret"; 260 261 /** 262 * SAE Config : data encryption algorithm. 263 * @supported.api 264 */ 265 public static final String SAE_CONFIG_DATA_ENCRYPTION_ALG = 266 "encryptionalgorithm"; 267 268 /** 269 * SAE Config : data encryption key strength. 270 * @supported.api 271 */ 272 public static final String SAE_CONFIG_ENCRYPTION_KEY_STRENGTH = 273 "encryptionkeystrength"; 274 275 /** 276 * SAE Config : Signature validity : since timetamp on signature. 277 * @supported.api 278 */ 279 public static final String SAE_CONFIG_SIG_VALIDITY_DURATION = 280 "saesigvalidityduration"; 281 /** 282 * Debug : true | false 283 */ 284 public static boolean dbg = false; 285 286 private Certs certs = null; 287 288 private static HashMap instances = new HashMap(); 289 private int tsDuration = 120000; // 2 minutes 290 private boolean asymsigning = false; 291 private boolean asymencryption = false; 292 private String dataEncAlg = "DES"; 293 private int encKeyStrength = 56; 294 295 /** 296 * Returns an instance to perform crypto operations. 297 * @param name 298 * @return <code>SecureAttrs</code> instance. 299 * @supported.api 300 */ 301 public static synchronized SecureAttrs getInstance(String name) 302 { 303 return (SecureAttrs)instances.get(name); 304 } 305 /** 306 * Initializes a SecureAttrs instance specified by <code>name</code>. 307 * If the instance already exists, it replaces it with the new instance. 308 * Use <code>SecureAttrs.getIstance(name)</code> to obtain the instance. 309 * @param name Name of the <code>SecureAttrs</code> instance. 310 * @param type Cryptographic key type. Possible values are 311 * <code>SecureAttrs.SAE_CRYPTO_TYPE_SYM<code>, and 312 * <code>SecureAttrs.SAE_CRYPTO_TYPE_ASYM</code> 313 * @param properties : please see SAE_CONFIG_* constants for configurable 314 * values. 315 * @throws Exception rethrows underlying exception. 316 * @supported.api 317 */ 318 synchronized public static void init( 319 String name, String type, Properties properties) throws Exception 320 { 321 SecureAttrs sa = new SecureAttrs(type, properties); 322 instances.put(name, sa); 323 } 324 325 /** 326 * Creates two instances of <code>SecureAttrs</code> named 327 * "symmetric" and "asymmetric" representing the two suppported 328 * crytp types. 329 * @param properties : please see SAE_CONFIG_* constants for configurable 330 * values. 331 * @throws Exception rethrows underlying exception. 332 * @supported.api 333 * @deprecated For backward compatability with older releases of this api. 334 * Replaced by {@link #init(String,String,Properties)} 335 */ 336 synchronized public static void init(Properties properties) throws Exception 337 { 338 init(SAE_CRYPTO_TYPE_ASYM, SAE_CRYPTO_TYPE_ASYM, properties); 339 init(SAE_CRYPTO_TYPE_SYM, SAE_CRYPTO_TYPE_SYM, properties); 340 } 341 342 /** 343 * Returns a Base64 encoded string comprising a signed set of attributes. 344 * 345 * @param attrs Attribute Value pairs to be processed. 346 * @param secret Shared secret (symmetric) Private key alias (asymmetric) 347 * 348 * @return Base64 encoded token String to be passed to a relying party. 349 * @supported.api 350 */ 351 public String getEncodedString(Map attrs, String secret) throws Exception 352 { 353 String signedAttrs = signAttributes(attrs, secret); 354 return Base64.encode(signedAttrs.getBytes("UTF-8")); 355 } 356 357 /** 358 * Returns encrypted string for the given attributes. The encrypted 359 * data is Base64 encoded string encrypted with supplied encryption 360 * secret and signs using shared secret. 361 * @param attrs Attribute Value pairs to be processed. 362 * @param secret Shared secret (symmetric) Private key alias (asymmetric) * @param encSecret The encryption secret (symmetric) or Public 363 * Key alias (asymmetric) 364 * @return Base64 encoded token String to be passed to a relying party. 365 * @supported.api 366 */ 367 public String getEncodedString(Map attrs, String secret, String encSecret) 368 throws Exception { 369 370 if(encSecret == null) { 371 return getEncodedString(attrs, secret); 372 } 373 374 String signedString = signAttributes(attrs, secret); 375 String encryptedString = null; 376 if(asymencryption) { 377 Key encKey = getPublicKey(encSecret).getPublicKey(); 378 encryptedString = DataEncryptor.encryptWithAsymmetricKey( 379 signedString, 380 dataEncAlg, 381 encKeyStrength, 382 encKey); 383 } else { 384 encryptedString = DataEncryptor.encryptWithSymmetricKey( 385 signedString, 386 dataEncAlg, 387 secret); 388 } 389 if(dbg) { 390 System.out.println("SAE.getEncodedString: encrypted string" + 391 encryptedString); 392 } 393 return encryptedString; 394 } 395 396 private String signAttributes(Map attrs, String secret) throws Exception { 397 if(attrs == null || attrs.isEmpty() ){ 398 return null; 399 } 400 401 StringBuffer sb = new StringBuffer(200); 402 Iterator iter = attrs.entrySet().iterator(); 403 while(iter.hasNext()) { 404 Map.Entry entry = (Map.Entry)iter.next(); 405 String key = (String)entry.getKey(); 406 String value = (String)entry.getValue(); 407 sb.append(key).append("=").append(value).append("|"); 408 } 409 410 sb.append("Signature=").append(getSignedString(attrs, secret)); 411 return sb.toString(); 412 } 413 414 /** 415 * Verifies a Base64 encoded string for authenticity based on the 416 * shared secret supplied. 417 * @param str Base64 encoded string containing attribute 418 * @param secret Shared secret (symmmetric) or Public Key (asymmetric) 419 * 420 * @return Decoded, verified and parsed attrbute name-valie pairs. 421 * @supported.api 422 */ 423 public Map verifyEncodedString(String str, String secret) throws Exception 424 { 425 if(str == null) { 426 return null; 427 } 428 429 Map map = getRawAttributesFromEncodedData(str); 430 if (dbg) 431 System.out.println("SAE:verifyEncodedString() : "+map); 432 String signatureValue = (String) map.remove("Signature"); 433 if(!verifyAttrs(map, signatureValue, secret)) { 434 return null; 435 } 436 return map; 437 } 438 439 /** 440 * Verifies the encrypted data string using encryption secret and 441 * shared secret that was used for signing. 442 * @param str Base64 encoded string containing attribute 443 * @param secret Shared secret (symmmetric) or Public Key (asymmetric) 444 * @param encSecret The encryption secret (symmetric) or Public 445 * Key alias (asymmetric) 446 * @return Decoded, verified and parsed attrbute name-valie pairs. 447 * @supported.api 448 */ 449 public Map verifyEncodedString(String str, String secret, String encSecret) 450 throws Exception { 451 452 if(encSecret == null) { 453 return verifyEncodedString(str, secret); 454 } 455 456 if(!isEncrypted(str)) { 457 return verifyEncodedString(str, secret); 458 } 459 if (str.indexOf(' ') > 0) { 460 str = str.replace(' ', '+'); 461 } 462 String decryptStr = null; 463 if(asymencryption) { 464 Key pKey = certs.getPrivateKey(encSecret); 465 decryptStr = DataEncryptor.decryptWithAsymmetricKey(str, 466 dataEncAlg, pKey); 467 } else { 468 decryptStr = DataEncryptor.decryptWithSymmetricKey(str, 469 dataEncAlg, encSecret); 470 } 471 if (dbg) { 472 System.out.println("SAE:verifyEncodedString() : "+ 473 "decrypted string " + decryptStr); 474 } 475 return verifyEncodedString(decryptStr, secret); 476 477 } 478 479 private boolean isEncrypted(String str) throws Exception { 480 481 if (str.indexOf(' ') > 0) { 482 str = str.replace(' ', '+'); 483 } 484 byte[] decoded = Base64.decode(str); 485 byte[] encString = new byte[9]; 486 for (int i=0; i < 9; i++) { 487 encString[i] = decoded[i]; 488 } 489 490 String tmp = new String(encString, "UTF-8"); 491 if(tmp.equals("ENCRYPTED")) { 492 return true; 493 } 494 return false; 495 } 496 497 /** 498 * Returns a decoded <code>Map</code> of attribute-value pairs. 499 * No verification is performed. Useful when retrieving data before 500 * verifying contents for authenticity. 501 * @param str Base64 encoded string containing attribute 502 * 503 * @return Decoded and parsed attrbute name-value pairs. 504 * @supported.api 505 */ 506 public Map getRawAttributesFromEncodedData(String str) throws Exception 507 { 508 if(str == null) { 509 return null; 510 } 511 512 if (str.indexOf(' ') > 0) 513 str = str.replace(' ', '+'); 514 byte[] bytes = Base64.decode(str); 515 String decoded = new String(bytes, "UTF-8"); 516 if(decoded.indexOf("|") == -1) { 517 return null; 518 } 519 StringTokenizer tokenizer = new StringTokenizer(decoded, "|"); 520 521 Map map = new HashMap(); 522 while(tokenizer.hasMoreTokens()) { 523 String st = tokenizer.nextToken(); 524 int index = st.indexOf("="); 525 if(index == -1) { 526 continue; 527 } 528 String attr = st.substring(0, index); 529 String value = st.substring(index+1, st.length()); 530 map.put(attr, value); 531 } 532 533 return map; 534 } 535 536 /** 537 * Returns a decoded <code>Map</code> of attribute-value pairs. 538 * No verification is performed. Useful when retrieving data before 539 * verifying contents for authenticity. 540 * @param str Base64 encoded string containing attribute 541 * @param encSecret The encryption secret (symmetric) or Public 542 * Key alias (asymmetric) 543 * @return Decoded and parsed attrbute name-value pairs. 544 * @supported.api 545 */ 546 public Map getRawAttributesFromEncodedData(String str, String encSecret) 547 throws Exception { 548 549 if(encSecret == null) { 550 return getRawAttributesFromEncodedData(str); 551 } 552 if (str.indexOf(' ') > 0) { 553 str = str.replace(' ', '+'); 554 } 555 if(!isEncrypted(str)) { 556 return getRawAttributesFromEncodedData(str); 557 } 558 String decryptStr = null; 559 if(asymencryption) { 560 Key pKey = certs.getPrivateKey(encSecret); 561 decryptStr = DataEncryptor.decryptWithAsymmetricKey(str, 562 dataEncAlg, pKey); 563 } else { 564 decryptStr = DataEncryptor.decryptWithSymmetricKey(str, 565 dataEncAlg, encSecret); 566 } 567 if(dbg) { 568 System.out.println("SAE.getRawAttributes() decrypted" + 569 " string" + decryptStr); 570 } 571 return getRawAttributesFromEncodedData(decryptStr); 572 } 573 574 /** 575 * This interface allows to set the private to be used for signing 576 * as an alternative to passing down <code>SAE_CONFIG_PRIVATE_KEY_ALIAS</a> 577 * via <code>init</code>. Use this interface if you do not want 578 * SecureAttr to obtain the signing key from a configured keystore. 579 * To use this key during signing, specify secret as null. 580 * @param privatekey 581 * @supported.api 582 */ 583 public void setPrivateKey(PrivateKey privatekey) 584 { 585 certs.setPrivatekey(privatekey); 586 } 587 588 /** 589 * This interface allows to register a public key to be used for signature 590 * verification. Use this interface if you do not want SecureAttrs to 591 * obtain public keys from a configured keystore. 592 * @param pubkeyalias 593 * @param x509certificate instance. 594 * @supported.api 595 */ 596 public void addPublicKey( 597 String pubkeyalias, X509Certificate x509certificate) 598 { 599 certs.addPublicKey(pubkeyalias, x509certificate); 600 } 601 602 private X509Certificate getPublicKey(String alias) 603 { 604 return certs.getPublicKey(alias); 605 } 606 607 /** 608 * Returns a String representing data in the attrs argument. 609 * The String generated can be one of the following depending 610 * on configuration : 611 * SHA1 digest based on a shared secret and current timestamp. 612 * or 613 * Digital signature based on a configured certificate key. 614 * 615 * @param attrs List of attribute Value pairs to be processed. 616 * @param secret Shared secret (symmmetric) or Private Key (asymmetric) 617 * 618 * @return token String to be passed to a relying party. 619 * @supported.api 620 */ 621 public String getSignedString(Map attrs, String secret) throws Exception 622 { 623 // Normalize 624 StringBuffer str = normalize(attrs); 625 // Setup a fresh timestamp 626 long timestamp = (newDate()).getTime(); 627 628 String signature = null; 629 630 if(asymsigning) 631 { 632 PrivateKey pKey = certs.getPrivateKey(secret); 633 signature = signAsym(str.append(timestamp).toString(), pKey); 634 } else 635 { 636 // Create seed : TIMESTAMP + shared secret 637 String seed = secret+timestamp; 638 // Encrypt 639 signature = encrypt(str+seed, seed); 640 } 641 if (signature == null) { 642 return null; 643 } 644 return ("TS"+timestamp + "TS"+signature); 645 } 646 647 /** 648 * Verifies the authenticity of data the attrs argument based 649 * on the token presented. Both attrs and token is sent by 650 * a asserting party. 651 * @param attrs List of attribute Value pairs to be processed. 652 * @param token token represnting attrs provided by asserting party. 653 * @param secret Shared secret (symmmetric) or Public Key (asymmetric) 654 * 655 * @return true if attrs and token verify okay, else returns false. 656 * @supported.api 657 */ 658 public boolean verifyAttrs(Map attrs, String token, String secret) 659 throws Exception 660 { 661 // Normalize 662 StringBuffer str = normalize(attrs); 663 // Retrieve timestamp 664 int idx = token.indexOf("TS", 2); 665 String ts = token.substring(2, idx); 666 long signts = Long.parseLong(ts); 667 long nowts = (newDate()).getTime(); 668 669 // Check timestamp validity 670 if ((nowts - signts) > tsDuration) 671 return false; 672 673 if(asymsigning) 674 { 675 String signature = token.substring(idx + 2, token.length()); 676 return verifyAsym(str.append(ts).toString(), 677 signature, getPublicKey(secret)); 678 } 679 // Create seed : TIMESTAMP + shared secret 680 String seed = secret + ts; 681 // Encrypt 682 String newstr ="TS"+ts+ "TS"+encrypt(str+seed, seed); 683 if (token.equals(newstr) ) 684 return true; 685 else 686 return false; 687 } 688 689 690 private SecureAttrs(String type, Properties properties) throws Exception 691 { 692 if (SAE_CRYPTO_TYPE_ASYM.equals(type)) { 693 asymsigning = true; 694 asymencryption = true; 695 } 696 String dur = properties.getProperty(SAE_CONFIG_SIG_VALIDITY_DURATION); 697 if (dur != null) 698 tsDuration = Integer.parseInt(dur); 699 700 String clzName = properties.getProperty(SAE_CONFIG_CERT_CLASS); 701 if (clzName != null) 702 certs = (Certs) Class.forName(clzName).newInstance(); 703 //"com.sun.identity.sae.api.FMCerts").newInstance(); 704 else 705 certs = new DefaultCerts(); 706 707 certs.init(properties); 708 dataEncAlg = (String)properties.get(SAE_CONFIG_DATA_ENCRYPTION_ALG); 709 String tmp = (String)properties.get( 710 SAE_CONFIG_ENCRYPTION_KEY_STRENGTH); 711 if(tmp != null) { 712 encKeyStrength = (new Integer(tmp)).intValue(); 713 } 714 } 715 716 private StringBuffer normalize(Map attrs) 717 { 718 // Sort the Map 719 TreeMap smap = new TreeMap(attrs); 720 721 // Flatten to a single String 722 StringBuffer str = new StringBuffer(); 723 Iterator iter = smap.keySet().iterator(); 724 while (iter.hasNext()) { 725 String key = (String) iter.next(); 726 str.append(key).append("=").append(smap.get(key)).append("|"); 727 } 728 return str; 729 } 730 731 private synchronized String encrypt(String plaintext, 732 String seed) throws Exception 733 { 734 MessageDigest md = null; 735 try 736 { 737 md = MessageDigest.getInstance("SHA"); //step 2 738 } catch(NoSuchAlgorithmException e) { 739 throw new Exception(e.getMessage()); 740 } 741 try 742 { 743 md.update((plaintext).getBytes("UTF-8")); //step 3 744 } catch(UnsupportedEncodingException e) { 745 throw new Exception(e.getMessage()); 746 } 747 748 byte raw[] = md.digest(); //step 4 749 String hash = Base64.encode(raw); 750 751 return hash; //step 6 752 } 753 private String signAsym(String s, PrivateKey privatekey) 754 { 755 if(s == null || s.length() == 0 || privatekey == null) { 756 if (dbg) 757 System.out.println("SAE : signAsym: returning since priv key null"); 758 return null; 759 } 760 String s1 = privatekey.getAlgorithm(); 761 Signature signature = null; 762 Object obj = null; 763 if(s1.equals("RSA")) 764 try 765 { 766 signature = Signature.getInstance("SHA1withRSA"); 767 String s2 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; 768 } 769 catch(Exception exception) 770 { 771 System.out.println("SAE:asym sign : RSA failed ="+exception); 772 return null; 773 } 774 else 775 if(s1.equals("DSA")) 776 { 777 try 778 { 779 signature = Signature.getInstance("SHA1withDSA"); 780 String s3 = "http://www.w3.org/2000/09/xmldsig#dsa-sha1"; 781 } 782 catch(Exception exception1) 783 { 784 System.out.println("SAE:asym sign : DSA failed ="+exception1); 785 return null; 786 } 787 } else 788 { 789 System.out.println("SAE:asym sign : No Algorithm"); 790 return null; 791 } 792 try 793 { 794 signature.initSign(privatekey); 795 } 796 catch(Exception exception2) 797 { 798 System.out.println("SAE:asym sign : sig.initSign failed"+exception2); 799 return null; 800 } 801 try 802 { 803 System.out.println("Query str:"+s); 804 signature.update(s.getBytes()); 805 } 806 catch(Exception exception3) 807 { 808 System.out.println("SAE:asym sign : sig.update failed"+exception3); 809 return null; 810 } 811 byte abyte0[] = null; 812 try 813 { 814 abyte0 = signature.sign(); 815 } 816 catch(Exception exception4) 817 { 818 System.out.println("SAE:asym sign : sig.sign failed"+exception4); 819 return null; 820 } 821 if(abyte0 == null || abyte0.length == 0) 822 { 823 System.out.println("SAE:asym sign : sigBytes null"); 824 return null; 825 } else 826 { 827 String s4 = Base64.encode(abyte0); 828 System.out.println("B64 Signature="+s4); 829 return s4; 830 } 831 } 832 833 private boolean verifyAsym(String s, String s1, X509Certificate x509certificate) 834 { 835 if(s == null || s.length() == 0 || x509certificate == null || s1 == null) 836 { 837 if (dbg) 838 System.out.println("SAE:asym verify: qstring or cert or signature is null"); 839 return false; 840 } 841 byte abyte0[] = Base64.decode(s1); 842 if (dbg) 843 System.out.println("SAE:verifyAsym:signature="+abyte0+" origstr="+s1); 844 Object obj = null; 845 Object obj1 = null; 846 String s2 = x509certificate.getPublicKey().getAlgorithm(); 847 Signature signature = null; 848 if(s2.equals("DSA")) 849 try 850 { 851 signature = Signature.getInstance("SHA1withDSA"); 852 } 853 catch(Exception exception) 854 { 855 System.out.println("SAE:asym verify : DSA instance"+exception); 856 exception.printStackTrace(); 857 return false; 858 } 859 else 860 if(s2.equals("RSA")) 861 { 862 try 863 { 864 signature = Signature.getInstance("SHA1withRSA"); 865 } 866 catch(Exception exception1) 867 { 868 System.out.println("SAE:asym verify : RSA instance"+exception1); 869 exception1.printStackTrace(); 870 return false; 871 } 872 } else 873 { 874 System.out.println("SAE:asym verify : no instance"); 875 return false; 876 } 877 try 878 { 879 signature.initVerify(x509certificate); 880 } 881 catch(Exception exception2) 882 { 883 System.out.println("SAE:asym verify :sig.initVerify"+exception2); 884 exception2.printStackTrace(); 885 return false; 886 } 887 try 888 { 889 signature.update(s.getBytes()); 890 } 891 catch(Exception exception3) 892 { 893 System.out.println("SAE:asym verify :sig.update:"+exception3+" sig="+abyte0); 894 exception3.printStackTrace(); 895 return false; 896 } 897 boolean flag = false; 898 try 899 { 900 flag = signature.verify(abyte0); 901 } 902 catch(Exception exception4) 903 { 904 System.out.println("SAE:asym verify :sig.verify:"+exception4+"sig="+abyte0); 905 exception4.printStackTrace(); 906 return false; 907 } 908 return flag; 909 } 910 911 static public void main(String[] args) 912 { 913 try 914 { 915 SecureAttrs.dbg = true; 916 Properties properties = new Properties(); 917 properties.setProperty("keystorefile", "mykeystore"); 918 properties.setProperty("keystoretype", "JKS"); 919 properties.setProperty("keystorepass", "22222222"); 920 properties.setProperty("privatekeyalias", "test"); 921 properties.setProperty("publickeyalias", "test"); 922 properties.setProperty("privatekeypass", "22222222"); 923 properties.setProperty("encryptionkeystrength", "56"); 924 properties.setProperty("encryptionalgorithm", "DES"); 925 926 SecureAttrs.init("testsym", SecureAttrs.SAE_CRYPTO_TYPE_SYM, 927 properties); 928 SecureAttrs.init("testasym", SecureAttrs.SAE_CRYPTO_TYPE_ASYM, 929 properties); 930 System.out.println("TEST 1 START test encoded str ==========="); 931 SecureAttrs secureattrs = SecureAttrs.getInstance("testsym"); 932 String s = "YnJhbmNoPTAwNXxtYWlsPXVzZXI1QG1haWwuY29tfHN1bi51c2VyaWQ9dXNlcjV8U2lnbmF0dXJlPVRTMTE3NDI3ODY1OTM2NlRTbzI2MkhoL3R1dDRJc0U1V3ZqWjVSLzZkM0FzPQ=="; 933 Map map = secureattrs.verifyEncodedString(s, "secret"); 934 if(map == null) 935 System.out.println(" FAILED"); 936 else 937 System.out.println(" PASSED"+map); 938 System.out.println("TEST 1 END ================"); 939 System.out.println("TEST 2 START : encode followed by decode ==="); 940 HashMap hashmap = new HashMap(); 941 hashmap.put("branch", "bb"); 942 hashmap.put("mail", "mm"); 943 hashmap.put("sun.userid", "uu"); 944 hashmap.put("sun.spappurl", "apapp"); 945 System.out.println(" TEST 2a START : SYM KEY ==="); 946 secureattrs = SecureAttrs.getInstance("testsym"); 947 String s1 = "secret"; 948 String s2 = secureattrs.getEncodedString(hashmap, s1); 949 System.out.println("Encoded string: "+s2); 950 Map map1 = secureattrs.verifyEncodedString(s2, s1); 951 if(map1 != null) 952 System.out.println(" 2a PASSED "+map1); 953 else 954 System.out.println(" 2a FAILED "+map1); 955 System.out.println(" TEST 2b START : ASYM KEY ==="); 956 secureattrs = getInstance("testasym"); 957 s1 = "test"; 958 String s3 = secureattrs.getEncodedString(hashmap, s1); 959 System.out.println("Encoded string: "+s3); 960 map1 = secureattrs.verifyEncodedString(s3, s1); 961 if(map1 != null) 962 System.out.println(" 2b PASSED "+map1); 963 else 964 System.out.println(" 2b FAILED "+map1); 965 System.out.println("TEST 2 END ===================="); 966 System.out.println("TEST 3 START : decode with incorrect secret"); 967 System.out.println(" TEST 3a START : SYM KEY ==="); 968 secureattrs = getInstance("testsym"); 969 map1 = secureattrs.verifyEncodedString(s2, "junk"); 970 if(map1 != null) 971 System.out.println(" 3a FAILED "+map1); 972 else 973 System.out.println(" 3a PASSED "+map1); 974 System.out.println(" TEST 3b START : ASYM KEY ==="); 975 secureattrs = getInstance("testasym"); 976 map1 = secureattrs.verifyEncodedString(s3, "junk"); 977 if(map1 != null) 978 System.out.println(" 3b FAILED "+map1); 979 else 980 System.out.println(" 3b PASSED "+map1); 981 System.out.println("TEST 3 END ===================="); 982 System.out.println("TEST 4 START : decode with correct secret"); 983 System.out.println(" TEST 4a START : SYM KEY ==="); 984 secureattrs = getInstance("testsym"); 985 s1 = "secret"; 986 map1 = secureattrs.verifyEncodedString(s2, s1); 987 if(map1 != null) 988 System.out.println(" 4a PASSED "+map1); 989 else 990 System.out.println(" 4a FAILED "+map1); 991 System.out.println(" TEST 4b START : ASYM KEY ==="); 992 secureattrs = getInstance("testasym"); 993 s1 = "test"; 994 map1 = secureattrs.verifyEncodedString(s3, s1); 995 if(map1 != null) 996 System.out.println(" 4a PASSED "+map1); 997 else 998 System.out.println(" 4a FAILED "+map1); 999 System.out.println("TEST 4 END ===================="); 1000 System.out.println(" TEST 5a START : ASYM KEY ==="); 1001 secureattrs = getInstance("testasym"); 1002 s1 = "test"; 1003 s3 = secureattrs.getEncodedString(hashmap, s1, s1); 1004 System.out.println("Encrypted string: "+s3); 1005 map1 = secureattrs.verifyEncodedString(s3, s1, s1); 1006 if(map1 != null) 1007 System.out.println(" 5a PASSED "+map1); 1008 else 1009 System.out.println(" 5a FAILED "+map1); 1010 System.out.println(" TEST 5b START : SYM KEY ==="); 1011 secureattrs = SecureAttrs.getInstance("testsym"); 1012 s1 = "secret"; 1013 s2 = secureattrs.getEncodedString(hashmap, s1, s1); 1014 System.out.println("Encrypted string: "+s2); 1015 map1 = secureattrs.verifyEncodedString(s2, s1, s1); 1016 if(map1 != null) 1017 System.out.println(" 5b PASSED "+map1); 1018 else 1019 System.out.println(" 5b FAILED "+map1); 1020 System.out.println("TEST 5 END ===================="); 1021 } 1022 catch(Exception exception) 1023 { 1024 exception.printStackTrace(); 1025 System.out.println("TEST Exc : "+exception); 1026 } 1027 1028 } 1029 public interface Certs { 1030 public void init(Properties props) throws Exception; 1031 public PrivateKey getPrivateKey(String alias); 1032 public X509Certificate getPublicKey(String alias); 1033 public void setPrivatekey(PrivateKey privatekey); 1034 public void addPublicKey( 1035 String pubkeyalias, X509Certificate x509certificate); 1036 } 1037 static class DefaultCerts implements Certs 1038 { 1039 private PrivateKey privateKey = null; 1040 private KeyStore ks = null; 1041 private String keystoreFile = ""; 1042 private HashMap keyTable = new HashMap(); 1043 private boolean cacheKeys = true; 1044 private String pkpass = null; 1045 1046 public void init(Properties properties) throws Exception 1047 { 1048 String keyfile = properties.getProperty("keystorefile"); 1049 if(keyfile != null) 1050 { 1051 String ktype = properties.getProperty( 1052 SAE_CONFIG_KEYSTORE_TYPE, "JKS"); 1053 ks = KeyStore.getInstance(ktype); 1054 FileInputStream fileinputstream = new FileInputStream(keyfile); 1055 String kpass = properties.getProperty(SAE_CONFIG_KEYSTORE_PASS); 1056 pkpass = properties.getProperty(SAE_CONFIG_PRIVATE_KEY_PASS); 1057 ks.load(fileinputstream, kpass.toCharArray()); 1058 String pkeyalias = properties.getProperty( 1059 SAE_CONFIG_PRIVATE_KEY_ALIAS ); 1060 if(pkeyalias != null) { 1061 privateKey = (PrivateKey)ks.getKey(pkeyalias, 1062 pkpass.toCharArray()); 1063 } 1064 String pubkeyalias = properties.getProperty( 1065 SAE_CONFIG_PUBLIC_KEY_ALIAS ); 1066 if ("false".equals(properties.getProperty( 1067 SAE_CONFIG_CACHE_KEYS))) 1068 cacheKeys = false; 1069 1070 if (cacheKeys && pubkeyalias != null) 1071 getPublicKeyFromKeystore(pubkeyalias); 1072 } 1073 } 1074 public PrivateKey getPrivateKey(String alias) 1075 { 1076 try { 1077 if (alias == null) 1078 return privateKey; 1079 return (PrivateKey)ks.getKey(alias, 1080 pkpass.toCharArray()); 1081 } catch (Exception ex) { 1082 return null; 1083 } 1084 } 1085 public X509Certificate getPublicKey(String alias) 1086 { 1087 X509Certificate x509certificate = 1088 (X509Certificate)keyTable.get(alias); 1089 if (x509certificate == null && ks != null) { 1090 try 1091 { 1092 x509certificate = getPublicKeyFromKeystore(alias); 1093 } 1094 catch(Exception exception) 1095 { 1096 System.out.println("SAE:getPublicKey:Exc:"+exception); 1097 } 1098 } 1099 return x509certificate; 1100 } 1101 public void setPrivatekey(PrivateKey privatekey) 1102 { 1103 privateKey = privatekey; 1104 } 1105 public void addPublicKey( 1106 String pubkeyalias, X509Certificate x509certificate) 1107 { 1108 keyTable.put(pubkeyalias, x509certificate); 1109 } 1110 private X509Certificate getPublicKeyFromKeystore(String pubkeyalias) 1111 throws Exception 1112 { 1113 X509Certificate x509certificate = 1114 (X509Certificate)ks.getCertificate(pubkeyalias); 1115 if(cacheKeys) 1116 keyTable.put(pubkeyalias, x509certificate); 1117 return x509certificate; 1118 } 1119 } 1120}