Reputation:
I have updated the question after the comments to make the problem clearer.I am trying to write a code for encrypted voice call over TCP. Of course SSL is one of the options when it comes to a secure connection, but just now I'm working on TCP, like any other project.
The program aims to capture the audio coming from microphone, encrypt it using AES and then send the data to the server.On the server side the received data would be decrypted and sent to the speaker. But with these codes I get a LineUnavailable Exception from the client side during runtime:
Unable to open the line: javax.sound.sampled.LineUnavailableException: line with format PCM_SIGNED 16000.0 Hz, 8 bit, stereo, 2 bytes/frame, not supported.
Normally, without encryption there is no problem with the codes, while I use a BufferedOutputStream(s.getOutputStream()) object for the recorded data; and the sound is transmitted over network successfully, i.e my hardware supports the mentioned PCM format.
On the other hand, in a simple standalone capture/play java module I have tested the encryption codes, where I have managed to encrypt and decrypt the audio data between the capturing and saving processes, while it was a byte[] data, just before it was saved to an output wav file. There seems no problem with the encryption process itself.
The problem arises during networking, when I use a ByteArrayOutputStream to write the audio data coming from DataLine, instead of using a BufferedOutputStream object which was directly taking the s.getOutputStream() method before. The reason to use a ByteArrayOutputStream here is to pass the captured bytes as an input argument to the encrypting method. It is worth to note that the ByteArrayOutputStream works well, while saving audio bytes to a wav file on local disk when there is no sockets around.
The question for me now is the disintegration between a working OutputStream object and the bytes waiting for encryption, considering a TCP network. My final test was the unsuccessful one with DataOutput/InputStream objects in the code below. Still need any ideas for an appropriate Input/Output streaming method to achieve a successful communication, if any.
The code piece for capturing and sending data is:
public void run() {
try {
dos = new DataOutputStream(s.getOutputStream());// need the exact stream obj.
} catch (IOException ex) {
return;
}
AudioFormat format =new AudioFormat(16000,8,2,true,true);
DataLine.Info info = new DataLine.Info(TargetDataLine.class,format);
if (!AudioSystem.isLineSupported(info)) {
System.err.println("Line matching " + info + " not supported.");// throws the exception
return;
}
try {
line = (TargetDataLine) AudioSystem.getLine(info);
line.open(format, line.getBufferSize());
} catch (LineUnavailableException ex) {
System.err.println("Unable to open the line: " + ex);// related to exception
return;
}
byte[] data = new byte[256];
int numBytesRead=0;
line.start();
// In the nonsecure call version, the audio data is written directly
// to the BufferedOutputStream(s.getOutputStream()) and transmitted without problem
ByteArrayOutputStream caps = new ByteArrayOutputStream(); //?
while (thread != null) {
numBytesRead = line.read(data, 0,128);
try {
caps.write(data, 0, numBytesRead);
} catch (Exception ex) {
System.err.println("Unable to read/write line data: " + ex);
break;
}
}
line.stop();
line.close();
line = null;
try {
caps.flush();
caps.close();
} catch (IOException ex) { ex.printStackTrace(); }
try {
SecretKeySpec skeySpec = CryptUtil.getSecretKeySpec("password12345678","AES",128);
byte[] encrypted = CryptUtil.encrypt(caps.toByteArray(), skeySpec);
dos.writeInt(encrypted.length);
dos.write(encrypted,0,encrypted.length);
} catch (Exception ex) { }
}
The code piece for receiving and playing data is:
public void run() {
AudioFormat format =new AudioFormat(16000,8,2,true,true);
try {
dis = new DataInputStream(s.getInputStream());// need the exact stream obj.
} catch (Exception e) {
System.err.println("Could not get InputStream: " + e);
}
try {
int length = dis.readInt();
byte[] message = new byte[length];
dis.readFully(message);
SecretKeySpec skeySpec = CryptUtil.getSecretKeySpec("password12345678","AES",128);
byte[] audioBytes = CryptUtil.decrypt(message, skeySpec);
ByteArrayInputStream bais = new ByteArrayInputStream(audioBytes);
playStream = new AudioInputStream(bais, format, audioBytes.length / format.getFrameSize());
} catch (Exception e) {
System.err.println("Could not decrypt the stream: " + e);
}
DataLine.Info info = new DataLine.Info(SourceDataLine.class,format);
try {
line = (SourceDataLine) AudioSystem.getLine(info);
line.open(format, bufSize);
} catch (LineUnavailableException ex) {
System.err.println("Unable to open the line: " + ex);
return;
}
byte[] data = new byte[256];
int numBytesRead = 0;
line.start();
while (thread != null) {
try{
numBytesRead = playStream.read(data);
line.write(data, 0,numBytesRead);
} catch (Exception e) {
System.err.println("Error during playback: " + e);
break;
}
}
if (thread != null) {
line.drain();
}
line.stop();
line.close();
line = null;
}
Upvotes: 0
Views: 1957
Reputation: 2005
Best to try and use a simpler approach for leveraging streaming encryption in your code using Encryptor4j: https://github.com/martinwithaar/Encryptor4j
Wrap an OutputStream for encryption:
Encryptor encryptor = new Encryptor(secretKey, "AES/CTR/NoPadding", 16);
InputStream is = null;
OutputStream os = null;
try {
is = new FileInputStream("original.jpg");
os = encryptor.wrapOutputStream(new FileOutputStream("encrypted.jpg"));
byte[] buffer = new byte[4096];
int nRead;
while((nRead = is.read(buffer)) != -1) {
os.write(buffer, 0, nRead);
}
os.flush();
} finally {
if(is != null) {
is.close();
}
if(os != null) {
os.close();
}
}
And an InputStream for decryption:
Encryptor encryptor = new Encryptor(secretKey, "AES/CTR/NoPadding", 16);
InputStream is = null;
OutputStream os = null;
try {
is = encryptor.wrapInputStream(new FileInputStream("encrypted.jpg"));
os = new FileOutputStream("decrypted.jpg");
byte[] buffer = new byte[4096];
int nRead;
while((nRead = is.read(buffer)) != -1) {
os.write(buffer, 0, nRead);
}
os.flush();
} finally {
if(is != null) {
is.close();
}
if(os != null) {
os.close();
}
}
Upvotes: 0