1 18 package sync4j.server.notification; 19 20 import java.security.GeneralSecurityException ; 21 import java.util.logging.Level ; 22 import java.util.logging.Logger ; 23 24 import sync4j.framework.config.Configuration; 25 import sync4j.framework.core.bootstrap.BootStrap; 26 import sync4j.framework.core.dm.ddf.SyncMLDM; 27 import sync4j.framework.logging.Sync4jLogger; 28 import sync4j.framework.logging.Sync4jLoggerName; 29 import sync4j.framework.notification.*; 30 import sync4j.framework.tools.DbgTools; 31 import sync4j.framework.tools.SecurityTools; 32 import sync4j.framework.tools.beans.BeanFactory; 33 34 import org.apache.commons.lang.StringUtils; 35 36 42 public class NotificationEngineImpl extends AbstractNotificationEngine { 43 44 46 private static final float SYNCML_DM_PROTOCOL_VERSION = 1.1f; 47 48 public static final String NOTIFICATION_SENDER_BEAN = 49 "sync4j/server/engine/dm/NotificationSender.xml"; 50 51 public static final String BOOTSTRAP_SENDER_BEAN = 52 "sync4j/server/engine/dm/BootstrapSender.xml"; 53 54 55 57 59 private static final Logger log = Sync4jLogger.getLogger(Sync4jLoggerName.SERVER_DM_NOTIFICATION); 60 61 private Configuration config = null; 62 63 64 66 public NotificationEngineImpl(Configuration config) { 67 super(config); 68 this.config = config; 69 } 70 71 73 85 public void sendNotificationMessage( int messageType , 86 int transportType , 87 int sessionId , 88 String phoneNumber , 89 String info , 90 String serverId , 91 String serverPassword, 92 byte[] serverNonce 93 ) throws NotificationException { 94 95 if (log.isLoggable(Level.INFO)) { 96 log.info("sendNotificationMessage with: "+ 97 "\n- messageType: " + messageType + 98 "\n- transportType: " + transportType + 99 "\n- sessionId: " + sessionId + 100 "\n- phoneNumber: " + phoneNumber + 101 "\n- info: " + info + 102 "\n- serverId: " + serverId + 103 "\n- serverPassword: " + serverPassword + 104 "\n- serverNonde: " + serverNonce 105 ); 106 } 107 108 NotificationMessageBuilder messageBuilder = null; 109 Sender messageSender = null; 110 111 messageBuilder = getNotificationMessageBuilder(messageType); 112 113 messageSender = getSender(transportType, messageType); 114 115 if (log.isLoggable(Level.FINE)) { 116 log.fine("NotificationMessageBuilder: " + messageBuilder); 117 log.fine("Sender: " + messageSender ); 118 } 119 120 121 byte[] message = messageBuilder.buildMessage(sessionId, phoneNumber, serverId, serverPassword, serverNonce, SYNCML_DM_PROTOCOL_VERSION); 123 124 125 if (log.isLoggable(Level.FINE)) { 126 log.fine("Notification message to send: " + DbgTools.bytesToHex(message)); 127 } 128 129 messageSender.sendMessage(messageType, phoneNumber, message, info); 131 132 if (log.isLoggable(Level.INFO)) { 133 log.info("Notification message to " + phoneNumber + " sent"); 134 } 135 136 } 137 138 150 public void sendNotificationMessages(int messageType, 151 int transportType, 152 int[] sessionIds, 153 String [] phoneNumbers, 154 String info, 155 String serverId, 156 String [] serverPasswords, 157 byte[][] serverNonces) throws NotificationException { 158 159 if (log.isLoggable(Level.INFO)) { 160 log.info("sendNotificationMessages with: "+ 161 "\n- messageType: " + messageType + 162 "\n- transportType: " + transportType + 163 "\n- info: " + info + 164 "\n- serverId: " + serverId ); 165 } 166 167 NotificationMessageBuilder messageBuilder = null; 168 Sender messageSender = null; 169 170 messageBuilder = getNotificationMessageBuilder(messageType); 171 messageSender = getSender(transportType, messageType); 172 173 if (log.isLoggable(Level.FINE)) { 174 log.fine("NotificationMessageBuilder: " + messageBuilder); 175 log.fine("Sender: " + messageSender ); 176 } 177 178 int numDevices = phoneNumbers.length; 179 byte[][] messages = new byte[numDevices][]; 180 for (int i=0; i<numDevices; i++) { 181 182 messages[i] = messageBuilder.buildMessage(sessionIds[i], phoneNumbers[i], serverId, 184 serverPasswords[i], serverNonces[i], SYNCML_DM_PROTOCOL_VERSION); 185 186 if (log.isLoggable(Level.FINE)) { 187 log.fine("Notification message to send to " + phoneNumbers[i] + ": " + 188 DbgTools.bytesToHex(messages[i])); 189 } 190 } 191 192 messageSender.sendMessages(messageType, phoneNumbers, messages, info); 194 195 if (log.isLoggable(Level.INFO)) { 196 log.info("Notification message sent to " + phoneNumbers.length + " devices"); 197 } 198 199 } 200 201 202 203 214 public void sendBootstrapMessage(int messageType, 215 int transportType, 216 String serverUri, 217 String deviceUri, 218 String phoneNumber, 219 SyncMLDM syncMLDM, 220 String info) throws NotificationException { 221 222 if (log.isLoggable(Level.INFO)) { 223 log.info("sendBootstrapMessage with messageType: " + messageType + 224 ", transportType: " + transportType + ", serverUri: " + serverUri + 225 ", deviceUri: " + deviceUri + 226 ", phoneNumber: " + phoneNumber); 227 } 228 229 234 BootstrapMessageBuilder messageBuilder = null; 235 Sender messageSender = null; 236 237 messageBuilder = getBootstrapMessageBuilder(messageType); 238 239 messageSender = getSender(transportType, messageType); 240 241 if (log.isLoggable(Level.FINE)) { 242 log.fine("BootstrapMessageBuilder: " + messageBuilder); 243 log.fine("Sender: " + messageSender); 244 } 245 246 byte[] message = messageBuilder.buildMessage(serverUri, deviceUri, phoneNumber, syncMLDM); 248 249 if (log.isLoggable(Level.INFO)) { 250 log.info("Bootstrap message to send: " + message + " (length: " + message.length + ")"); 251 } 252 253 messageSender.sendMessage(messageType, phoneNumber, message, info); 255 256 if (log.isLoggable(Level.INFO)) { 257 log.info("Bootstrap message to " + phoneNumber + " sent"); 258 } 259 } 260 261 262 273 public void sendBootstrapMessages(int messageType, 274 int transportType, 275 BootStrap[] bootstrap, 276 String serverUri, 277 SyncMLDM[] syncMLDM, 278 String info) throws NotificationException { 279 280 if (log.isLoggable(Level.INFO)) { 281 log.info("sendBootstrapMessage with messageType: " + messageType + 282 ", transportType: " + transportType + " to " + bootstrap.length + " devices"); 283 } 284 285 int numDevices = bootstrap.length; 286 287 Sender messageSender = getSender(transportType, messageType); 288 289 byte[][] messages = new byte[numDevices][]; 290 String [] macs = new String [numDevices]; 291 int[] authMethods = new int[numDevices]; 292 String [] phoneNumbers = new String [numDevices]; 293 294 BootstrapMessageBuilder messageBuilder = null; 295 String imsi = null; 296 String userPin = null; 297 298 for (int i=0; i<numDevices; i++) { 299 verifyBootStrap(bootstrap[i]); 300 301 phoneNumbers[i] = bootstrap[i].getMsisdn(); 302 303 309 messageBuilder = getBootstrapMessageBuilder(messageType); 310 311 if (log.isLoggable(Level.FINEST)) { 312 log.finest("BootstrapMessageBuilder: " + messageBuilder); 313 } 314 315 messages[i] = messageBuilder.buildMessage(serverUri, 317 bootstrap[i].getDeviceId(), 318 bootstrap[i].getMsisdn(), 319 syncMLDM[i]); 320 321 authMethods[i] = bootstrap[i].getAuthMethod(); 322 323 macs[i] = bootstrap[i].getDigest(); 324 if (macs[i] == null) { 325 326 imsi = bootstrap[i].getImsi(); 327 userPin = bootstrap[i].getUserPin(); 328 329 if (authMethods[i] == NotificationConstants.AUTH_METHOD_NETWPIN) { 330 331 if (bootstrap[i].isImsiInSemiOctet()) { 332 if (log.isLoggable(Level.FINEST)) { 333 log.finest("Compute mac using imsi in semi-octect"); 334 } 335 336 macs[i] = computeMac(getKeyFromIMSI(imsi), messages[i]); 337 } else { 338 if (log.isLoggable(Level.FINEST)) { 339 log.finest("Compute mac using imsi in textplain"); 340 } 341 342 macs[i] = computeMac(imsi.getBytes(), messages[i]); 343 } 344 } else if (authMethods[i] == NotificationConstants.AUTH_METHOD_USERPIN) { 345 346 byte[] key = null; 347 if (userPin == null) { 351 352 if (log.isLoggable(Level.FINEST)) { 353 log.finest("Try using USERPIN but userPin is null. Uses imsi to calculate the MAC "); 354 } 355 356 if (imsi == null) { 357 if (log.isLoggable(Level.FINEST)) { 358 log.finest("Try using USERPIN but userPin and imsi are null"); 359 } 360 throw new NotificationException( 361 "Userpin or imsi must be not null using USERPIN authentication method"); 362 } 363 364 if (bootstrap[i].isImsiInSemiOctet()) { 365 366 if (log.isLoggable(Level.FINEST)) { 367 log.finest("Compute mac using imsi in semi-octect"); 368 } 369 370 key = getKeyFromIMSI(imsi); 371 } else { 372 if (log.isLoggable(Level.FINEST)) { 373 log.finest("Compute mac using imsi in textplain"); 374 } 375 376 key = imsi.getBytes(); 377 } 378 } else { 379 if (log.isLoggable(Level.FINEST)) { 380 log.finest("Compute mac using user pin"); 381 } 382 key = userPin.getBytes(); 383 } 384 385 macs[i] = computeMac(key, messages[i]); 386 } else if (authMethods[i] == NotificationConstants.AUTH_METHOD_USERNETWPIN) { 387 byte[] imsiKey = null; 388 if (bootstrap[i].isImsiInSemiOctet()) { 389 if (log.isLoggable(Level.FINEST)) { 390 log.finest("Compute mac using imsi (in semi-octect) and user pin"); 391 } 392 393 imsiKey = getKeyFromIMSI(imsi); 394 } else { 395 if (log.isLoggable(Level.FINEST)) { 396 log.finest("Compute mac using imsi (in text-plain) and user pin"); 397 } 398 imsiKey = imsi.getBytes(); 399 } 400 int imsiKeyLength = imsiKey.length; 402 byte[] userPinKey = userPin.getBytes(); 403 int userPinKeyLength = userPinKey.length; 404 byte[] key = new byte[imsiKeyLength + userPinKeyLength]; 405 System.arraycopy(imsiKey, 0, key, 0, imsiKeyLength); 406 System.arraycopy(userPinKey, 0, key, imsiKeyLength, userPinKeyLength); 407 macs[i] = computeMac(key, messages[i]); 408 } 409 410 } 411 412 413 414 if (log.isLoggable(Level.FINEST)) { 415 log.finest("Bootstrap message to send: " + messages[i] + " (length: " + messages[i].length + 416 ")"); 417 } 418 } 419 420 messageSender.sendMessages(messageType, CONTENT_TYPE_BOOTSTRAP, macs, authMethods, phoneNumbers, messages, info); 422 } 423 424 435 public void sendBootstrapMessages(int messageType, 436 int transportType, 437 String [] phoneNumbers, 438 String [] macs, 439 int[] authMethods, 440 byte[][] messages, 441 String info) throws NotificationException { 442 443 Sender messageSender = null; 444 445 messageSender = getSender(transportType, messageType); 446 447 if (log.isLoggable(Level.FINE)) { 448 log.fine("Sender: " + messageSender); 449 } 450 451 messageSender.sendMessages(messageType, CONTENT_TYPE_BOOTSTRAP, macs, authMethods, phoneNumbers, messages, info); 453 } 454 455 463 public BootStrap[] prepareBootstrap(int messageType, 464 BootStrap[] bootstrap, 465 String serverUri, 466 SyncMLDM[] syncMLDM, 467 String info) throws NotificationException { 468 469 if (log.isLoggable(Level.INFO)) { 470 log.info("prepareBootstrap with messageType: " + messageType + 471 " for " + bootstrap.length + " devices"); 472 } 473 474 int numDevices = bootstrap.length; 475 476 byte[][] messages = new byte[numDevices][]; 477 String [] digests = new String [numDevices]; 478 String [] phoneNumbers = new String [numDevices]; 479 480 BootstrapMessageBuilder messageBuilder = null; 481 482 for (int i=0; i<numDevices; i++) { 483 484 digests[i] = bootstrap[i].getDigest(); 485 phoneNumbers[i] = bootstrap[i].getMsisdn(); 486 487 493 messageBuilder = getBootstrapMessageBuilder(messageType); 494 495 if (log.isLoggable(Level.FINEST)) { 496 log.finest("BootstrapMessageBuilder: " + messageBuilder); 497 } 498 499 messages[i] = messageBuilder.buildMessage(serverUri, 501 bootstrap[i].getDeviceId(), 502 bootstrap[i].getMsisdn(), 503 syncMLDM[i]); 504 505 bootstrap[i].setBootstrapMessage(messages[i]); 506 if (log.isLoggable(Level.FINEST)) { 507 log.finest(bootstrap[i].toString()); 508 } 509 } 510 511 return bootstrap; 512 } 513 514 515 516 523 private BootstrapMessageBuilder getBootstrapMessageBuilder(int messageType) throws 524 NotificationException { 525 BootstrapMessageBuilder messageBuilder = null; 526 527 switch (messageType) { 528 case NotificationConstants.MESSAGE_TYPE_BOOTSTRAP_PLAIN: 529 messageBuilder = new PlainBootstrapMessageBuilder(); 530 break; 531 case NotificationConstants.MESSAGE_TYPE_BOOTSTRAP_WAP: 532 messageBuilder = new WAPBootstrapMessageBuilder(); 533 break; 534 535 default: 536 throw new NotificationException("Message type not supported"); 537 } 538 539 return messageBuilder; 540 } 541 542 550 private Sender getSender(int transportType, int messageType) throws NotificationException { 551 Sender messageSender = null; 552 553 ClassLoader classLoader = config.getClassLoader(); 555 556 try { 557 if (messageType == NotificationConstants.MESSAGE_TYPE_BOOTSTRAP_PLAIN || 558 messageType == NotificationConstants.MESSAGE_TYPE_BOOTSTRAP_WAP) { 559 560 messageSender = (Sender)BeanFactory.getBeanInstance(classLoader, BOOTSTRAP_SENDER_BEAN); 561 562 } else if (messageType == NotificationConstants.MESSAGE_TYPE_NOTIFICATION_GENERIC) { 563 564 messageSender = (Sender)BeanFactory.getBeanInstance(classLoader, NOTIFICATION_SENDER_BEAN); 565 566 } else { 567 messageSender = new SimpleSender(); 568 } 569 570 } catch (Exception ex) { 571 log.throwing(getClass().getName(), "getSender", ex); 572 throw new NotificationException("Error reading the sender", ex); 573 } 574 575 return messageSender; 576 } 577 578 584 private NotificationMessageBuilder getNotificationMessageBuilder(int messageType) throws 585 NotificationException { 586 587 NotificationMessageBuilder messageBuilder = null; 588 589 switch (messageType) { 591 case NotificationConstants.MESSAGE_TYPE_NOTIFICATION_GENERIC: 592 messageBuilder = new GenericNotificationMessageBuilder(); 593 break; 594 default: 595 throw new NotificationException("Message type not supported"); 596 } 597 598 return messageBuilder; 599 } 600 601 609 private String computeMac(byte[] key, byte[] message) throws NotificationException { 610 611 byte[] bMac = null; 612 613 try { 614 bMac = SecurityTools.computeHmacSha1(key, message); 615 } catch (GeneralSecurityException e) { 616 throw new NotificationException("Error computing the mac", e); 617 } 618 619 String mac = bytesToHex(bMac); 620 621 if (log.isLoggable(Level.FINEST)) { 622 log.finest("Mac: " + mac); 623 } 624 return mac; 625 } 626 627 632 private static String bytesToHex(byte[] b) { 633 StringBuffer buf = new StringBuffer (""); 634 for (int i = 0; i < b.length; i++) { 635 buf.append(byteToHex(b[i])); 636 } 637 return buf.toString(); 638 } 639 640 645 private static String byteToHex(byte b) { 646 char hexDigit[] = { 648 '0', '1', '2', '3', '4', '5', '6', '7', 649 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' 650 }; 651 char[] array = {hexDigit[ (b >> 4) & 0x0f], hexDigit[b & 0x0f]}; 652 return new String (array); 653 } 654 655 665 private static byte[] getKeyFromIMSI(String imsi) throws NotificationException { 666 667 imsi = imsi.trim(); 668 669 if (log.isLoggable(Level.FINEST)) { 670 log.finest("IMSI: " + imsi); 671 } 672 673 if ( (imsi.length() % 2) == 1 ) { 674 imsi = "9" + imsi; 675 } else { 676 imsi = "1" + imsi; 677 imsi = imsi + "F"; 678 679 } 680 681 if (log.isLoggable(Level.FINEST)) { 682 log.finest("IMSI updated: " + imsi); 683 } 684 685 int numDigit = imsi.length(); 686 String temp = null; 687 char c1 = 0; 688 char c2 = 0; 689 byte b = 0; 690 byte[] key = new byte[numDigit / 2]; int t = 0; 692 for (int i = 0; i < numDigit; i++) { 693 c1 = imsi.charAt(i); 694 c2 = imsi.charAt(++i); 695 temp = "" + c2 + c1; 696 try { 697 key[t] = (byte) (Integer.parseInt(temp, 16)); 698 } catch (NumberFormatException ex) { 699 throw new NotificationException("IMSI isn't valid (only numbers are permitted)"); 700 } 701 t++; 702 } 703 if (log.isLoggable(Level.FINEST)) { 704 log.finest("Key from imsi: " + bytesToHex(key)); 705 } 706 707 return key; 708 } 709 710 711 716 private void verifyBootStrap(BootStrap bootstrap) throws NotificationException { 717 718 if (bootstrap == null) { 719 throw new NotificationException( 720 "Unable to send the bootstrap (is null)"); 721 } 722 723 String imsi = bootstrap.getImsi(); 724 String userPin = bootstrap.getUserPin(); 725 int authMethod = bootstrap.getAuthMethod(); 726 String digest = bootstrap.getDigest(); 727 728 if (StringUtils.isNotEmpty(digest)) { 729 return; 730 } 731 732 if (authMethod == NotificationConstants.AUTH_METHOD_NETWPIN) { 733 if (StringUtils.isEmpty(imsi)) { 734 throw new NotificationException( 735 "Imsi can't be null using NETWPIN authentication method"); 736 } 737 } else if (authMethod == NotificationConstants.AUTH_METHOD_USERPIN) { 738 if (StringUtils.isEmpty(userPin) && StringUtils.isEmpty(imsi)) { 739 throw new NotificationException( 740 "Userpin or imsi must be not null using USERPIN authentication method"); 741 } 742 } else if (authMethod == NotificationConstants.AUTH_METHOD_USERNETWPIN) { 743 if (StringUtils.isEmpty(imsi)) { 744 throw new NotificationException( 745 "Imsi can't be null using USERNETWPIN authentication method"); 746 } 747 748 if (StringUtils.isEmpty(userPin)) { 749 throw new NotificationException( 750 "Userpin can't be null using USERNETWPIN authentication method"); 751 } 752 } 753 } 754 755 } | Popular Tags |