乐山源代码附带的LeshanStandalone.java
和LeshanClientExample.java
未设置安全客户端连接。
所以我从SecurityTest.java
和SecureIntegrationTestHelper.java
借用了一些代码来设置服务器和客户端。
我修改过的LeshanStandalone代码:
public class LeshanStandalone {
private static final Logger LOG = LoggerFactory.getLogger(LeshanStandalone.class);
private Server server;
private LeshanServer lwServer;
public void start() throws NonUniqueSecurityInfoException {
// Build LWM2M server
LeshanServerBuilder builder = new LeshanServerBuilder();
builder.setLocalAddress(new InetSocketAddress(InetAddress.getLoopbackAddress(), 5683));
builder.setLocalAddressSecure(new InetSocketAddress(InetAddress.getLoopbackAddress(), 5684));
// Get public and private server key
PrivateKey privateKey = null;
PublicKey publicKey = null;
try {
// Get point values
byte[] publicX = DatatypeConverter
.parseHexBinary("fcc28728c123b155be410fc1c0651da374fc6ebe7f96606e90d927d188894a73");
byte[] publicY = DatatypeConverter
.parseHexBinary("d2ffaa73957d76984633fc1cc54d0b763ca0559a9dff9706e9f4557dacc3f52a");
byte[] privateS = DatatypeConverter
.parseHexBinary("1dae121ba406802ef07c193c1ee4df91115aabd79c1ed7f4c0ef7ef6a5449400");
// Get Elliptic Curve Parameter spec for secp256r1
AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
algoParameters.init(new ECGenParameterSpec("secp256r1"));
ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);
// Create key specs
KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
parameterSpec);
KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec);
// Get keys
publicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
privateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec);
builder.setSecurityRegistry(new SecurityRegistryImpl(privateKey, publicKey));
} catch (InvalidKeySpecException | NoSuchAlgorithmException | InvalidParameterSpecException e) {
LOG.warn("Unable to load RPK.", e);
}
lwServer = builder.build();
lwServer.getSecurityRegistry().add(SecurityInfo.newPreSharedKeyInfo("lwm2mClient", "testclient", DatatypeConverter.parseHexBinary("73656372657450534b")));
lwServer.start();
// Now prepare and start jetty
String webPort = System.getenv("PORT");
if (webPort == null || webPort.isEmpty()) {
webPort = System.getProperty("PORT");
}
if (webPort == null || webPort.isEmpty()) {
webPort = "8080";
}
server = new Server(Integer.valueOf(webPort));
WebAppContext root = new WebAppContext();
root.setContextPath("/");
root.setResourceBase(this.getClass().getClassLoader().getResource("webapp").toExternalForm());
root.setParentLoaderPriority(true);
server.setHandler(root);
// Create Servlet
EventServlet eventServlet = new EventServlet(lwServer);
ServletHolder eventServletHolder = new ServletHolder(eventServlet);
root.addServlet(eventServletHolder, "/event/*");
ServletHolder clientServletHolder = new ServletHolder(new ClientServlet(lwServer));
root.addServlet(clientServletHolder, "/api/clients/*");
ServletHolder securityServletHolder = new ServletHolder(new SecurityServlet(lwServer.getSecurityRegistry()));
root.addServlet(securityServletHolder, "/api/security/*");
ServletHolder objectSpecServletHolder = new ServletHolder(new ObjectSpecServlet());
root.addServlet(objectSpecServletHolder, "/api/objectspecs/*");
// Start jetty
try {
server.start();
} catch (Exception e) {
LOG.error("jetty error", e);
}
}
public void stop() {
try {
lwServer.destroy();
server.stop();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) throws NonUniqueSecurityInfoException {
new LeshanStandalone().start();
}
}
我修改过的LeshanClientExample.java:
public class LeshanClientExample {
private String registrationID;
public static void main(final String[] args) throws Exception {
if (args.length != 4 && args.length != 2) {
System.out
.println("Usage:\njava -jar target/leshan-client-example-*-SNAPSHOT-jar-with-dependencies.jar [ClientIP] [ClientPort] ServerIP ServerPort");
} else {
if (args.length == 4)
new LeshanClientExample(args[0], Integer.parseInt(args[1]), args[2], Integer.parseInt(args[3]));
else
new LeshanClientExample("0", 0, args[0], Integer.parseInt(args[1]));
}
}
public LeshanClientExample(final String localHostName, final int localPort, final String serverHostName,
final int serverPort) throws Exception {
// Initialize object list
ObjectsInitializer initializer = new ObjectsInitializer();
initializer.setClassForObject(3, Device.class);
List<ObjectEnabler> enablers = initializer.createMandatory();
// Create client
final InetSocketAddress clientAddress = new InetSocketAddress(localHostName, localPort);
final InetSocketAddress serverAddress = new InetSocketAddress(serverHostName, serverPort);
CoapServer serverLocal = new CoapServer();
// Get point values
byte[] publicX = DatatypeConverter
.parseHexBinary("fcc28728c123b155be410fc1c0651da374fc6ebe7f96606e90d927d188894a73");
byte[] publicY = DatatypeConverter
.parseHexBinary("d2ffaa73957d76984633fc1cc54d0b763ca0559a9dff9706e9f4557dacc3f52a");
byte[] privateS = DatatypeConverter
.parseHexBinary("1dae121ba406802ef07c193c1ee4df91115aabd79c1ed7f4c0ef7ef6a5449400");
// Get Elliptic Curve Parameter spec for secp256r1
AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
algoParameters.init(new ECGenParameterSpec("secp256r1"));
ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);
// Create key specs
KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
parameterSpec);
KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec);
// Get keys
PublicKey serverPublicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
PrivateKey serverPrivateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec);
DTLSConnector dtlsConnector = new DTLSConnector(serverAddress);
dtlsConnector.getConfig().setPreferredCipherSuite(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8);
dtlsConnector.getConfig().setPskStore(new StaticPskStore("testclient", DatatypeConverter.parseHexBinary("73656372657450534b")));
dtlsConnector.getConfig().setPrivateKey(serverPrivateKey, serverPublicKey);
dtlsConnector.getConfig().setRequireClientAuth(true);
serverLocal.addEndpoint(new CoAPEndpoint(dtlsConnector, NetworkConfig.getStandard()));
final LeshanClient client = new LeshanClient(clientAddress, serverAddress, serverLocal, new ArrayList<LwM2mObjectEnabler>(
enablers));
// Start the client
client.start();
// Register to the server provided
RegisterResponse response = client.send(new RegisterRequest("lwm2mClient"));
// Report registration response.
System.out.println("Device Registration (Success? " + response.getCode() + ")");
if (response.getCode() == ResponseCode.CREATED) {
System.out.println("\tDevice: Registered Client Location '" + response.getRegistrationID() + "'");
registrationID = response.getRegistrationID();
} else {
// TODO Should we have a error message on response ?
// System.err.println("\tDevice Registration Error: " + response.getErrorMessage());
System.err.println("\tDevice Registration Error: " + response.getCode());
System.err
.println("If you're having issues connecting to the LWM2M endpoint, try using the DTLS port instead");
}
// Deregister on shutdown and stop client.
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
if (registrationID != null) {
System.out.println("\tDevice: Deregistering Client '" + registrationID + "'");
client.send(new DeregisterRequest(registrationID));
client.stop();
}
}
});
}
public static class Device extends BaseInstanceEnabler {
public Device() {
// notify new date each 5 second
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
fireResourceChange(13);
}
}, 5000, 5000);
}
@Override
public ValueResponse read(int resourceid) {
System.out.println("Read on resource " + resourceid);
switch (resourceid) {
case 0:
return new ValueResponse(ResponseCode.CONTENT, new LwM2mResource(resourceid,
Value.newStringValue(getManufacturer())));
case 1:
return new ValueResponse(ResponseCode.CONTENT, new LwM2mResource(resourceid,
Value.newStringValue(getModelNumber())));
case 2:
return new ValueResponse(ResponseCode.CONTENT, new LwM2mResource(resourceid,
Value.newStringValue(getSerialNumber())));
case 3:
return new ValueResponse(ResponseCode.CONTENT, new LwM2mResource(resourceid,
Value.newStringValue(getFirmwareVersion())));
case 9:
return new ValueResponse(ResponseCode.CONTENT, new LwM2mResource(resourceid,
Value.newIntegerValue(getBatteryLevel())));
case 10:
return new ValueResponse(ResponseCode.CONTENT, new LwM2mResource(resourceid,
Value.newIntegerValue(getMemoryFree())));
case 13:
return new ValueResponse(ResponseCode.CONTENT, new LwM2mResource(resourceid,
Value.newDateValue(getCurrentTime())));
case 14:
return new ValueResponse(ResponseCode.CONTENT, new LwM2mResource(resourceid,
Value.newStringValue(getUtcOffset())));
case 15:
return new ValueResponse(ResponseCode.CONTENT, new LwM2mResource(resourceid,
Value.newStringValue(getTimezone())));
case 16:
return new ValueResponse(ResponseCode.CONTENT, new LwM2mResource(resourceid,
Value.newStringValue(getSupportedBinding())));
default:
return super.read(resourceid);
}
}
@Override
public LwM2mResponse execute(int resourceid, byte[] params) {
System.out.println("Execute on resource " + resourceid + " params " + params);
return new LwM2mResponse(ResponseCode.CHANGED);
}
@Override
public LwM2mResponse write(int resourceid, LwM2mResource value) {
System.out.println("Write on resource " + resourceid + " value " + value);
switch (resourceid) {
case 13:
return new LwM2mResponse(ResponseCode.NOT_FOUND);
case 14:
setUtcOffset((String) value.getValue().value);
fireResourceChange(resourceid);
return new LwM2mResponse(ResponseCode.CHANGED);
case 15:
setTimezone((String) value.getValue().value);
fireResourceChange(resourceid);
return new LwM2mResponse(ResponseCode.CHANGED);
default:
return super.write(resourceid, value);
}
}
private String getManufacturer() {
return "Leshan Example Device";
}
private String getModelNumber() {
return "Model 500";
}
private String getSerialNumber() {
return "LT-500-000-0001";
}
private String getFirmwareVersion() {
return "1.0.0";
}
private int getBatteryLevel() {
final Random rand = new Random();
return rand.nextInt(100);
}
private int getMemoryFree() {
final Random rand = new Random();
return rand.nextInt(50) + 114;
}
private Date getCurrentTime() {
return new Date();
}
private String utcOffset = new SimpleDateFormat("X").format(Calendar.getInstance().getTime());
;
private String getUtcOffset() {
return utcOffset;
}
private void setUtcOffset(String t) {
utcOffset = t;
}
private String timeZone = TimeZone.getDefault().getID();
private String getTimezone() {
return timeZone;
}
private void setTimezone(String t) {
timeZone = t;
}
private String getSupportedBinding() {
return "U";
}
}
}
服务器启动正常,但客户端在5684上以RequestTimeoutException
启动,服务器打印:
May 19, 2015 2:47:04 PM org.eclipse.californium.scandium.dtls.Record fromByteArray
WARNING: Received illegal record content type: 68
May 19, 2015 2:47:07 PM org.eclipse.californium.scandium.dtls.Record fromByteArray
WARNING: Received illegal record content type: 68
我做错了什么或错过了什么?