gpt4 book ai didi

java - Java 中通过 UDP 加密语音聊天的噪音

转载 作者:太空宇宙 更新时间:2023-11-04 06:47:30 24 4
gpt4 key购买 nike

我们必须通过 UDP 构建加密/解密的语音聊天。聊天无需加密即可工作,但当我添加 AES 代码进行加密时,我听到非常大的噪音,这是连续的周期性蜂鸣信号,但同时我也听到解密的对话,这很好。我需要消除这种噪音。

我们将非常感谢您的帮助。谢谢

正在发送

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

import javax.sound.sampled.*;

public class MicPlayer {

private static final String IP_TO_STREAM_TO = "localhost" ;
private static final int PORT_TO_STREAM_TO = 1234 ;

/** Creates a new instance of MicPlayer */
public MicPlayer() {

}

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
Mixer.Info minfo[] = AudioSystem.getMixerInfo() ;
for( int i = 0 ; i < minfo.length ; i++ )
{
System.out.println( minfo[i] ) ;
}


if (AudioSystem.isLineSupported(Port.Info.MICROPHONE)) {
try {


DataLine.Info dataLineInfo = new DataLine.Info( TargetDataLine.class , getAudioFormat() ) ;
TargetDataLine targetDataLine = (TargetDataLine)AudioSystem.getLine( dataLineInfo ) ;
targetDataLine.open( getAudioFormat() );
targetDataLine.start();
byte tempBuffer[] = new byte[8192] ;

while( true )
{
targetDataLine.read( tempBuffer , 0 , tempBuffer.length );
byte[] encrypt = AES.encrypt(tempBuffer);
sendThruUDP(encrypt) ;

}

}
catch(Exception e )
{
System.out.println(" not correct " ) ;
System.exit(0) ;
}
}



}


public static AudioFormat getAudioFormat(){
float sampleRate = 8000.0F;
//8000,11025,16000,22050,44100
int sampleSizeInBits = 16;
//8,16
int channels = 1;
//1,2
boolean signed = true;
//true,false
boolean bigEndian = false;
//true,false
return new AudioFormat( sampleRate, sampleSizeInBits, channels, signed, bigEndian );
}


public static void sendThruUDP( byte soundpacket[] )
{
try
{
DatagramSocket sock = new DatagramSocket() ;
sock.send( new DatagramPacket( soundpacket , soundpacket.length , InetAddress.getByName( IP_TO_STREAM_TO ) , PORT_TO_STREAM_TO ) ) ;
sock.close() ;
}
catch( Exception e )
{
e.printStackTrace() ;
System.out.println(" Unable to send soundpacket using UDP " ) ;
}

}



}

接收

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;

public class RadioReceiver extends Thread {

private static final String IP_TO_STREAM_TO = "localhost" ;
private static final int PORT_TO_STREAM_TO = 1234;

/** Creates a new instance of RadioReceiver */
public RadioReceiver() {
}

public void run()
{
byte b[] = null ;
while( true )
{
b = receiveThruUDP() ;
toSpeaker( b ) ;
}
}

/**
* @param args the command line arguments
*/
public static void main(String[] args) {

RadioReceiver r = new RadioReceiver() ;
r.start() ;

}


public static byte[] receiveThruUDP()
{
try
{
DatagramSocket sock = new DatagramSocket(PORT_TO_STREAM_TO) ;
byte soundpacket[] = new byte[8192] ;
DatagramPacket datagram = new DatagramPacket( soundpacket , soundpacket.length , InetAddress.getByName( IP_TO_STREAM_TO ) , PORT_TO_STREAM_TO ) ;
sock.receive( datagram ) ;
sock.close() ;
return AES.decrypt(datagram.getData()); // soundpacket ;
}
catch( Exception e )
{
System.out.println(" Unable to send soundpacket using UDP " ) ;
return null ;
}

}


public static void toSpeaker( byte soundbytes[] )
{

try{
DataLine.Info dataLineInfo = new DataLine.Info( SourceDataLine.class , getAudioFormat() ) ;
SourceDataLine sourceDataLine = (SourceDataLine)AudioSystem.getLine( dataLineInfo );
sourceDataLine.open( getAudioFormat() ) ;
sourceDataLine.start();

sourceDataLine.write( soundbytes , 0, soundbytes.length );
sourceDataLine.drain() ;
sourceDataLine.close() ;
}
catch(Exception e )
{
System.out.println("not working in speakers " ) ;
}

}


public static AudioFormat getAudioFormat()
{
float sampleRate = 44100.0F;
//8000,11025,16000,22050,44100
int sampleSizeInBits = 16;
//8,16
int channels = 1;
//1,2
boolean signed = true;
//true,false
boolean bigEndian = false;
//true,false
return new AudioFormat( sampleRate, sampleSizeInBits, channels, signed, bigEndian );
}



}

AES

import java.security.MessageDigest;
import java.util.Arrays;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class AES {
static String IV = "AAAAAAAAAAAAAAAA";
static String encryptionKey = "0123456789abcdef";

public static byte[] encrypt(byte[] inputcum) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
cipher.init(Cipher.ENCRYPT_MODE, key,new IvParameterSpec(IV.getBytes("UTF-8")));
return cipher.doFinal(inputcum);
}

public static byte[] decrypt(byte[] cipherSound) throws Exception{
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
cipher.init(Cipher.DECRYPT_MODE, key,new IvParameterSpec(IV.getBytes("UTF-8")));
return cipher.doFinal(cipherSound);
}
}

最佳答案

该问题与 UDP 或加密无关。每次调用 TargetDataLine.read(byte[]) 仅填充数组的一部分,其余部分则由之前调用的剩余部分填充,但每次都会加密并发送整个数组。

调用TargetDataLine.read(byte[])行为类似于 InputStream.read(byte[]) - 它返回传输到字节数组中的实际字节数。不能忽略该值。

对于最低工作流程,应按照以下准则修改代码:

发送时:

while( true ) {
int read = targetDataLine.read( tempBuffer , 0 , tempBuffer.length );
byte[] encrypt = AES.encrypt(tempBuffer, 0, read);
sendThruUDP(encrypt) ;
}

加密时(请注意,填充更改为 PKCS5Padding 以允许输入长度不是 AES block 大小的倍数):

public static byte[] encrypt(byte[] plainData, int offset, int length) throws Exception 
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
cipher.init(Cipher.ENCRYPT_MODE, key,new IvParameterSpec(IV.getBytes("UTF-8")));
return cipher.doFinal(plainData, offset, length);
}

应修改 decrypt() 方法以使用相同的填充。

其他最明显的改进:

  • 为每个数据 block 生成新的随机 IV,并发送包含 IV 和加密数据的数据包。它需要在发送+加密和接收+解密方面的多个字节数组之间进行一些复制,但从密码学的角度来看,在同一 key 上重复使用相同的 IV 进行多个密码操作是绝对不安全的
  • 使用正确的 key 派生函数(搜索 PBKDF2),而不是简单地将密码字符串转换为字节。
  • 获取一次 Cipher 实例,然后使用 key 和新的 IV 重新初始化它。这将节省一些 CPU 和内存。

关于java - Java 中通过 UDP 加密语音聊天的噪音,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23783457/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com