【问题标题】:Streaming encrypted audio bytes over socket通过套接字流式传输加密的音频字节
【发布时间】:2014-06-28 11:55:06
【问题描述】:

我在 cmets 之后更新了问题以使问题更清楚。我正在尝试编写一个用于通过 TCP 进行加密语音呼叫的代码。当然,当涉及到安全连接时,SSL 是一种选择,但现在我正在研究 TCP,就像任何其他项目一样。

该程序旨在捕获来自麦克风的音频,使用 AES 对其进行加密,然后将数据发送到服务器。在服务器端,接收到的数据将被解密并发送到扬声器。但是使用这些代码,我在运行时从客户端收到了 LineUnavailable 异常:

无法打开线路:javax.sound.sampled.LineUnavailableException:线路格式为 PCM_SIGNED 16000.0 Hz,8 位,立体声,2 字节/帧,不支持。

通常情况下,如果没有加密,代码没有问题,而我使用 BufferedOutputStream(s.getOutputStream()) 对象来记录数据;并且声音通过网络成功传输,即我的硬件支持提到的 PCM 格式。

另一方面,在一个简单的独立捕获/播放 java 模块中,我测试了加密代码,我设法在捕获和保存过程之间加密和解密音频数据,而它是一个字节 [] 数据,就在它被保存到输出 wav 文件之前。加密过程本身似乎没有问题。

问题出现在联网过程中,当我使用 ByteArrayOutputStream 写入来自 DataLine 的音频数据时,而不是使用之前直接采用 s.getOutputStream() 方法的 BufferedOutputStream 对象。此处使用 ByteArrayOutputStream 的原因是将捕获的字节作为输入参数传递给加密方法。值得注意的是,ByteArrayOutputStream 运行良好,当周围没有套接字时,将音频字节保存到本地磁盘上的 wav 文件中。

考虑到 TCP 网络,我现在的问题是工作的 OutputStream 对象和等待加密的字节之间的解体。我的最后一个测试是在下面的代码中使用 DataOutput/InputStream 对象的不成功测试。仍然需要任何关于适当的输入/输出流方法的想法来实现成功的通信(如果有的话)。

捕获和发送数据的代码片段是:

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) { }

}

接收和播放数据的代码片段是:

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;
}

【问题讨论】:

  • 哪一方抛出异常:记录和加密的一端,还是解密和播放的一端?
  • 录制并发送音频的客户端,抛出异常
  • 一旦你加密了音频,它就不再是音频了。
  • 为什么不简单地使用 SSL 隧道?
  • 我认为这与加密无关;原因是您似乎没有对引发异常的行执行任何加密操作(如果确实是line = (TargetDataLine) AudioSystem.getLine(info);,请验证)。同时,您的运行时更有可能以某种方式搞砸了。

标签: java aes audio-streaming


【解决方案1】:

最好尝试使用更简单的方法来使用 Encryptor4j 在代码中利用流式加密:https://github.com/martinwithaar/Encryptor4j

包装一个 OutputStream 进行加密:

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();
  }
}

还有一个用于解密的 InputStream:

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();
  }
}

【讨论】:

    猜你喜欢
    • 2011-02-14
    • 2013-04-27
    • 1970-01-01
    • 2020-09-23
    • 2014-10-19
    • 2014-07-23
    • 2013-01-14
    • 1970-01-01
    • 2014-07-23
    相关资源
    最近更新 更多