Return client's authentication response to server's challenge. @param host the host name @param user the user name @param passwd the user's password @param realm the security realm @param serverChallenge the challenge from the server @return byte array with client's response @exception IOException
(String host, String user, String passwd, String realm, String serverChallenge)
| 58 | * @exception IOException for I/O errors |
| 59 | */ |
| 60 | public byte[] authClient(String host, String user, String passwd, |
| 61 | String realm, String serverChallenge) |
| 62 | throws IOException { |
| 63 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); |
| 64 | OutputStream b64os = new BASE64EncoderStream(bos, Integer.MAX_VALUE); |
| 65 | SecureRandom random; |
| 66 | try { |
| 67 | //random = SecureRandom.getInstance("SHA1PRNG"); |
| 68 | random = new SecureRandom(); |
| 69 | md5 = MessageDigest.getInstance("MD5"); |
| 70 | } catch (NoSuchAlgorithmException ex) { |
| 71 | logger.log(Level.FINE, "NoSuchAlgorithmException", ex); |
| 72 | throw new IOException(ex.toString()); |
| 73 | } |
| 74 | StringBuilder result = new StringBuilder(); |
| 75 | |
| 76 | uri = "smtp/" + host; |
| 77 | String nc = "00000001"; |
| 78 | String qop = "auth"; |
| 79 | byte[] bytes = new byte[32]; // arbitrary size ... |
| 80 | int resp; |
| 81 | |
| 82 | logger.fine("Begin authentication ..."); |
| 83 | |
| 84 | // Code based on http://www.ietf.org/rfc/rfc2831.txt |
| 85 | Map<String, String> map = tokenize(serverChallenge); |
| 86 | |
| 87 | if (realm == null) { |
| 88 | String text = map.get("realm"); |
| 89 | realm = text != null ? new StringTokenizer(text, ",").nextToken() |
| 90 | : host; |
| 91 | } |
| 92 | |
| 93 | // server challenge random value |
| 94 | String nonce = map.get("nonce"); |
| 95 | |
| 96 | // Does server support UTF-8 usernames and passwords? |
| 97 | String charset = map.get("charset"); |
| 98 | boolean utf8 = charset != null && charset.equalsIgnoreCase("utf-8"); |
| 99 | |
| 100 | random.nextBytes(bytes); |
| 101 | b64os.write(bytes); |
| 102 | b64os.flush(); |
| 103 | |
| 104 | // client challenge random value |
| 105 | String cnonce = bos.toString("iso-8859-1"); // really ASCII? |
| 106 | bos.reset(); |
| 107 | |
| 108 | // DIGEST-MD5 computation, common portion (order critical) |
| 109 | if (utf8) { |
| 110 | String up = user + ":" + realm + ":" + passwd; |
| 111 | md5.update(md5.digest(up.getBytes(StandardCharsets.UTF_8))); |
| 112 | } else |
| 113 | md5.update(md5.digest( |
| 114 | ASCIIUtility.getBytes(user + ":" + realm + ":" + passwd))); |
| 115 | md5.update(ASCIIUtility.getBytes(":" + nonce + ":" + cnonce)); |
| 116 | clientResponse = toHex(md5.digest()) |
| 117 | + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":"; |