This closes #1169
diff --git a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/policy/CreatePasswordSensorIntegrationTest.java b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/policy/CreatePasswordSensorIntegrationTest.java
new file mode 100644
index 0000000..360b705
--- /dev/null
+++ b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/policy/CreatePasswordSensorIntegrationTest.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.camp.brooklyn.policy;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.sensor.Sensors;
+import org.apache.brooklyn.entity.software.base.EmptySoftwareProcess;
+import org.apache.brooklyn.test.Asserts;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.Iterables;
+
+public class CreatePasswordSensorIntegrationTest extends AbstractYamlTest {
+
+    private static final Logger LOG = LoggerFactory.getLogger(CreatePasswordSensorIntegrationTest.class);
+    AttributeSensor<String> PASSWORD_1 = Sensors.newStringSensor("test.password.1", "Host name as known internally in " +
+            "the subnet where it is running (if different to host.name)");
+    AttributeSensor<String> PASSWORD_2 = Sensors.newStringSensor("test.password.2", "Host name as known internally in " +
+            "the subnet where it is running (if different to host.name)");
+
+    @Test(groups = "Integration")
+    public void testProvisioningProperties() throws Exception {
+        final Entity app = createAndStartApplication(loadYaml("EmptySoftwareProcessWithPassword.yaml"));
+
+        waitForApplicationTasks(app);
+        EmptySoftwareProcess entity = Iterables.getOnlyElement(Entities.descendants(app, EmptySoftwareProcess.class));
+
+        assertPasswordLength(entity, PASSWORD_1, 15);
+
+        assertPasswordOnlyContains(entity, PASSWORD_2, "abc");
+
+    }
+
+    private void assertPasswordOnlyContains(EmptySoftwareProcess entity, AttributeSensor<String> password, String acceptableChars) {
+        String attribute_2 = entity.getAttribute(password);
+        for (char c : attribute_2.toCharArray()) {
+            Asserts.assertTrue(acceptableChars.indexOf(c) != -1);
+        }
+    }
+
+    private void assertPasswordLength(EmptySoftwareProcess entity, AttributeSensor<String> password, int expectedLength) {
+        String attribute_1 = entity.getAttribute(password);
+        Asserts.assertEquals(attribute_1.length(), expectedLength);
+    }
+
+}
diff --git a/brooklyn-server/camp/camp-brooklyn/src/test/resources/EmptySoftwareProcessWithPassword.yaml b/brooklyn-server/camp/camp-brooklyn/src/test/resources/EmptySoftwareProcessWithPassword.yaml
new file mode 100644
index 0000000..2b6acb2
--- /dev/null
+++ b/brooklyn-server/camp/camp-brooklyn/src/test/resources/EmptySoftwareProcessWithPassword.yaml
@@ -0,0 +1,17 @@
+name: example-with-CreatePasswordSensor
+description: |
+  Creates an emptyService and then attaches a password to it
+origin: https://github.com/apache/incubator-brooklyn
+location: localhost
+services:
+- type: org.apache.brooklyn.entity.software.base.EmptySoftwareProcess
+  brooklyn.initializers:
+  - type: org.apache.brooklyn.core.sensor.password.CreatePasswordSensor
+    brooklyn.config:
+      name: test.password.1
+      password.length: 15
+  - type: org.apache.brooklyn.core.sensor.password.CreatePasswordSensor
+    brooklyn.config:
+      name: test.password.2
+      password.chars: abc
+
diff --git a/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/password/CreatePasswordSensor.java b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/password/CreatePasswordSensor.java
new file mode 100644
index 0000000..7b7a908
--- /dev/null
+++ b/brooklyn-server/core/src/main/java/org/apache/brooklyn/core/sensor/password/CreatePasswordSensor.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.sensor.password;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.EntityLocal;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.effector.AddSensor;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.text.Identifiers;
+
+public class CreatePasswordSensor extends AddSensor<String> {
+
+    public static final ConfigKey<Integer> PASSWORD_LENGTH = ConfigKeys.newIntegerConfigKey("password.length", "The length of the password to be created", 12);
+
+    public static final ConfigKey<String> ACCEPTABLE_CHARS = ConfigKeys.newStringConfigKey("password.chars", "The characters allowed in password");
+
+    private Integer passwordLength;
+    private String acceptableChars;
+
+    public CreatePasswordSensor(Map<String, String> params) {
+        this(ConfigBag.newInstance(params));
+    }
+
+    public CreatePasswordSensor(ConfigBag params) {
+        super(params);
+        passwordLength = params.get(PASSWORD_LENGTH);
+        acceptableChars = params.get(ACCEPTABLE_CHARS);
+    }
+
+    @Override
+    public void apply(EntityLocal entity) {
+        super.apply(entity);
+
+        String password = acceptableChars == null
+                ? Identifiers.makeRandomPassword(passwordLength)
+                : Identifiers.makeRandomPassword(passwordLength, acceptableChars);
+        
+        entity.sensors().set(sensor, password);
+    }
+}
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/sensor/password/CreatePasswordSensorTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/sensor/password/CreatePasswordSensorTest.java
new file mode 100644
index 0000000..0dc1126
--- /dev/null
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/core/sensor/password/CreatePasswordSensorTest.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.sensor.password;
+
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.core.entity.EntityInternal;
+import org.apache.brooklyn.core.sensor.Sensors;
+import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
+import org.apache.brooklyn.core.test.entity.TestEntity;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+public class CreatePasswordSensorTest extends BrooklynAppUnitTestSupport{
+
+    final static AttributeSensor<String> SENSOR_STRING = Sensors.newStringSensor("aString");
+    private EntityInternal entity;
+
+    @BeforeMethod(alwaysRun = true)
+    public void setup() throws Exception {
+        super.setUp();
+
+        entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)
+                .location(app.newLocalhostProvisioningLocation().obtain()));
+        app.start(ImmutableList.<Location>of());
+    }
+
+    @Test
+    public void testCreatePasswordCreatesAPasswordOfDefaultLength() {
+
+        final CreatePasswordSensor sensor = new CreatePasswordSensor(ConfigBag.newInstance()
+                .configure(CreatePasswordSensor.SENSOR_NAME, SENSOR_STRING.getName()));
+        sensor.apply(entity);
+
+        String password = entity.getAttribute(SENSOR_STRING);
+        Asserts.assertEquals(password.length(), 12);
+    }
+}
\ No newline at end of file
diff --git a/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/text/Identifiers.java b/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/text/Identifiers.java
index c2ec4a5..bea6208 100644
--- a/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/text/Identifiers.java
+++ b/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/text/Identifiers.java
@@ -18,16 +18,30 @@
  */
 package org.apache.brooklyn.util.text;
 
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
 import java.util.Random;
