136 | * As a special case, if the input array consists entirely of null bytes,
137 | * then an array with a single null element will be returned.
138 | */
139 | private byte[] removeLeadingNullBytes(byte[] inArray) {
140 | int offset = 0;
141 | while (offset < inArray.length & inArray[offset] == 0) {
142 | offset += 1;
143 | }
144 | byte[] result = new byte[inArray.length - offset];
145 | for (int i=offset; i < inArray.length; i++) {
146 | result[i - offset] = inArray[i];
147 | }
148 | return result;
149 | }
150 |
151 | private void logDebug(String message) {
152 | System.out.println(message);
153 | }
154 |
155 | private void logVerbose(String message) {
156 | System.out.println(message);
157 | }
158 |
159 | public static interface DebugLogger {
160 | /**
161 | * Logs debugging information from challenge-response generation.
162 | */
163 | public void debug(String message);
164 |
165 | /**
166 | * Logs verbose debugging information from challenge-response generation.
167 | */
168 | public void verbose(String message);
169 |
170 | }
171 |
172 | }
173 |
174 |
--------------------------------------------------------------------------------
/src/main/java/com/kunal52/pairing/PairingListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2009 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.kunal52.pairing;
18 |
19 |
20 | import com.kunal52.exception.PairingException;
21 |
22 | /**
23 | * Listener interface for handling events within a pairing session.
24 | */
25 | public interface PairingListener {
26 |
27 |
28 | void onSessionCreated();
29 |
30 |
31 | void onPerformInputDeviceRole() throws PairingException;
32 |
33 |
34 | void onPerformOutputDeviceRole(byte[] gamma)
35 | throws PairingException;
36 |
37 | void onSecretRequested();
38 |
39 | void onSessionEnded();
40 |
41 |
42 | void onError(String message);
43 |
44 | void onPaired();
45 |
46 | void onLog(String message);
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/com/kunal52/pairing/PairingMessageManager.java:
--------------------------------------------------------------------------------
1 | package com.kunal52.pairing;
2 |
3 | import com.google.protobuf.ByteString;
4 | import com.kunal52.wire.MessageManager;
5 |
6 | import java.nio.ByteBuffer;
7 | import java.util.Collections;
8 |
9 | public class PairingMessageManager extends MessageManager {
10 | public byte[] createPairingMessage(String clientName, String serviceName) {
11 | Pairingmessage.PairingMessage.Builder pairingMessage = Pairingmessage.PairingMessage.newBuilder()
12 | .setPairingRequest(Pairingmessage.PairingRequest.newBuilder()
13 | .setClientName(clientName)
14 | .setServiceName(serviceName))
15 | .setStatus(Pairingmessage.PairingMessage.Status.STATUS_OK)
16 | .setProtocolVersion(2);
17 | byte[] pairingMessageByteArray = pairingMessage.build().toByteArray();
18 | return addLengthAndCreate(pairingMessageByteArray);
19 | }
20 |
21 | public byte[] createPairingOption() {
22 | Pairingmessage.PairingEncoding pairingEncoding = Pairingmessage.PairingEncoding.newBuilder()
23 | .setType(Pairingmessage.PairingEncoding.EncodingType.ENCODING_TYPE_HEXADECIMAL)
24 | .setSymbolLength(6).build();
25 |
26 | Pairingmessage.PairingMessage.Builder pairingOption = Pairingmessage.PairingMessage.newBuilder()
27 | .setPairingOption(Pairingmessage.PairingOption.newBuilder()
28 | .setPreferredRole(Pairingmessage.RoleType.ROLE_TYPE_INPUT)
29 | .addInputEncodings(pairingEncoding))
30 | .setStatus(Pairingmessage.PairingMessage.Status.STATUS_OK)
31 | .setProtocolVersion(2);
32 |
33 | byte[] pairingMessageByteArray = pairingOption.build().toByteArray();
34 | return addLengthAndCreate(pairingMessageByteArray);
35 | }
36 |
37 |
38 | public byte[] createConfigMessage() {
39 | Pairingmessage.PairingEncoding pairingEncoding = Pairingmessage.PairingEncoding.newBuilder()
40 | .setType(Pairingmessage.PairingEncoding.EncodingType.ENCODING_TYPE_HEXADECIMAL)
41 | .setSymbolLength(6).build();
42 |
43 | Pairingmessage.PairingMessage.Builder pairingConfig = Pairingmessage.PairingMessage.newBuilder()
44 | .setPairingConfiguration(Pairingmessage.PairingConfiguration.newBuilder()
45 | .setClientRole(Pairingmessage.RoleType.ROLE_TYPE_INPUT)
46 | .setEncoding(pairingEncoding).build())
47 | .setStatus(Pairingmessage.PairingMessage.Status.STATUS_OK)
48 | .setProtocolVersion(2);
49 |
50 | byte[] pairingMessageByteArray = pairingConfig.build().toByteArray();
51 | return addLengthAndCreate(pairingMessageByteArray);
52 | }
53 |
54 | public byte[] createSecretMessage(Pairingmessage.PairingMessage pairingSecretMessage) {
55 | byte[] pairingMessageByteArray = pairingSecretMessage.toByteArray();
56 | return addLengthAndCreate(pairingMessageByteArray);
57 | }
58 |
59 | public Pairingmessage.PairingMessage createSecretMessageProto(byte[] secret) {
60 |
61 | return Pairingmessage.PairingMessage.newBuilder()
62 | .setPairingSecret(Pairingmessage.PairingSecret.newBuilder().setSecret(ByteString.copyFrom(secret)).build())
63 | .setStatus(Pairingmessage.PairingMessage.Status.STATUS_OK)
64 | .setProtocolVersion(2).build();
65 |
66 | }
67 |
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/com/kunal52/pairing/PairingPacketParser.java:
--------------------------------------------------------------------------------
1 | package com.kunal52.pairing;
2 |
3 | import com.google.protobuf.InvalidProtocolBufferException;
4 | import com.kunal52.wire.PacketParser;
5 |
6 | import java.io.IOException;
7 | import java.io.InputStream;
8 | import java.util.Arrays;
9 | import java.util.concurrent.BlockingQueue;
10 |
11 | public class PairingPacketParser extends PacketParser {
12 |
13 | private BlockingQueue mMessagesQueue;
14 |
15 | public PairingPacketParser(InputStream inputStream, BlockingQueue messagesQueue) {
16 | super(inputStream);
17 | mMessagesQueue = messagesQueue;
18 | }
19 |
20 | @Override
21 | public void messageBufferReceived(byte[] buf) {
22 | try {
23 | Pairingmessage.PairingMessage pairingMessage = Pairingmessage.PairingMessage.parseFrom(buf);
24 | if (pairingMessage.getStatus() == Pairingmessage.PairingMessage.Status.STATUS_OK) {
25 | mMessagesQueue.put(pairingMessage);
26 | }
27 | } catch (InvalidProtocolBufferException | InterruptedException e) {
28 | throw new RuntimeException(e);
29 | }
30 | }
31 |
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/kunal52/pairing/PairingSession.java:
--------------------------------------------------------------------------------
1 | package com.kunal52.pairing;
2 |
3 | import com.kunal52.AndroidRemoteContext;
4 | import com.kunal52.util.Utils;
5 | import com.kunal52.exception.PairingException;
6 | import com.kunal52.ssl.DummyTrustManager;
7 | import com.kunal52.ssl.KeyStoreManager;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 |
11 | import javax.net.ssl.SSLContext;
12 | import javax.net.ssl.SSLSocket;
13 | import javax.net.ssl.SSLSocketFactory;
14 | import javax.net.ssl.TrustManager;
15 | import java.io.IOException;
16 | import java.io.OutputStream;
17 | import java.security.GeneralSecurityException;
18 | import java.security.SecureRandom;
19 | import java.util.ArrayList;
20 | import java.util.Arrays;
21 | import java.util.List;
22 | import java.util.concurrent.BlockingQueue;
23 | import java.util.concurrent.LinkedBlockingDeque;
24 |
25 | public class PairingSession {
26 |
27 | private final Logger logger = LoggerFactory.getLogger(PairingSession.class);
28 |
29 | private final BlockingQueue mMessagesQueue;
30 |
31 | private final PairingMessageManager mPairingMessageManager;
32 |
33 | SecretProvider secretProvider;
34 |
35 | private SSLSocket mSslSocket;
36 |
37 | public PairingSession() {
38 | mMessagesQueue = new LinkedBlockingDeque<>();
39 | mPairingMessageManager = new PairingMessageManager();
40 | }
41 |
42 | public void pair(String host, int port, PairingListener pairingListener) throws GeneralSecurityException, IOException, InterruptedException, PairingException {
43 |
44 | SSLContext sSLContext = SSLContext.getInstance("TLS");
45 | sSLContext.init(new KeyStoreManager().getKeyManagers(), new TrustManager[]{new DummyTrustManager()}, new SecureRandom());
46 | SSLSocketFactory sslsocketfactory = sSLContext.getSocketFactory();
47 | SSLSocket sSLSocket = (SSLSocket) sslsocketfactory.createSocket(host, port);
48 | mSslSocket = sSLSocket;
49 | // sSLSocket.setNeedClientAuth(true);
50 | // sSLSocket.setUseClientMode(true);
51 | // sSLSocket.setKeepAlive(true);
52 | // sSLSocket.setTcpNoDelay(true);
53 | // sSLSocket.startHandshake();
54 |
55 | pairingListener.onSessionCreated();
56 | PairingPacketParser pairingPacketParser = new PairingPacketParser(sSLSocket.getInputStream(), mMessagesQueue);
57 | pairingPacketParser.start();
58 |
59 | final OutputStream outputStream = sSLSocket.getOutputStream();
60 |
61 | byte[] pairingMessage = mPairingMessageManager.createPairingMessage(AndroidRemoteContext.getInstance().getClientName(), AndroidRemoteContext.getInstance().getServiceName());
62 | outputStream.write(pairingMessage);
63 | Pairingmessage.PairingMessage pairingMessageResponse = waitForMessage();
64 | logReceivedMessage(pairingMessageResponse.toString());
65 |
66 | byte[] pairingOption = new PairingMessageManager().createPairingOption();
67 | outputStream.write(pairingOption);
68 | Pairingmessage.PairingMessage pairingOptionAck = waitForMessage();
69 | logReceivedMessage(pairingOptionAck.toString());
70 |
71 | byte[] configMessage = new PairingMessageManager().createConfigMessage();
72 | outputStream.write(configMessage);
73 | Pairingmessage.PairingMessage pairingConfigAck = waitForMessage();
74 | logReceivedMessage(pairingConfigAck.toString());
75 |
76 | if (secretProvider != null)
77 | secretProvider.requestSecret(this);
78 | pairingListener.onSecretRequested();
79 | logger.info("Waiting for secret");
80 | Pairingmessage.PairingMessage pairingSecretMessage = waitForMessage();
81 | byte[] secretMessage = mPairingMessageManager.createSecretMessage(pairingSecretMessage);
82 | outputStream.write(secretMessage);
83 | Pairingmessage.PairingMessage pairingSecretAck = waitForMessage();
84 | logReceivedMessage(pairingSecretAck.toString());
85 |
86 | pairingListener.onPaired();
87 | pairingListener.onSessionEnded();
88 |
89 | }
90 |
91 |
92 | Pairingmessage.PairingMessage waitForMessage() throws InterruptedException, PairingException {
93 | Pairingmessage.PairingMessage pairingMessage = mMessagesQueue.take();
94 | if (pairingMessage.getStatus() != Pairingmessage.PairingMessage.Status.STATUS_OK) {
95 | throw new PairingException(pairingMessage.toString());
96 | }
97 | return pairingMessage;
98 | }
99 |
100 |
101 | public void provideSecret(String secret) {
102 | createCodeSecret(secret);
103 | }
104 |
105 | private void createCodeSecret(String code) {
106 | code = code.substring(2);
107 | PairingChallengeResponse pairingChallengeResponse = new PairingChallengeResponse(Utils.getLocalCert(mSslSocket.getSession()), Utils.getPeerCert(mSslSocket.getSession()));
108 | byte[] secret = Utils.hexStringToBytes(code);
109 | System.out.println(Arrays.toString(secret));
110 | try {
111 | pairingChallengeResponse.checkGamma(secret);
112 | } catch (PairingException e) {
113 | throw new RuntimeException(e);
114 | }
115 | byte[] pairingChallengeResponseAlpha;
116 | try {
117 | pairingChallengeResponseAlpha = pairingChallengeResponse.getAlpha(secret);
118 | } catch (PairingException e) {
119 | throw new RuntimeException(e);
120 | }
121 | Pairingmessage.PairingMessage secretMessageProto = new PairingMessageManager().createSecretMessageProto(pairingChallengeResponseAlpha);
122 | try {
123 | mMessagesQueue.put(secretMessageProto);
124 | } catch (InterruptedException e) {
125 | throw new RuntimeException(e);
126 | }
127 | }
128 |
129 | void logSendMessage(String message) {
130 | logger.info("Send Message : {}", message);
131 | }
132 |
133 | void logReceivedMessage(String message) {
134 | logger.info("Received Message : {}", message);
135 | }
136 |
137 | }
138 |
--------------------------------------------------------------------------------
/src/main/java/com/kunal52/pairing/SecretProvider.java:
--------------------------------------------------------------------------------
1 | package com.kunal52.pairing;
2 |
3 | interface SecretProvider {
4 |
5 | void requestSecret(PairingSession pairingSession);
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/com/kunal52/remote/RemoteListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2009 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.kunal52.remote;
18 |
19 |
20 | import com.kunal52.exception.PairingException;
21 |
22 | /**
23 | * Listener interface for handling events within a pairing session.
24 | */
25 | public interface RemoteListener {
26 |
27 |
28 | void onConnected();
29 |
30 | void onDisconnected();
31 |
32 | void onVolume();
33 |
34 | void onPerformInputDeviceRole() throws PairingException;
35 |
36 | void onPerformOutputDeviceRole(byte[] gamma)
37 | throws PairingException;
38 |
39 |
40 | void onSessionEnded();
41 |
42 |
43 | void onError(String message);
44 |
45 | void onLog(String message);
46 |
47 | void sSLException();
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/com/kunal52/remote/RemoteMessageManager.java:
--------------------------------------------------------------------------------
1 | package com.kunal52.remote;
2 |
3 | import com.google.protobuf.ByteString;
4 | import com.kunal52.pairing.Pairingmessage;
5 | import com.kunal52.wire.MessageManager;
6 |
7 | import java.nio.ByteBuffer;
8 |
9 | public class RemoteMessageManager extends MessageManager {
10 |
11 | public byte[] createRemoteConfigure(int code, String model, String vendor, int unknown1, String unknown2) {
12 | Remotemessage.RemoteConfigure remoteConfigure = Remotemessage.RemoteConfigure.newBuilder()
13 | .setCode1(code)
14 | .setDeviceInfo(Remotemessage.RemoteDeviceInfo.newBuilder()
15 | .setModel(model)
16 | .setVendor(vendor)
17 | .setUnknown1(unknown1)
18 | .setUnknown2(unknown2)
19 | .setPackageName("androidtv-remote")
20 | .setAppVersion("1.0.0")
21 | .build())
22 | .build();
23 |
24 | return createRemoteConfigure(remoteConfigure);
25 | }
26 |
27 | public byte[] createRemoteConfigure(Remotemessage.RemoteConfigure remoteConfigure) {
28 |
29 | Remotemessage.RemoteMessage remoteMessage = Remotemessage.RemoteMessage.newBuilder()
30 | .setRemoteConfigure(remoteConfigure)
31 | .build();
32 | byte[] pairingMessageByteArray = remoteMessage.toByteArray();
33 | return addLengthAndCreate(pairingMessageByteArray);
34 | }
35 |
36 | public byte[] createRemoteActive(int code) {
37 |
38 | Remotemessage.RemoteMessage remoteMessage = Remotemessage.RemoteMessage.newBuilder()
39 | .setRemoteSetActive(Remotemessage.RemoteSetActive.newBuilder().setActive(code).build())
40 | .build();
41 | byte[] pairingMessageByteArray = remoteMessage.toByteArray();
42 | return addLengthAndCreate(pairingMessageByteArray);
43 | }
44 |
45 |
46 | public byte[] createPingResponse(int val1) {
47 | Remotemessage.RemotePingResponse remotePingResponse = Remotemessage.RemotePingResponse.newBuilder().setVal1(val1).build();
48 | Remotemessage.RemoteMessage remoteMessage = Remotemessage.RemoteMessage.newBuilder().setRemotePingResponse(remotePingResponse).build();
49 | byte[] pairingMessageByteArray = remoteMessage.toByteArray();
50 | return addLengthAndCreate(pairingMessageByteArray);
51 | }
52 |
53 | public byte[] createPower() {
54 | Remotemessage.RemoteKeyInject remoteKeyInject = Remotemessage.RemoteKeyInject.newBuilder().setDirection(Remotemessage.RemoteDirection.SHORT).setKeyCode(Remotemessage.RemoteKeyCode.KEYCODE_POWER).build();
55 | Remotemessage.RemoteMessage remoteMessage = Remotemessage.RemoteMessage.newBuilder().setRemoteKeyInject(remoteKeyInject).build();
56 | byte[] pairingMessageByteArray = remoteMessage.toByteArray();
57 | int length = pairingMessageByteArray.length;
58 | mPacketBuffer.put((byte) length).put(pairingMessageByteArray);
59 | byte[] bArr = new byte[mPacketBuffer.position()];
60 | System.arraycopy(mPacketBuffer.array(), mPacketBuffer.arrayOffset(), bArr, 0, mPacketBuffer.position());
61 | mPacketBuffer.clear();
62 | return bArr;
63 | }
64 |
65 | public byte[] createVolumeLevel(int volume) {
66 | // Remotemessage.RemoteKeyInject remoteKeyInject = Remotemessage.RemoteKeyInject.newBuilder().setDirection(Remotemessage.RemoteDirection.SHORT).setKeyCode(Remotemessage.RemoteKeyCode.KEYCODE_POWER).build();
67 | Remotemessage.RemoteMessage remoteMessage = Remotemessage.RemoteMessage.newBuilder().setRemoteAdjustVolumeLevel(Remotemessage.RemoteAdjustVolumeLevel.newBuilder().build()).build();
68 | byte[] pairingMessageByteArray = remoteMessage.toByteArray();
69 | int length = pairingMessageByteArray.length;
70 | mPacketBuffer.put((byte) length).put(pairingMessageByteArray);
71 | byte[] bArr = new byte[mPacketBuffer.position()];
72 | System.arraycopy(mPacketBuffer.array(), mPacketBuffer.arrayOffset(), bArr, 0, mPacketBuffer.position());
73 | mPacketBuffer.clear();
74 | return bArr;
75 | }
76 |
77 | public byte[] createKeyCommand(Remotemessage.RemoteKeyCode keyCode, Remotemessage.RemoteDirection remoteDirection) {
78 | Remotemessage.RemoteMessage remoteMessage = Remotemessage.RemoteMessage.newBuilder()
79 | .setRemoteKeyInject(Remotemessage.RemoteKeyInject.newBuilder()
80 | .setKeyCode(keyCode)
81 | .setDirection(remoteDirection)
82 | .build())
83 | .build();
84 |
85 | return addLengthAndCreate(remoteMessage.toByteArray());
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/src/main/java/com/kunal52/remote/RemotePacketParser.java:
--------------------------------------------------------------------------------
1 | package com.kunal52.remote;
2 |
3 | import com.google.protobuf.InvalidProtocolBufferException;
4 | import com.kunal52.wire.PacketParser;
5 |
6 | import java.io.IOException;
7 | import java.io.InputStream;
8 | import java.io.OutputStream;
9 | import java.util.Arrays;
10 | import java.util.concurrent.BlockingQueue;
11 |
12 | public class RemotePacketParser extends PacketParser {
13 |
14 | BlockingQueue mMessageQueue;
15 | private final OutputStream mOutputStream;
16 | private final RemoteMessageManager remoteMessageManager;
17 |
18 | private final RemoteListener mRemoteListener;
19 |
20 | private boolean isConnected = false;
21 |
22 | public RemotePacketParser(InputStream inputStream, OutputStream outputStream, BlockingQueue messageQueue, RemoteListener remoteListener) {
23 | super(inputStream);
24 | mOutputStream = outputStream;
25 | remoteMessageManager = new RemoteMessageManager();
26 | mRemoteListener = remoteListener;
27 | mMessageQueue = messageQueue;
28 | }
29 |
30 | @Override
31 | public void messageBufferReceived(byte[] buf) {
32 | System.out.println(Arrays.toString(buf));
33 | Remotemessage.RemoteMessage remoteMessage;
34 | try {
35 | remoteMessage = Remotemessage.RemoteMessage.parseFrom(buf);
36 | } catch (InvalidProtocolBufferException e) {
37 | throw new RuntimeException(e);
38 | }
39 | //Send Ping Response
40 | if (remoteMessage.hasRemotePingRequest()) {
41 | try {
42 | mOutputStream.write(remoteMessageManager.createPingResponse(remoteMessage.getRemotePingRequest().getVal1()));
43 | } catch (IOException e) {
44 | throw new RuntimeException(e);
45 | }
46 | } else if (remoteMessage.hasRemoteStart()) {
47 | if (!isConnected)
48 | mRemoteListener.onConnected();
49 | isConnected = true;
50 | } else {
51 | try {
52 | mMessageQueue.put(remoteMessage);
53 | } catch (InterruptedException e) {
54 | throw new RuntimeException(e);
55 | }
56 | }
57 |
58 |
59 | System.out.println(remoteMessage);
60 |
61 | }
62 |
63 |
64 | public interface RemotePacketParserListener {
65 |
66 | void onConnected();
67 |
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/com/kunal52/remote/RemoteSession.java:
--------------------------------------------------------------------------------
1 | package com.kunal52.remote;
2 |
3 | import com.kunal52.ssl.KeyStoreManager;
4 | import com.kunal52.exception.PairingException;
5 | import com.kunal52.ssl.DummyTrustManager;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 |
9 | import javax.net.ssl.*;
10 | import java.io.IOException;
11 | import java.io.OutputStream;
12 | import java.security.GeneralSecurityException;
13 | import java.security.SecureRandom;
14 | import java.util.concurrent.BlockingQueue;
15 | import java.util.concurrent.LinkedBlockingQueue;
16 |
17 | public class RemoteSession {
18 |
19 | private final Logger logger = LoggerFactory.getLogger(RemoteSession.class);
20 |
21 | private final BlockingQueue mMessageQueue;
22 |
23 | private static final int SECRET_POLL_TIMEOUT_MS = 500;
24 |
25 | private static RemoteMessageManager mMessageManager;
26 |
27 | private final String mHost;
28 |
29 | private final int mPort;
30 |
31 | private final RemoteSessionListener mRemoteSessionListener;
32 |
33 | int retry;
34 |
35 | OutputStream outputStream;
36 |
37 | public RemoteSession(String host, int port, RemoteSessionListener remoteSessionListener) {
38 | mMessageQueue = new LinkedBlockingQueue<>();
39 | mMessageManager = new RemoteMessageManager();
40 | mHost = host;
41 | mPort = port;
42 | mRemoteSessionListener = remoteSessionListener;
43 | }
44 |
45 | public void connect() throws GeneralSecurityException, IOException, InterruptedException, PairingException {
46 |
47 | try {
48 | SSLContext sSLContext = SSLContext.getInstance("TLS");
49 | sSLContext.init(new KeyStoreManager().getKeyManagers(), new TrustManager[]{new DummyTrustManager()}, new SecureRandom());
50 | SSLSocketFactory sslsocketfactory = sSLContext.getSocketFactory();
51 | SSLSocket sSLSocket = (SSLSocket) sslsocketfactory.createSocket(mHost, mPort);
52 | sSLSocket.setNeedClientAuth(true);
53 | sSLSocket.setUseClientMode(true);
54 | sSLSocket.setKeepAlive(true);
55 | sSLSocket.setTcpNoDelay(true);
56 | sSLSocket.startHandshake();
57 |
58 | outputStream = sSLSocket.getOutputStream();
59 | new RemotePacketParser(sSLSocket.getInputStream(), outputStream, mMessageQueue, new RemoteListener() {
60 | @Override
61 | public void onConnected() {
62 | mRemoteSessionListener.onConnected();
63 | }
64 |
65 | @Override
66 | public void onDisconnected() {
67 |
68 | }
69 |
70 | @Override
71 | public void onVolume() {
72 |
73 | }
74 |
75 | @Override
76 | public void onPerformInputDeviceRole() throws PairingException {
77 |
78 | }
79 |
80 | @Override
81 | public void onPerformOutputDeviceRole(byte[] gamma) throws PairingException {
82 |
83 | }
84 |
85 | @Override
86 | public void onSessionEnded() {
87 |
88 | }
89 |
90 | @Override
91 | public void onError(String message) {
92 |
93 | }
94 |
95 | @Override
96 | public void onLog(String message) {
97 |
98 | }
99 |
100 | @Override
101 | public void sSLException() {
102 |
103 | }
104 | }).start();
105 |
106 | Remotemessage.RemoteMessage remoteMessage = waitForMessage();
107 | logger.info(remoteMessage.toString());
108 |
109 | byte[] remoteConfigure = mMessageManager.createRemoteConfigure(622, "ROG Strix G531GT_G531GT", "ASUSTeK COMPUTER INC.", 1, "1");
110 |
111 | outputStream.write(remoteConfigure);
112 |
113 | waitForMessage();
114 |
115 | byte[] remoteActive = mMessageManager.createRemoteActive(622);
116 | outputStream.write(remoteActive);
117 | } catch (SSLException sslException) {
118 | mRemoteSessionListener.onSslError();
119 | } catch (Exception e) {
120 | e.printStackTrace();
121 | mRemoteSessionListener.onError(e.getMessage());
122 | }
123 | }
124 |
125 | Remotemessage.RemoteMessage waitForMessage() throws InterruptedException, PairingException {
126 | return mMessageQueue.take();
127 | }
128 |
129 | public void attemptToReconnect() {
130 | retry++;
131 | try {
132 | connect();
133 | } catch (GeneralSecurityException | IOException | InterruptedException | PairingException e) {
134 | mRemoteSessionListener.onError(e.getMessage());
135 | throw new RuntimeException(e);
136 | }
137 | }
138 |
139 |
140 | public void sendCommand(Remotemessage.RemoteKeyCode remoteKeyCode, Remotemessage.RemoteDirection remoteDirection) {
141 | try {
142 | outputStream.write(mMessageManager.createKeyCommand(remoteKeyCode,remoteDirection));
143 | } catch (IOException e) {
144 | throw new RuntimeException(e);
145 | }
146 | }
147 |
148 | public interface RemoteSessionListener {
149 | void onConnected();
150 |
151 | void onSslError() throws GeneralSecurityException, IOException, InterruptedException, PairingException;
152 |
153 | void onDisconnected();
154 |
155 | void onError(String message);
156 | }
157 |
158 | }
159 |
--------------------------------------------------------------------------------
/src/main/java/com/kunal52/ssl/DummySSLServerSocketFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2009 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.kunal52.ssl;
18 |
19 | import java.security.KeyManagementException;
20 | import java.security.NoSuchAlgorithmException;
21 |
22 | import javax.net.ssl.KeyManager;
23 | import javax.net.ssl.SSLServerSocketFactory;
24 | import javax.net.ssl.TrustManager;
25 |
26 |
27 | /**
28 | * An {@link SSLServerSocketFactory} that performs no verification on client
29 | * certificates; ie, is all-trusting.
30 | *
31 | * @see DummyTrustManager
32 | */
33 | public class DummySSLServerSocketFactory extends SSLServerSocketFactoryWrapper {
34 |
35 | DummySSLServerSocketFactory(KeyManager[] keyManagers,
36 | TrustManager[] trustManagers) throws KeyManagementException,
37 | NoSuchAlgorithmException {
38 | super(keyManagers, trustManagers);
39 | }
40 |
41 | public static DummySSLServerSocketFactory fromKeyManagers(
42 | KeyManager[] keyManagers) throws KeyManagementException,
43 | NoSuchAlgorithmException {
44 | TrustManager[] trustManagers = { new DummyTrustManager() };
45 | return new DummySSLServerSocketFactory(keyManagers, trustManagers);
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/com/kunal52/ssl/DummySSLSocketFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2009 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.kunal52.ssl;
18 |
19 | import java.security.KeyManagementException;
20 | import java.security.NoSuchAlgorithmException;
21 |
22 | import javax.net.ssl.KeyManager;
23 | import javax.net.ssl.SSLSocketFactory;
24 | import javax.net.ssl.TrustManager;
25 |
26 |
27 | /**
28 | * An {@link SSLSocketFactory} that performs no verification on server
29 | * certificates; ie, is all-trusting.
30 | *
31 | * @see DummyTrustManager
32 | */
33 | public class DummySSLSocketFactory extends SSLSocketFactoryWrapper {
34 |
35 | DummySSLSocketFactory(KeyManager[] keyManagers,
36 | TrustManager[] trustManagers) throws KeyManagementException,
37 | NoSuchAlgorithmException {
38 | super(keyManagers, trustManagers);
39 | }
40 |
41 | public static DummySSLSocketFactory fromKeyManagers(KeyManager[] keyManagers)
42 | throws KeyManagementException, NoSuchAlgorithmException {
43 | TrustManager[] trustManagers = { new DummyTrustManager() };
44 | return new DummySSLSocketFactory(keyManagers, trustManagers);
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/com/kunal52/ssl/DummyTrustManager.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2009 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.kunal52.ssl;
18 |
19 | import java.security.cert.X509Certificate;
20 |
21 | import javax.net.ssl.X509TrustManager;
22 |
23 | /**
24 | * A trust manager that accepts all X509 client and server certificates.
25 | *
26 | * @see "http://java.sun.com/products/javamail/SSLNOTES.txt"
27 | */
28 | public class DummyTrustManager implements X509TrustManager {
29 |
30 | public void checkClientTrusted(X509Certificate[] chain, String authType) {
31 | // Does not throw CertificateException: all chains trusted
32 | return;
33 | }
34 |
35 | public void checkServerTrusted(X509Certificate[] chain, String authType) {
36 | // Does not throw CertificateException: all chains trusted
37 | return;
38 | }
39 |
40 | public X509Certificate[] getAcceptedIssuers() {
41 | return new X509Certificate[0];
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/com/kunal52/ssl/KeyStoreManager.java:
--------------------------------------------------------------------------------
1 | package com.kunal52.ssl;
2 |
3 |
4 | import com.kunal52.AndroidRemoteContext;
5 | import com.kunal52.ssl.SslUtil;
6 |
7 | import java.io.FileOutputStream;
8 | import java.io.IOException;
9 | import java.nio.file.Files;
10 | import java.nio.file.Paths;
11 | import java.security.GeneralSecurityException;
12 | import java.security.KeyPair;
13 | import java.security.KeyPairGenerator;
14 | import java.security.KeyStore;
15 | import java.security.KeyStoreException;
16 | import java.security.NoSuchAlgorithmException;
17 | import java.security.cert.Certificate;
18 | import java.security.cert.CertificateException;
19 | import java.security.cert.X509Certificate;
20 | import java.util.Enumeration;
21 | import java.util.Locale;
22 | import java.util.UUID;
23 |
24 | import javax.net.ssl.KeyManager;
25 | import javax.net.ssl.KeyManagerFactory;
26 | import javax.net.ssl.TrustManager;
27 | import javax.net.ssl.TrustManagerFactory;
28 | import javax.net.ssl.X509TrustManager;
29 | import javax.security.auth.x500.X500Principal;
30 |
31 | /* renamed from: sensustech.android.tv.remote.control.manager.keystore.KeyStoreManager */
32 | /* loaded from: classes3.dex */
33 | public final class KeyStoreManager {
34 | private static String ANDROID_KEYSTORE = "AndroidKeyStore";
35 | private static final boolean DEBUG = false;
36 | // public static final String KEYSTORE_FILENAME = "androidtv.keystore";
37 | // static final char[] KEYSTORE_PASSWORD = "KeyStore_Password".toCharArray();
38 | private static final String LOCAL_IDENTITY_ALIAS = "androidtv-remote";
39 | private static final String REMOTE_IDENTITY_ALIAS_PATTERN = "androidtv-remote-%s";
40 | private static final String SERVER_IDENTITY_ALIAS = "androidtv-local";
41 | private static final String TAG = "KeyStoreManager";
42 | private DynamicTrustManager mDynamicTrustManager;
43 | private KeyStore mKeyStore;
44 |
45 | private AndroidRemoteContext androidRemoteContext = AndroidRemoteContext.getInstance();
46 |
47 | /* renamed from: sensustech.android.tv.remote.control.manager.keystore.KeyStoreManager$DynamicTrustManager */
48 | /* loaded from: classes3.dex */
49 | public static class DynamicTrustManager implements X509TrustManager {
50 | private X509TrustManager trustManager;
51 |
52 | @Override // javax.net.ssl.X509TrustManager
53 | public X509Certificate[] getAcceptedIssuers() {
54 | return new X509Certificate[0];
55 | }
56 |
57 | public DynamicTrustManager(KeyStore keyStore) {
58 | reloadTrustManager(keyStore);
59 | }
60 |
61 | @Override // javax.net.ssl.X509TrustManager
62 | public void checkClientTrusted(X509Certificate[] x509CertificateArr, String str) throws CertificateException {
63 | this.trustManager.checkClientTrusted(x509CertificateArr, str);
64 | }
65 |
66 | @Override // javax.net.ssl.X509TrustManager
67 | public void checkServerTrusted(X509Certificate[] x509CertificateArr, String str) throws CertificateException {
68 | this.trustManager.checkServerTrusted(x509CertificateArr, str);
69 | }
70 |
71 | public void reloadTrustManager(KeyStore keyStore) {
72 | try {
73 | TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
74 | trustManagerFactory.init(keyStore);
75 | TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
76 | for (int i = 0; i < trustManagers.length; i++) {
77 | if (trustManagers[i] instanceof X509TrustManager) {
78 | this.trustManager = (X509TrustManager) trustManagers[i];
79 | return;
80 | }
81 | }
82 | throw new IllegalStateException("No trust manager found");
83 | } catch (KeyStoreException | NoSuchAlgorithmException unused) {
84 | }
85 | }
86 | }
87 |
88 | public KeyStoreManager() {
89 | KeyStore load = load();
90 | this.mKeyStore = load;
91 | this.mDynamicTrustManager = new DynamicTrustManager(load);
92 | }
93 |
94 | private void clearKeyStore() {
95 | try {
96 | Enumeration aliases = this.mKeyStore.aliases();
97 | while (aliases.hasMoreElements()) {
98 | this.mKeyStore.deleteEntry(aliases.nextElement());
99 | }
100 | } catch (KeyStoreException unused) {
101 | }
102 | store();
103 | }
104 |
105 | private static String createAlias(String str) {
106 | return String.format(REMOTE_IDENTITY_ALIAS_PATTERN, str);
107 | }
108 |
109 | private void createIdentity(KeyStore keyStore) throws GeneralSecurityException {
110 | createIdentity(keyStore, SERVER_IDENTITY_ALIAS);
111 | }
112 |
113 | private void createIdentity(KeyStore keyStore, String str) throws GeneralSecurityException {
114 | createIdentity(keyStore, str, getUniqueId());
115 | }
116 |
117 | private void setLocale(Locale locale) {
118 | try {
119 | Locale.setDefault(locale);
120 | } catch (Exception unused) {
121 | }
122 | }
123 |
124 | private void createIdentity(KeyStore keyStore, String str, String str2) throws GeneralSecurityException {
125 | KeyPair generateKeyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
126 | try {
127 | keyStore.setKeyEntry(str, generateKeyPair.getPrivate(), null, new Certificate[]{SslUtil.generateX509V3Certificate(generateKeyPair, getCertificateName(str2))});
128 | } catch (IllegalArgumentException unused) {
129 | Locale locale = Locale.getDefault();
130 | setLocale(Locale.ENGLISH);
131 | keyStore.setKeyEntry(str, generateKeyPair.getPrivate(), null, new Certificate[]{SslUtil.generateX509V3Certificate(generateKeyPair, getCertificateName(str2))});
132 | setLocale(locale);
133 | }
134 | }
135 |
136 | private KeyStore createIdentityKeyStore() throws GeneralSecurityException {
137 | KeyStore keyStore;
138 | if (!useAndroidKeyStore()) {
139 | keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
140 | try {
141 | keyStore.load(null, androidRemoteContext.getKeyStorePass());
142 | } catch (IOException e) {
143 | throw new GeneralSecurityException("Unable to create empty keyStore", e);
144 | }
145 | } else {
146 | keyStore = KeyStore.getInstance(ANDROID_KEYSTORE);
147 | try {
148 | keyStore.load(null);
149 | } catch (IOException e2) {
150 | throw new GeneralSecurityException("Unable to create empty keyStore", e2);
151 | }
152 | }
153 | createIdentity(keyStore);
154 | return keyStore;
155 | }
156 |
157 | private static final String getCertificateName() {
158 | return getCertificateName(getUniqueId());
159 | }
160 |
161 | private static final String getCertificateName(String str) {
162 | return "CN=androidtv/livingTV";
163 | }
164 |
165 | private static String getSubjectDN(Certificate certificate) {
166 | X500Principal subjectX500Principal;
167 | if (!(certificate instanceof X509Certificate) || (subjectX500Principal = ((X509Certificate) certificate).getSubjectX500Principal()) == null) {
168 | return null;
169 | }
170 | return subjectX500Principal.getName();
171 | }
172 |
173 | private static final String getUniqueId() {
174 | return UUID.randomUUID().toString();
175 | }
176 |
177 | private boolean hasServerIdentityAlias(KeyStore keyStore) {
178 | try {
179 | return keyStore.containsAlias(SERVER_IDENTITY_ALIAS);
180 | } catch (KeyStoreException unused) {
181 | return false;
182 | }
183 | }
184 |
185 | private KeyStore load() {
186 | KeyStore keyStore;
187 | KeyStore keyStore2 = null;
188 | try {
189 | if (!useAndroidKeyStore()) {
190 | keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
191 | keyStore.load(Files.newInputStream(androidRemoteContext.getKeyStoreFile().toPath()), androidRemoteContext.getKeyStorePass());
192 | } else {
193 | keyStore = KeyStore.getInstance(ANDROID_KEYSTORE);
194 | keyStore.load(null);
195 | }
196 | keyStore2 = keyStore;
197 | } catch (KeyStoreException e) {
198 | throw new IllegalStateException("Unable to get default instance of KeyStore", e);
199 | } catch (IOException | GeneralSecurityException unused) {
200 | }
201 | if (keyStore2 == null || !hasServerIdentityAlias(keyStore2)) {
202 | try {
203 | KeyStore createIdentityKeyStore = createIdentityKeyStore();
204 | store(createIdentityKeyStore);
205 | return createIdentityKeyStore;
206 | } catch (GeneralSecurityException e2) {
207 | throw new IllegalStateException("Unable to create identity KeyStore", e2);
208 | }
209 | }
210 | return keyStore2;
211 | }
212 |
213 | private void store(KeyStore keyStore) {
214 | if (!useAndroidKeyStore()) {
215 | try {
216 | FileOutputStream openFileOutput = new FileOutputStream(androidRemoteContext.getKeyStoreFile());
217 | keyStore.store(openFileOutput, androidRemoteContext.getKeyStorePass());
218 | openFileOutput.close();
219 | } catch (IOException e) {
220 | throw new IllegalStateException("Unable to store keyStore", e);
221 | } catch (GeneralSecurityException e2) {
222 | throw new IllegalStateException("Unable to store keyStore", e2);
223 | }
224 | }
225 | }
226 |
227 | private boolean useAndroidKeyStore() {
228 | return false;
229 | }
230 |
231 | public void clear() {
232 | clearKeyStore();
233 | try {
234 | createIdentity(this.mKeyStore);
235 | } catch (GeneralSecurityException unused) {
236 | }
237 | store();
238 | }
239 |
240 | public KeyManager[] getKeyManagers() throws GeneralSecurityException {
241 | KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
242 | keyManagerFactory.init(this.mKeyStore, "".toCharArray());
243 | return keyManagerFactory.getKeyManagers();
244 | }
245 |
246 | public TrustManager[] getTrustManagers() throws GeneralSecurityException {
247 | try {
248 | return new DynamicTrustManager[]{this.mDynamicTrustManager};
249 | } catch (Exception e) {
250 | throw new GeneralSecurityException(e);
251 | }
252 | }
253 |
254 | public boolean hasServerIdentityAlias() {
255 | return hasServerIdentityAlias(this.mKeyStore);
256 | }
257 |
258 | public void initializeKeyStore() {
259 | initializeKeyStore(getUniqueId());
260 | }
261 |
262 | public void initializeKeyStore(String str) {
263 | try {
264 | createIdentity(this.mKeyStore, LOCAL_IDENTITY_ALIAS, str);
265 | store();
266 | } catch (GeneralSecurityException e) {
267 | throw new IllegalStateException("Unable to create identity KeyStore", e);
268 | }
269 | }
270 |
271 | public Certificate removeCertificate(String str) {
272 | try {
273 | String createAlias = createAlias(str);
274 | if (!this.mKeyStore.containsAlias(createAlias)) {
275 | return null;
276 | }
277 | Certificate certificate = this.mKeyStore.getCertificate(createAlias);
278 | this.mKeyStore.deleteEntry(createAlias);
279 | store();
280 | return certificate;
281 | } catch (KeyStoreException unused) {
282 | return null;
283 | }
284 | }
285 |
286 | public void store() {
287 | this.mDynamicTrustManager.reloadTrustManager(this.mKeyStore);
288 | store(this.mKeyStore);
289 | }
290 |
291 | public void storeCertificate(Certificate certificate) {
292 | storeCertificate(certificate, Integer.toString(certificate.hashCode()));
293 | }
294 |
295 | public void storeCertificate(Certificate certificate, String str) {
296 | try {
297 | String createAlias = createAlias(str);
298 | String subjectDN = getSubjectDN(certificate);
299 | if (this.mKeyStore.containsAlias(createAlias)) {
300 | this.mKeyStore.deleteEntry(createAlias);
301 | }
302 | if (subjectDN != null) {
303 | Enumeration aliases = this.mKeyStore.aliases();
304 | while (aliases.hasMoreElements()) {
305 | String nextElement = aliases.nextElement();
306 | String subjectDN2 = getSubjectDN(this.mKeyStore.getCertificate(nextElement));
307 | if (subjectDN2 != null && subjectDN2.equals(subjectDN)) {
308 | this.mKeyStore.deleteEntry(nextElement);
309 | }
310 | }
311 | }
312 | this.mKeyStore.setCertificateEntry(createAlias, certificate);
313 | store();
314 | } catch (KeyStoreException unused) {
315 | }
316 | }
317 | }
--------------------------------------------------------------------------------
/src/main/java/com/kunal52/ssl/SSLServerSocketFactoryWrapper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2009 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.kunal52.ssl;
18 |
19 | import java.io.IOException;
20 | import java.net.InetAddress;
21 | import java.net.ServerSocket;
22 | import java.security.KeyManagementException;
23 | import java.security.NoSuchAlgorithmException;
24 |
25 | import javax.net.ssl.KeyManager;
26 | import javax.net.ssl.SSLContext;
27 | import javax.net.ssl.SSLServerSocketFactory;
28 | import javax.net.ssl.TrustManager;
29 |
30 |
31 | /**
32 | * A convenience wrapper to generate an {@link SSLServerSocketFactory} that uses
33 | * the given {@link KeyManager} and {@link TrustManager} instances.
34 | */
35 | public class SSLServerSocketFactoryWrapper extends SSLServerSocketFactory {
36 |
37 | /**
38 | * The internal SSLServerSocketFactory which will be wrapped.
39 | */
40 | private SSLServerSocketFactory mFactory;
41 |
42 | public SSLServerSocketFactoryWrapper(KeyManager[] keyManagers,
43 | TrustManager[] trustManagers)
44 | throws NoSuchAlgorithmException, KeyManagementException {
45 | SSLContext sslcontext = SSLContext.getInstance("TLS");
46 | sslcontext.init(keyManagers, trustManagers, null);
47 | mFactory = sslcontext.getServerSocketFactory();
48 | }
49 |
50 | public static SSLServerSocketFactoryWrapper CreateWithDummyTrustManager(
51 | KeyManager[] keyManagers) throws KeyManagementException,
52 | NoSuchAlgorithmException {
53 | TrustManager[] trustManagers = { new DummyTrustManager() };
54 | return new SSLServerSocketFactoryWrapper(keyManagers, trustManagers);
55 | }
56 |
57 | @Override
58 | public ServerSocket createServerSocket(int port) throws IOException {
59 | return mFactory.createServerSocket(port);
60 | }
61 |
62 | @Override
63 | public ServerSocket createServerSocket(int port, int backlog)
64 | throws IOException {
65 | return mFactory.createServerSocket(port, backlog);
66 | }
67 |
68 | @Override
69 | public ServerSocket createServerSocket(int port, int backlog,
70 | InetAddress ifAddress) throws IOException {
71 | return mFactory.createServerSocket(port, backlog, ifAddress);
72 | }
73 |
74 | @Override
75 | public String[] getDefaultCipherSuites() {
76 | return mFactory.getDefaultCipherSuites();
77 | }
78 |
79 | @Override
80 | public String[] getSupportedCipherSuites() {
81 | return mFactory.getSupportedCipherSuites();
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/java/com/kunal52/ssl/SSLSocketFactoryWrapper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2009 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.kunal52.ssl;
18 |
19 | import java.io.IOException;
20 | import java.net.InetAddress;
21 | import java.net.Socket;
22 | import java.security.KeyManagementException;
23 | import java.security.NoSuchAlgorithmException;
24 |
25 | import javax.net.SocketFactory;
26 | import javax.net.ssl.KeyManager;
27 | import javax.net.ssl.SSLContext;
28 | import javax.net.ssl.SSLSocketFactory;
29 | import javax.net.ssl.TrustManager;
30 |
31 |
32 | /**
33 | * A convenience wrapper to generate an {@link SSLSocketFactory} that uses the
34 | * {@link KeyManager} and {@link TrustManager} instances that are passed in.
35 | */
36 | public class SSLSocketFactoryWrapper extends SSLSocketFactory {
37 |
38 | /**
39 | * The internal SSLSocketFactory which will be wrapped.
40 | */
41 | private SSLSocketFactory mFactory;
42 |
43 | public static SocketFactory getDefault() {
44 | throw new IllegalStateException("Not implemented.");
45 | }
46 |
47 | public SSLSocketFactoryWrapper() {
48 | throw new IllegalStateException("Not implemented.");
49 | }
50 |
51 | public SSLSocketFactoryWrapper(KeyManager[] keyManagers,
52 | TrustManager[] trustManagers) throws NoSuchAlgorithmException,
53 | KeyManagementException {
54 | java.security.Security.addProvider(
55 | new org.bouncycastle.jce.provider.BouncyCastleProvider());
56 |
57 | SSLContext sslcontext = SSLContext.getInstance("TLS");
58 | sslcontext.init(keyManagers, trustManagers, null);
59 | mFactory = sslcontext.getSocketFactory();
60 | }
61 |
62 | public static SSLSocketFactoryWrapper CreateWithDummyTrustManager(
63 | KeyManager[] keyManagers) throws KeyManagementException,
64 | NoSuchAlgorithmException {
65 | TrustManager[] trustManagers = { new DummyTrustManager() };
66 | return new SSLSocketFactoryWrapper(keyManagers, trustManagers);
67 | }
68 |
69 | @Override
70 | public Socket createSocket() throws IOException {
71 | return mFactory.createSocket();
72 | }
73 |
74 |
75 | @Override
76 | public Socket createSocket(InetAddress inaddr, int i)
77 | throws IOException {
78 | return mFactory.createSocket(inaddr, i);
79 | }
80 |
81 | @Override
82 | public Socket createSocket(InetAddress inaddr, int i,
83 | InetAddress inaddr1, int j) throws IOException {
84 | return mFactory.createSocket(inaddr, i, inaddr1, j);
85 | }
86 |
87 | @Override
88 | public Socket createSocket(Socket socket, String s, int i, boolean flag)
89 | throws IOException {
90 | return mFactory.createSocket(socket, s, i, flag);
91 | }
92 |
93 | @Override
94 | public Socket createSocket(String s, int i) throws IOException {
95 | return mFactory.createSocket(s, i);
96 | }
97 |
98 | @Override
99 | public Socket createSocket(String s, int i, InetAddress inaddr, int j)
100 | throws IOException {
101 | return mFactory.createSocket(s, i, inaddr, j);
102 | }
103 |
104 | @Override
105 | public String[] getDefaultCipherSuites() {
106 | return mFactory.getDefaultCipherSuites();
107 | }
108 |
109 | @Override
110 | public String[] getSupportedCipherSuites() {
111 | return mFactory.getSupportedCipherSuites();
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/main/java/com/kunal52/ssl/SslUtil.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2009 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.kunal52.ssl;
18 |
19 | import org.bouncycastle.asn1.ASN1InputStream;
20 | import org.bouncycastle.asn1.ASN1Sequence;
21 | import org.bouncycastle.asn1.x500.X500Name;
22 | import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
23 | import org.bouncycastle.asn1.x509.BasicConstraints;
24 | import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
25 | import org.bouncycastle.asn1.x509.GeneralName;
26 | import org.bouncycastle.asn1.x509.GeneralNames;
27 | import org.bouncycastle.asn1.x509.KeyPurposeId;
28 | import org.bouncycastle.asn1.x509.KeyUsage;
29 | import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
30 | import org.bouncycastle.asn1.x509.X509Extension;
31 | import org.bouncycastle.x509.X509V1CertificateGenerator;
32 | import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
33 |
34 | import java.io.FileInputStream;
35 | import java.io.IOException;
36 | import java.math.BigInteger;
37 | import java.security.GeneralSecurityException;
38 | import java.security.KeyPair;
39 | import java.security.KeyPairGenerator;
40 | import java.security.KeyStore;
41 | import java.security.NoSuchAlgorithmException;
42 | import java.security.PublicKey;
43 | import java.security.cert.Certificate;
44 | import java.security.cert.X509Certificate;
45 | import java.util.Calendar;
46 | import java.util.Date;
47 |
48 | import javax.net.ssl.KeyManager;
49 | import javax.net.ssl.KeyManagerFactory;
50 | import javax.net.ssl.SSLContext;
51 | import javax.net.ssl.TrustManager;
52 | import javax.security.auth.x500.X500Principal;
53 |
54 | /**
55 | * A collection of miscellaneous utility functions for use in Polo.
56 | */
57 | public class SslUtil {
58 |
59 | /**
60 | * Generates a new RSA key pair.
61 | *
62 | * @return the new object
63 | * @throws NoSuchAlgorithmException
64 | * if the RSA generator could not be loaded
65 | */
66 | public static KeyPair generateRsaKeyPair() throws NoSuchAlgorithmException {
67 | KeyPairGenerator kg = KeyPairGenerator.getInstance("RSA");
68 | KeyPair kp = kg.generateKeyPair();
69 | return kp;
70 | }
71 |
72 | /**
73 | * Creates a new, empty {@link KeyStore}
74 | *
75 | * @return the new KeyStore
76 | * @throws GeneralSecurityException
77 | * on error creating the keystore
78 | * @throws IOException
79 | * on error loading the keystore
80 | */
81 | public static KeyStore getEmptyKeyStore() throws GeneralSecurityException,
82 | IOException {
83 | KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
84 | ks.load(null, null);
85 | return ks;
86 | }
87 |
88 | /**
89 | * Generates a new, self-signed X509 V1 certificate for a KeyPair.
90 | *
91 | * @param pair
92 | * the {@link KeyPair} to be used
93 | * @param name
94 | * X.500 distinguished name
95 | * @return the new certificate
96 | * @throws GeneralSecurityException
97 | * on error generating the certificate
98 | */
99 | @SuppressWarnings("deprecation")
100 | public static X509Certificate generateX509V1Certificate(KeyPair pair,
101 | String name) throws GeneralSecurityException {
102 | java.security.Security
103 | .addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
104 |
105 | Calendar calendar = Calendar.getInstance();
106 | calendar.set(2009, 0, 1);
107 | Date startDate = new Date(calendar.getTimeInMillis());
108 | calendar.set(2029, 0, 1);
109 | Date expiryDate = new Date(calendar.getTimeInMillis());
110 |
111 | BigInteger serialNumber = BigInteger.valueOf(Math.abs(System
112 | .currentTimeMillis()));
113 |
114 | X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
115 | X500Principal dnName = new X500Principal(name);
116 | certGen.setSerialNumber(serialNumber);
117 | certGen.setIssuerDN(dnName);
118 | certGen.setNotBefore(startDate);
119 | certGen.setNotAfter(expiryDate);
120 | certGen.setSubjectDN(dnName); // note: same as issuer
121 | certGen.setPublicKey(pair.getPublic());
122 | certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
123 |
124 | // This method is deprecated, but Android Eclair does not provide the
125 | // generate() methods.
126 | X509Certificate cert = certGen.generate(pair.getPrivate(), "BC");
127 | return cert;
128 | }
129 |
130 | /**
131 | * Generates a new, self-signed X509 V3 certificate for a KeyPair.
132 | *
133 | * @param pair
134 | * the {@link KeyPair} to be used
135 | * @param name
136 | * X.500 distinguished name
137 | * @param notBefore
138 | * not valid before this date
139 | * @param notAfter
140 | * not valid after this date
141 | * @param serialNumber
142 | * serial number
143 | * @return the new certificate
144 | * @throws GeneralSecurityException
145 | * on error generating the certificate
146 | */
147 | @SuppressWarnings("deprecation")
148 | public static X509Certificate generateX509V3Certificate(KeyPair pair,
149 | String name, Date notBefore, Date notAfter, BigInteger serialNumber)
150 | throws GeneralSecurityException {
151 | java.security.Security
152 | .addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
153 |
154 | org.bouncycastle.x509.X509V3CertificateGenerator certGen = new org.bouncycastle.x509.X509V3CertificateGenerator();
155 | X500Name dnName = new org.bouncycastle.asn1.x500.X500Name(name);
156 | X500Principal principal = new X500Principal(name);
157 |
158 | certGen.setSerialNumber(serialNumber);
159 | // certGen.setIssuerDN(dnName);
160 | // certGen.setSubjectDN(dnName); // note: same as issuer
161 | certGen.setIssuerDN(principal);
162 | certGen.setSubjectDN(principal);
163 | certGen.setNotBefore(notBefore);
164 | certGen.setNotAfter(notAfter);
165 | certGen.setPublicKey(pair.getPublic());
166 | certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
167 |
168 | // For self-signed certificates, OpenSSL 0.9.6 has specific requirements
169 | // about certificate and extension content. Quoting the `man verify`:
170 | //
171 | // In OpenSSL 0.9.6 and later all certificates whose subject name
172 | // matches
173 | // the issuer name of the current certificate are subject to further
174 | // tests. The relevant authority key identifier components of the
175 | // current
176 | // certificate (if present) must match the subject key identifier (if
177 | // present) and issuer and serial number of the candidate issuer, in
178 | // addition the keyUsage extension of the candidate issuer (if present)
179 | // must permit certificate signing.
180 | //
181 | // In the code that follows,
182 | // - the KeyUsage extension permits cert signing (KeyUsage.keyCertSign);
183 | // - the Authority Key Identifier extension is added, matching the
184 | // subject key identifier, and using the issuer, and serial number.
185 |
186 | certGen.addExtension(X509Extension.basicConstraints, true,
187 | new BasicConstraints(false));
188 |
189 | certGen.addExtension(X509Extension.keyUsage, true, new KeyUsage(
190 | KeyUsage.digitalSignature | KeyUsage.keyEncipherment
191 | | KeyUsage.keyCertSign));
192 | certGen.addExtension(X509Extension.extendedKeyUsage, true,
193 | new ExtendedKeyUsage(KeyPurposeId.id_kp_serverAuth));
194 |
195 | AuthorityKeyIdentifier authIdentifier = createAuthorityKeyIdentifier(
196 | pair.getPublic(), dnName, serialNumber);
197 |
198 | certGen.addExtension(X509Extension.authorityKeyIdentifier, true,
199 | authIdentifier);
200 | // certGen.addExtension(X509Extension.subjectKeyIdentifier, true,
201 | // new SubjectKeyIdentifier(pair.getPublic()));
202 |
203 | certGen.addExtension(X509Extension.subjectAlternativeName, false,
204 | new GeneralNames(new GeneralName(GeneralName.rfc822Name,
205 | "googletv@test.test")));
206 |
207 | // This method is deprecated, but Android Eclair does not provide the
208 | // generate() methods.
209 | X509Certificate cert = certGen.generate(pair.getPrivate(), "BC");
210 | return cert;
211 | }
212 |
213 | /**
214 | * Creates an AuthorityKeyIdentifier from a public key, name, and serial
215 | * number.
216 | *
217 | * {@link AuthorityKeyIdentifierStructure} is almost perfect for
218 | * this, but sadly it does not have a constructor suitable for us:
219 | * {@link AuthorityKeyIdentifierStructure#AuthorityKeyIdentifierStructure(PublicKey)}
220 | * does not set the serial number or name (which is important to us), while
221 | * {@link AuthorityKeyIdentifierStructure#AuthorityKeyIdentifierStructure(X509Certificate)}
222 | * sets those fields but needs a completed certificate to do so.
223 | *
224 | * This method addresses the gap in available {@link AuthorityKeyIdentifier}
225 | * constructors provided by BouncyCastle; its implementation is derived from
226 | * {@link AuthorityKeyIdentifierStructure#AuthorityKeyIdentifierStructure(X509Certificate)}.
227 | *
228 | * @param publicKey
229 | * the public key
230 | * @param dnName
231 | * the name
232 | * @param serialNumber
233 | * the serial number
234 | * @return a new {@link AuthorityKeyIdentifier}
235 | */
236 | private static AuthorityKeyIdentifier createAuthorityKeyIdentifier(
237 | PublicKey publicKey, org.bouncycastle.asn1.x500.X500Name dnName,
238 | BigInteger serialNumber) {
239 | GeneralName genName = new GeneralName(dnName);
240 | SubjectPublicKeyInfo info;
241 | try {
242 | info = new SubjectPublicKeyInfo((ASN1Sequence) new ASN1InputStream(
243 | publicKey.getEncoded()).readObject());
244 | } catch (IOException e) {
245 | throw new RuntimeException("Error encoding public key");
246 | }
247 | return new AuthorityKeyIdentifier(info, new GeneralNames(genName),
248 | serialNumber);
249 | }
250 |
251 | /**
252 | * Wrapper for
253 | * {@link SslUtil#generateX509V3Certificate(KeyPair, String, Date, Date, BigInteger)}
254 | * which uses a default validity period and serial number.
255 | *
256 | * The validity period is Jan 1 2009 - Jan 1 2099. The serial number is the
257 | * current system time.
258 | */
259 | public static X509Certificate generateX509V3Certificate(KeyPair pair,
260 | String name) throws GeneralSecurityException {
261 | Calendar calendar = Calendar.getInstance();
262 | calendar.set(2009, 0, 1);
263 | Date notBefore = new Date(calendar.getTimeInMillis());
264 | calendar.set(2099, 0, 1);
265 | Date notAfter = new Date(calendar.getTimeInMillis());
266 |
267 | BigInteger serialNumber = BigInteger.valueOf(Math.abs(System
268 | .currentTimeMillis()));
269 |
270 | return generateX509V3Certificate(pair, name, notBefore, notAfter,
271 | serialNumber);
272 | }
273 |
274 | /**
275 | * Wrapper for
276 | * {@link SslUtil#generateX509V3Certificate(KeyPair, String, Date, Date, BigInteger)}
277 | * which uses a default validity period.
278 | *
279 | * The validity period is Jan 1 2009 - Jan 1 2099.
280 | */
281 | public static X509Certificate generateX509V3Certificate(KeyPair pair,
282 | String name, BigInteger serialNumber)
283 | throws GeneralSecurityException {
284 | Calendar calendar = Calendar.getInstance();
285 | calendar.set(2009, 0, 1);
286 | Date notBefore = new Date(calendar.getTimeInMillis());
287 | calendar.set(2099, 0, 1);
288 | Date notAfter = new Date(calendar.getTimeInMillis());
289 |
290 | return generateX509V3Certificate(pair, name, notBefore, notAfter,
291 | serialNumber);
292 | }
293 |
294 | /**
295 | * Generates a new {@code SSLContext} suitable for a test environment.
296 | *
297 | * A new {@link KeyPair}, {@link X509Certificate}, {@link DummyTrustManager}
298 | * , and an empty {@link KeyStore} are created and used to initialize the
299 | * context.
300 | *
301 | * @return the new context
302 | * @throws GeneralSecurityException
303 | * if an error occurred during initialization
304 | * @throws IOException
305 | * if an empty KeyStore could not be generated
306 | */
307 | public SSLContext generateTestSslContext() throws GeneralSecurityException,
308 | IOException {
309 | SSLContext sslcontext = SSLContext.getInstance("SSLv3");
310 | KeyManager[] keyManagers = SslUtil.generateTestServerKeyManager(
311 | "SunX509", "test");
312 | sslcontext.init(keyManagers,
313 | new TrustManager[] { new DummyTrustManager() }, null);
314 | return sslcontext;
315 | }
316 |
317 | /**
318 | * Creates a new pain of {@link KeyManager}s, backed by a keystore file.
319 | *
320 | * @param keyManagerInstanceName
321 | * name of the {@link KeyManagerFactory} to request
322 | * @param fileName
323 | * the name of the keystore to load
324 | * @param password
325 | * the password for the keystore
326 | * @return the new object
327 | * @throws GeneralSecurityException
328 | * if an error occurred during initialization
329 | * @throws IOException
330 | * if the keystore could not be loaded
331 | */
332 | public static KeyManager[] getFileBackedKeyManagers(
333 | String keyManagerInstanceName, String fileName, String password)
334 | throws GeneralSecurityException, IOException {
335 | KeyManagerFactory km = KeyManagerFactory
336 | .getInstance(keyManagerInstanceName);
337 | KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
338 | ks.load(new FileInputStream(fileName), password.toCharArray());
339 | km.init(ks, password.toCharArray());
340 | return km.getKeyManagers();
341 | }
342 |
343 | /**
344 | * Creates a pair of {@link KeyManager}s suitable for use in testing.
345 | *