+import java.util.Set;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Preconditions;
 
 public class Identifiers {
     
     private static Random random = new Random();
 
-    /** @see #JAVA_GOOD_PACKAGE_OR_CLASS_REGEX */ 
-    public static final String JAVA_GOOD_START_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_";
-    /** @see #JAVA_GOOD_PACKAGE_OR_CLASS_REGEX */ 
-    public static final String JAVA_GOOD_NONSTART_CHARS = JAVA_GOOD_START_CHARS+"1234567890";
+    public static final String UPPER_CASE_ALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+    public static final String LOWER_CASE_ALPHA = "abcdefghijklmnopqrstuvwxyz";
+    public static final String NUMERIC = "1234567890";
+    public static final String NON_ALPHA_NUMERIC = "!@$%^&*()-_=+[]{};:\\|/?,.<>~";
+
+    /** @see #JAVA_GOOD_PACKAGE_OR_CLASS_REGEX */
+    public static final String JAVA_GOOD_START_CHARS = UPPER_CASE_ALPHA + LOWER_CASE_ALPHA +"_";
+    /** @see #JAVA_GOOD_PACKAGE_OR_CLASS_REGEX */
+    public static final String JAVA_GOOD_NONSTART_CHARS = JAVA_GOOD_START_CHARS+NUMERIC;
     /** @see #JAVA_GOOD_PACKAGE_OR_CLASS_REGEX */ 
     public static final String JAVA_GOOD_SEGMENT_REGEX = "["+JAVA_GOOD_START_CHARS+"]"+"["+JAVA_GOOD_NONSTART_CHARS+"]*";
     /** regex for a java package or class name using "good" chars, that is no accents or funny unicodes.
@@ -37,15 +51,21 @@
     public static final String JAVA_GOOD_PACKAGE_OR_CLASS_REGEX = "("+JAVA_GOOD_SEGMENT_REGEX+"\\."+")*"+JAVA_GOOD_SEGMENT_REGEX;
     /** as {@link #JAVA_GOOD_PACKAGE_OR_CLASS_REGEX} but allowing a dollar sign inside a class name (e.g. Foo$1) */
     public static final String JAVA_GOOD_BINARY_REGEX = JAVA_GOOD_PACKAGE_OR_CLASS_REGEX+"(\\$["+JAVA_GOOD_NONSTART_CHARS+"]+)*";
-    
-    public static final String JAVA_GENERATED_IDENTIFIER_START_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
-    public static final String JAVA_GENERATED_IDENTIFIERNONSTART_CHARS = JAVA_GENERATED_IDENTIFIER_START_CHARS+"1234567890";
+
+    public static final String JAVA_GENERATED_IDENTIFIER_START_CHARS = UPPER_CASE_ALPHA + LOWER_CASE_ALPHA;
+
+    public static final String JAVA_GENERATED_IDENTIFIERNONSTART_CHARS = JAVA_GENERATED_IDENTIFIER_START_CHARS+NUMERIC;
 
     public static final String BASE64_VALID_CHARS = JAVA_GENERATED_IDENTIFIERNONSTART_CHARS+"+=";
-    
-    public static final String ID_VALID_START_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
-    public static final String ID_VALID_NONSTART_CHARS = ID_VALID_START_CHARS+"1234567890";
-    
+
+    public static final String ID_VALID_START_CHARS = UPPER_CASE_ALPHA + LOWER_CASE_ALPHA;
+    public static final String ID_VALID_NONSTART_CHARS = ID_VALID_START_CHARS+ NUMERIC;
+
+    public static final String PASSWORD_VALID_CHARS = NON_ALPHA_NUMERIC + ID_VALID_NONSTART_CHARS;
+
+    // We only create a secure random when it is first used
+    private static Random secureRandom = null;
+
     /** makes a random id string (letters and numbers) of the given length;
      * starts with letter (upper or lower) so can be used as java-id;
      * tests ensure random distribution, so random ID of length 5 
@@ -91,15 +111,65 @@
         return new String(id);
     }
 
+    /**
+     *
+     * @param length of password to be returned
+     * @return randomly generated password containing at least one of each upper case,
+     * lower case, numeric, and non alpha-numeric characters.  Hopefully this is acceptible
+     * for most password schemes.
+     */
+    public static String makeRandomPassword(final int length) {
+        return makeRandomPassword(length, UPPER_CASE_ALPHA, LOWER_CASE_ALPHA, NUMERIC, NON_ALPHA_NUMERIC, PASSWORD_VALID_CHARS);
+    }
+
+    /**
+     * A fairly slow but hopefully secure way to randomly select characters for a password
+     * Takes a pool of acceptible characters using the first set in the pool for the first character,
+     * second set for the second character, ..., nth set for all remaining character.
+     *
+     * @param length length of password
+     * @param passwordValidCharsPool pool of acceptable character sets
+     * @return a randomly generated password
+     */
+    public static String makeRandomPassword(final int length, String... passwordValidCharsPool) {
+        Preconditions.checkState(length >= passwordValidCharsPool.length);
+        int l = 0;
+
+        Character[] password = new Character[length];
+
+        for(int i = 0; i < passwordValidCharsPool.length; i++){
+            password[l++] = pickRandomCharFrom(passwordValidCharsPool[i]);
+        }
+
+        String remainingValidChars = mergeCharacterSets(passwordValidCharsPool);
+
+        while(l < length) {
+            password[l++] = pickRandomCharFrom(remainingValidChars);
+        }
+
+        List<Character> list = Arrays.asList(password);
+        Collections.shuffle(list);
+        return Joiner.on("").join(list);
+    }
+
+    protected static String mergeCharacterSets(String... s) {
+        Set characters = new HashSet<Character>();
+        for (String characterSet : s) {
+            characters.addAll(Arrays.asList(characterSet.split("")));
+        }
+
+        return Joiner.on("").join(characters);
+    }
+
     /** creates a short identifier comfortable in java and OS's, given an input hash code
      * <p>
-     * result is always at least of length 1, shorter if the hash is smaller */ 
+     * result is always at least of length 1, shorter if the hash is smaller */
     public static String makeIdFromHash(long d) {
         StringBuffer result = new StringBuffer();
         if (d<0) d=-d;
         // correction for Long.MIN_VALUE
         if (d<0) d=-(d+1000);
-        
+
         result.append(ID_VALID_START_CHARS.charAt((int)(d % (26+26))));
         d /= (26+26);
         while (d!=0) {
@@ -108,31 +178,31 @@
         }
         return result.toString();
     }
-    
+
     /** makes a random id string (letters and numbers) of the given length;
      * starts with letter (upper or lower) so can be used as java-id;
-     * tests ensure random distribution, so random ID of length 5 
-     * is about 2^29 possibilities 
+     * tests ensure random distribution, so random ID of length 5
+     * is about 2^29 possibilities
      * <p>
-     * implementation is efficient, uses char array, and 
+     * implementation is efficient, uses char array, and
      * makes one call to random per 5 chars; makeRandomId(5)
      * takes about 4 times as long as a simple Math.random call,
      * or about 50 times more than a simple x++ instruction;
      * in other words, it's appropriate for contexts where random id's are needed,
-     * but use efficiently (ie cache it per object), and 
+     * but use efficiently (ie cache it per object), and
      * prefer to use a counter where feasible
      **/
     public static String makeRandomJavaId(int l) {
             // copied from Monterey util's com.cloudsoftcorp.util.StringUtils.
             // TODO should share code with makeRandomId, just supplying different char sets (though the char sets in fact are the same..)
 
-            //this version is 30-50% faster than the old double-based one, 
+            //this version is 30-50% faster than the old double-based one,
             //which computed a random every 3 turns --
             //takes about 600 ns to do id of len 10, compared to 10000 ns for old version [on 1.6ghz machine]
             if (l<=0) return "";
             char[] id = new char[l];
             int d = random.nextInt( (26+26) * (26+26+10) * (26+26+10) * (26+26+10) * (26+26+10));
-            int i = 0;    
+            int i = 0;
             id[i] = JAVA_GENERATED_IDENTIFIER_START_CHARS.charAt(d % (26+26));
             d /= (26+26);
             if (++i<l) do {
@@ -151,6 +221,7 @@
     public static double randomDouble() {
         return random.nextDouble();
     }
+
     public static long randomLong() {
         return random.nextLong();
     }
@@ -173,7 +244,6 @@
         byte[] buf = new byte[length];
         return randomBytes(buf);
     }
-
     public static String makeRandomBase64Id(int length) {
         StringBuilder s = new StringBuilder();
         while (length>0) {
@@ -182,6 +252,7 @@
         }
         return s.toString();
     }
+
     public static String getBase64IdFromValue(long value) {
         return getBase64IdFromValue(value, 10);
     }
@@ -210,7 +281,6 @@
             idx = idx >> 6;
         }
     }
-    
     public static boolean isValidToken(String token, String validStartChars, String validSubsequentChars) {
         if (token==null || token.length()==0) return false;
         if (validStartChars.indexOf(token.charAt(0))==-1) return false;
@@ -218,4 +288,15 @@
             if (validSubsequentChars.indexOf(token.charAt(i))==-1) return false;
         return true;
     }
+
+    private static char pickRandomCharFrom(String validChars) {
+        return validChars.charAt(getSecureRandom().nextInt(validChars.length()));
+    }
+
+    private static Random getSecureRandom() {
+        if(secureRandom == null) {
+            secureRandom = new SecureRandom();
+        }
+        return secureRandom;
+    }
 }
diff --git a/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/text/IdentifiersTest.java b/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/text/IdentifiersTest.java
index 7b4f999..e21bb81 100644
--- a/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/text/IdentifiersTest.java
+++ b/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/text/IdentifiersTest.java
@@ -21,8 +21,6 @@
 import java.util.HashSet;
 import java.util.Set;
 
-import org.apache.brooklyn.util.text.Identifiers;
-import org.apache.brooklyn.util.text.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
@@ -98,5 +96,23 @@
         Assert.assertTrue("foo".matches(Identifiers.JAVA_GOOD_BINARY_REGEX));
         Assert.assertTrue("foo.bar.Baz$1".matches(Identifiers.JAVA_GOOD_BINARY_REGEX));
     }
+
+    @Test
+    public void testPassword() {
+        String upper = "ABC";
+        String numbers = "123";
+        String symbols = "!£$";
+        String password = Identifiers.makeRandomPassword(4, upper, numbers, symbols, Identifiers.PASSWORD_VALID_CHARS);
+
+        Assert.assertTrue(password.matches(".*[" + upper + "].*"));
+        Assert.assertTrue(password.matches(".*[" + numbers + "].*"));
+        Assert.assertTrue(password.matches(".*[" + symbols + "].*"));
+    }
+
+    @Test
+    public void testCharMerge() {
+        String characters = Identifiers.mergeCharacterSets("abc", "bcd", "ghjj");
+        Assert.assertEquals(characters.indexOf('b'), characters.lastIndexOf('b'));
+    }
     
 }