首页
工具
隐私协议
App Privacy Policy
更多
作品
关于我们
Search
1
android5遇到INSTALL_FAILED_DEXOPT 解决办法
1,664 阅读
2
设置max_connections无效
1,484 阅读
3
FlexboxLayout+recyclerView实现自动换行
1,374 阅读
4
Nginx配置多个域名
1,257 阅读
5
Android P http网络请求失败
1,230 阅读
默认分类
mysql
android
android深入
Jetpack Compose
Android传感器
php
Yii2
windows
webrtc
登录
Search
标签搜索
android
kotlin
webrtc
kurento
mysql
adb
nginx
flutter
rsa
微信
git
Yii2
md5
加密
dart
aes
wechat
windows
小程序
dexopt
Typecho
累计撰写
80
篇文章
累计收到
3
条评论
首页
栏目
默认分类
mysql
android
android深入
Jetpack Compose
Android传感器
php
Yii2
windows
webrtc
页面
工具
隐私协议
App Privacy Policy
作品
关于我们
搜索到
9
篇与
android
的结果
2024-08-13
adb查看当前包名
windows:adb shell dumpsys window w |findstr \/ |findstr name=mac端:adb shell dumpsys window w |grep / |grep name=
2024年08月13日
15 阅读
0 评论
0 点赞
2022-11-17
Android Webrtc之Kurento-group-call
简介Kurento-group-call是一个类似视频会议的官方例子,在每个用户端都会创建N个端点,相比one2one-call/one2many-call要复杂些。在用户之间连接多个客户端,创建一个视频会议;同样,运行之前要安装Kurento媒体服务。先克隆项目,然后运行主类:git clone https://github.com/Kurento/kurento-tutorial-java.git cd kurento-tutorial-java/kurento-group-call运行项目mvn -U clean spring-boot:run \ -Dspring-boot.run.jvmArguments="-Dkms.url=ws://{KMS_HOST}:8888/kurento"功能分析用户进入房间时,会创建一个新的Media,另外会通知其他用户有新用户连接,然后所有参于者将请求服务器接收新参于者的媒体。新用户依次获取所有已连接的参于者列表,并请求服务器接收房间中所有客户端的media。每个客户端都发送自己的media,然后从其他用户那里接收媒体,每个客户端都会有n个端点,房间总共会有n*n个端点。当用户离开房间时,服务器会通知所有客户端。然后,客户端代码请求服务器取消与离开的客户端相关的所有媒体元素。服务端实例化Kurento客户端后,您就可以与Kurento媒体服务器通信并控制其多媒体功能了。GroupCallApp实现了接口WebSocketConfigurer,注册了一个WebSocketHandler用于处理WebSocket请求@EnableWebSocket @SpringBootApplication public class GroupCallApp implements WebSocketConfigurer { @Bean public UserRegistry registry() { return new UserRegistry(); } @Bean public RoomManager roomManager() { return new RoomManager(); } @Bean public CallHandler groupCallHandler() { return new CallHandler(); } @Bean public KurentoClient kurentoClient() { return KurentoClient.create(); } public static void main(String[] args) throws Exception { SpringApplication.run(GroupCallApp.class, args); } @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(groupCallHandler(), "/groupcall"); } } CallHandler类实现TextWebSocketHandler处理文本WebSocket请求。这个类的核心部分是方法handleTextMessage。此方法实现请求的操作,通过WebSocket返回响应public class CallHandler extends TextWebSocketHandler { private static final Logger log = LoggerFactory.getLogger(CallHandler.class); private static final Gson gson = new GsonBuilder().create(); @Autowired private RoomManager roomManager; @Autowired private UserRegistry registry; @Override public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { final JsonObject jsonMessage = gson.fromJson(message.getPayload(), JsonObject.class); final UserSession user = registry.getBySession(session); if (user != null) { log.debug("Incoming message from user '{}': {}", user.getName(), jsonMessage); } else { log.debug("Incoming message from new user: {}", jsonMessage); } switch (jsonMessage.get("id").getAsString()) { case "joinRoom": joinRoom(jsonMessage, session); break; case "receiveVideoFrom": final String senderName = jsonMessage.get("sender").getAsString(); final UserSession sender = registry.getByName(senderName); final String sdpOffer = jsonMessage.get("sdpOffer").getAsString(); user.receiveVideoFrom(sender, sdpOffer); break; case "leaveRoom": leaveRoom(user); break; case "onIceCandidate": JsonObject candidate = jsonMessage.get("candidate").getAsJsonObject(); if (user != null) { IceCandidate cand = new IceCandidate(candidate.get("candidate").getAsString(), candidate.get("sdpMid").getAsString(), candidate.get("sdpMLineIndex").getAsInt()); user.addCandidate(cand, jsonMessage.get("name").getAsString()); } break; default: break; } } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { ... } private void joinRoom(JsonObject params, WebSocketSession session) throws IOException { ... } private void leaveRoom(UserSession user) throws IOException { ... } }在afterConnectionClosed方法里,它会将用户userSession从registry房间中移除并驱逐出去@Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { UserSession user = registry.removeBySession(session); roomManager.getRoom(user.getRoomName()).leave(user); }在joinRoom方法中,服务器检查是否有指定名称的已注册房间,将用户添加到该房间并注册用户private void joinRoom(JsonObject params, WebSocketSession session) throws IOException { final String roomName = params.get("room").getAsString(); final String name = params.get("name").getAsString(); log.info("PARTICIPANT {}: trying to join room {}", name, roomName); Room room = roomManager.getRoom(roomName); final UserSession user = room.join(name, session); registry.register(user); }leaveRoom方法结束一个用户的视频通话private void leaveRoom(UserSession user) throws IOException { final Room room = roomManager.getRoom(user.getRoomName()); room.leave(user); if (room.getParticipants().isEmpty()) { roomManager.removeRoom(room); } }客户端创建WebSocket,在onMessage方法下监听JSON信令协议。有5个不用的消息传入到客户端:existingParticipants,newParticipantArrived,participantLeft,receiveVideoAnswer和iceCandidate。var ws = new WebSocket('wss://' + location.host + '/groupcall'); var participants = {}; var name; window.onbeforeunload = function() { ws.close(); }; ws.onmessage = function(message) { var parsedMessage = JSON.parse(message.data); console.info('Received message: ' + message.data); switch (parsedMessage.id) { case 'existingParticipants': onExistingParticipants(parsedMessage); break; case 'newParticipantArrived': onNewParticipant(parsedMessage); break; case 'participantLeft': onParticipantLeft(parsedMessage); break; case 'receiveVideoAnswer': receiveVideoResponse(parsedMessage); break; case 'iceCandidate': participants[parsedMessage.name].rtcPeer.addIceCandidate(parsedMessage.candidate, function (error) { if (error) { console.error("Error adding candidate: " + error); return; } }); break; default: console.error('Unrecognized message', parsedMessage); } } function register() { name = document.getElementById('name').value; var room = document.getElementById('roomName').value; document.getElementById('room-header').innerText = 'ROOM ' + room; document.getElementById('join').style.display = 'none'; document.getElementById('room').style.display = 'block'; var message = { id : 'joinRoom', name : name, room : room, } sendMessage(message); } function onNewParticipant(request) { receiveVideo(request.name); } function receiveVideoResponse(result) { participants[result.name].rtcPeer.processAnswer (result.sdpAnswer, function (error) { if (error) return console.error (error); }); } function callResponse(message) { if (message.response != 'accepted') { console.info('Call not accepted by peer. Closing call'); stop(); } else { webRtcPeer.processAnswer(message.sdpAnswer, function (error) { if (error) return console.error (error); }); } } function onExistingParticipants(msg) { var constraints = { audio : true, video : { mandatory : { maxWidth : 320, maxFrameRate : 15, minFrameRate : 15 } } }; console.log(name + " registered in room " + room); var participant = new Participant(name); participants[name] = participant; var video = participant.getVideoElement(); var options = { localVideo: video, mediaConstraints: constraints, onicecandidate: participant.onIceCandidate.bind(participant) } participant.rtcPeer = new kurentoUtils.WebRtcPeer.WebRtcPeerSendonly(options, function (error) { if(error) { return console.error(error); } this.generateOffer (participant.offerToReceiveVideo.bind(participant)); }); msg.data.forEach(receiveVideo); } function leaveRoom() { sendMessage({ id : 'leaveRoom' }); for ( var key in participants) { participants[key].dispose(); } document.getElementById('join').style.display = 'block'; document.getElementById('room').style.display = 'none'; ws.close(); } function receiveVideo(sender) { var participant = new Participant(sender); participants[sender] = participant; var video = participant.getVideoElement(); var options = { remoteVideo: video, onicecandidate: participant.onIceCandidate.bind(participant) } participant.rtcPeer = new kurentoUtils.WebRtcPeer.WebRtcPeerRecvonly(options, function (error) { if(error) { return console.error(error); } this.generateOffer (participant.offerToReceiveVideo.bind(participant)); });; } function onParticipantLeft(request) { console.log('Participant ' + request.name + ' left'); var participant = participants[request.name]; participant.dispose(); delete participants[request.name]; } function sendMessage(message) { var jsonMessage = JSON.stringify(message); console.log('Sending message: ' + jsonMessage); ws.send(jsonMessage); } 这里主要依赖于kurento-utils来完成Android上面是官方的代码分析,接下来主要用Android来实现客户端功能,跟one2one一样,都是引用org.webrtc: implementation 'org.webrtc:google-webrtc:1.0.32006' implementation 'org.java-websocket:Java-WebSocket:1.5.3' implementation "com.google.code.gson:gson:2.+"初始化WebSocket,在onMessage里监听JSON信令消息,实现五个方法:existingParticipants,newParticipantArrived,participantLeft,receiveVideoAnswer和iceCandidate when (id) { "existingParticipants" -> { onExistingParticipants(jsonObject) } "newParticipantArrived" -> { onNewParticipantArrived(jsonObject) } "participantLeft" -> { onParticipantLeft(jsonObject) } "receiveVideoAnswer" -> { onReceiveVideoAnswer(jsonObject) } "iceCandidate" -> { iceCandidate(jsonObject) } }existingParticipants:在加入房间后会收到existingParticipants,并告诉有哪些参于者newParticipantArrived:如果有新的参于者加入时,会收到此信息participantLeft:有参于者离开,会收到此信息receiveVideoAnswer:接收到服务器发送的Answer信令iceCandidate:candidates事件发送给服务端的请求:joinRoom:加入到房间的请求receiveVideoFrom:发起接收某参于者时间的请求leaveRoom:离开房间的请求onIceCandidate:将candidate发送到服务端手机与电脑完成连接
2022年11月17日
233 阅读
0 评论
0 点赞
2022-11-11
Android Webrtc之Kurento-one2many-call
Kurento-one2many-call这是官方的一个例子,一个发送视频,N个人来观看,预览者可以随时离开通讯,当发布者完成会话时,每个连接的预览者都会收到一个停止的消息并终止其会话,类似直播。cd kurento-tutorial-java/kurento-one2many-call mvn -U clean spring-boot:run \ -Dspring-boot.run.jvmArguments="-Dkms.url=ws://{KMS_HOST}:8888/kurento"功能说明:发布者创建offer,并设置本地描述;发布者将offer发送presenter到服务端;发布者接收到presenterResponse,获取到answer并设置远程描述;预览者创建offer,并设置本地描述;预览者将offer,发送viewer到服务端;预览者收到viewerResponse,获取到answer并设置远程描述;一对N端的视频完成,预览者可以随时离开当发布者完成会话时,每个连接的预览者都会收到一个stopCommunication消息并终止其会话。以下是官方流程图:发布者://创建Offer val mediaConstraints = MediaConstraints() peerConnection.createOffer(this, mediaConstraints) ... //设置本地描述 override fun onCreateSuccess(sd: SessionDescription?) { peerConnection.setLocalDescription(this, sd) } ... //设置本地描述成功后发送到服务端 override fun onSetSuccess() { peerConnection.localDescription?.let { sendPresenter(it.description) } }接收到presenterResponse后将answer设置为远程描述:if (message["response"].asString == "accepted") { val sdpAnswer = message["sdpAnswer"].asString peerConnection.setRemoteDescription( this, SessionDescription(SessionDescription.Type.ANSWER, sdpAnswer) ) }预览者//创建Offer val mediaConstraints = MediaConstraints() peerConnection.createOffer(this, mediaConstraints) ... //设置本地描述 override fun onCreateSuccess(sd: SessionDescription?) { peerConnection.setLocalDescription(this, sd) } //设置本地描述成功后发送到服务端 override fun onSetSuccess() { peerConnection.localDescription?.let { sendViewer(it.description) } }预览者接收到viewerResponse后将answer设置为远程描述: if (message["response"].asString == "accepted") { val sdpAnswer = message["sdpAnswer"].asString peerConnection!!.setRemoteDescription( this, SessionDescription(SessionDescription.Type.ANSWER, sdpAnswer) ) }手机端发起视频:电脑端查看视频:
2022年11月11日
377 阅读
0 评论
0 点赞
2022-10-18
Android传感器说明
名称说明TYPE_ACCELEROMETER加速度传感器TYPE_MAGNETIC_FIELD磁场传感器TYPE_GYROSCOPE陀螺仪传感器TYPE_LIGHT光照传感器TYPE_PRESSURE气压传感器TYPE_TEMPERATURE手机内部温度传感器TYPE_PROXIMITY距离传感器TYPE_GRAVITY重力传感器TYPE_LINEAR_ACCELERATION线性加速度传感器TYPE_ROTATION_VECTOR旋转矢量传感器TYPE_RELATIVE_HUMIDITY湿度传感器TYPE_AMBIENT_TEMPERATURE手机外部温度传感器TYPE_STEP_DETECTOR累计步数传感器TYPE_STEP_COUNTER单次步数传感器TYPE_ORIENTATION方向传感器检查传感器服务//获取传感器服务 sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager //获取当前手机支持的感应器并打印 sensorManager.getSensorList(Sensor.TYPE_ALL).forEach { Log.e(TGA, "Sensor:${it.name} ${it.stringType}") }
2022年10月18日
185 阅读
0 评论
0 点赞
2021-07-10
kotlin-android-extensions 已被弃用,如何使用 @Parcelize?
第 1 步。更新到最新的 kotlin 版本 -1.4.20并替换apply plugin: 'kotlin-android-extensions'toapply plugin: 'kotlin-parcelize'或者plugins { .. id 'kotlin-parcelize' }第 2 步。从 android {} 中删除以下代码androidExtensions { experimental = true }第 3 步。最后,替换旧的 import ->import kotlinx.android.parcel.Parcelizetoimport kotlinx.parcelize.Parcelize新插件:https : //plugins.gradle.org/plugin/org.jetbrains.kotlin.plugin.parcelize
2021年07月10日
517 阅读
0 评论
0 点赞
2020-10-26
WebView监听HTML的事件
添加addJavascriptInterfaceval ws: WebSettings = wv.getSettings() ws.javaScriptEnabled = true wv.addJavascriptInterface(object : Any() { @JavascriptInterface fun performClick(strl: String) { Toast.makeText(this@YourActivity, stringVariable, Toast.LENGTH_SHORT).show() } }, "ok")在onclick中添加对应方法,这里的"ok"是addJavascriptInterface的name参数<button type="button" onclick="ok.performClick();">OK</button>
2020年10月26日
244 阅读
0 评论
0 点赞
2020-09-15
Android加密算法之RSA(二)
The RSA algorithm can only encrypt data that has a maximum byte length of the RSA key length in bits divided with eight minus eleven padding bytes, i.e. number of maximum bytes = key length in bits / 8 - 11.使用RSA时,数据过长会报错,如:javax.crypto.IllegalBlockSizeException: input must be under 256 bytes因此,基本上,您将密钥长度除以8 -11(如果有填充)。例如,如果您具有2048位密钥,则可以加密2048/8 = 256字节(如果有填充则为11字节)。因此,可以使用更大的密钥,也可以使用对称密钥加密数据,然后使用rsa加密该密钥(推荐的方法)参考:https://stackoverflow.com/questions/10007147/getting-a-illegalblocksizeexception-data-must-not-be-longer-than-256-bytes-when通过分段加密解决1.生成公钥私钥@Throws(Exception::class) fun genKeyPair(): Map<String?, String> { val keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM) keyPairGen.initialize(2048) val keyPair = keyPairGen.generateKeyPair() val publicKey = keyPair.public as RSAPublicKey val privateKey = keyPair.private as RSAPrivateKey val keyMap: MutableMap<String?, String> = HashMap(2) keyMap[PUBLIC_KEY] = Base64.encodeToString(publicKey.encoded, Base64.DEFAULT) keyMap[PRIVATE_KEY] = Base64.encodeToString(privateKey.encoded, Base64.DEFAULT) return keyMap }2.公钥加密 @Throws(Exception::class) fun encryptByPublicKey(data: ByteArray, publicKey: String?): ByteArray { val keyBytes: ByteArray = Base64.decode(publicKey, Base64.DEFAULT) val x509KeySpec = X509EncodedKeySpec(keyBytes) val keyFactory = KeyFactory.getInstance(KEY_ALGORITHM) val publicK: Key = keyFactory.generatePublic(x509KeySpec) // 对数据加密 val cipher = Cipher.getInstance(keyFactory.algorithm) cipher.init(Cipher.ENCRYPT_MODE, publicK) val inputLen = data.size val out = ByteArrayOutputStream() var offSet = 0 var cache: ByteArray var i = 0 // 对数据分段加密 while (inputLen - offSet > 0) { cache = if (inputLen - offSet > MAX_ENCRYPT_BLOCK) { cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK) } else { cipher.doFinal(data, offSet, inputLen - offSet) } out.write(cache, 0, cache.size) i++ offSet = i * MAX_ENCRYPT_BLOCK } val encryptedData = out.toByteArray() out.close() return encryptedData }3.私钥解密 @Throws(Exception::class) fun decryptByPrivateKey(encryptedData: ByteArray, privateKey: String?): ByteArray { val keyBytes: ByteArray = Base64.decode(privateKey, Base64.DEFAULT) val pkcs8KeySpec = PKCS8EncodedKeySpec(keyBytes) val keyFactory = KeyFactory.getInstance(KEY_ALGORITHM) val privateK: Key = keyFactory.generatePrivate(pkcs8KeySpec) val cipher = Cipher.getInstance(keyFactory.algorithm) cipher.init(Cipher.DECRYPT_MODE, privateK) val inputLen = encryptedData.size val out = ByteArrayOutputStream() var offSet = 0 var cache: ByteArray var i = 0 // 对数据分段解密 while (inputLen - offSet > 0) { cache = if (inputLen - offSet > MAX_DECRYPT_BLOCK) { cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK) } else { cipher.doFinal(encryptedData, offSet, inputLen - offSet) } out.write(cache, 0, cache.size) i++ offSet = i * MAX_DECRYPT_BLOCK } val decryptedData = out.toByteArray() out.close() return decryptedData }4.调用方法: val keyMap: Map<String?, String?> = RSACrypt.genKeyPair() val publicKey = keyMap[RSACrypt.PUBLIC_KEY] val privateKey = keyMap[RSACrypt.PRIVATE_KEY] val source = "身高一米七左右,标准的九头身,五官立体宛如艺术大师鬼斧神工之作,肌肤赛雪,宛如白玉般似能反射光彩。" + "“我和茹雪两情相悦,但对我们而言,你是第三者,所以请你自己主动退出,我愿意给你一笔钱,你开个价码吧!”啪,一声清脆的响声,干净利落地狠狠扇在韩斌的脸上。" + "乔智愿意当陶家的女婿,除了偿还父亲欠下的人情债之外,还有一个重要原因,陶茹雪本身出众,是琼金一枝花,知名度高,追求者众多。" val data = source.encodeToByteArray() val encodedData: ByteArray = RSACrypt.encryptByPublicKey(data, publicKey) Log.w("rsa", "加密后文字:${Base64.encodeToString(encodedData, Base64.DEFAULT)}") val decodedData: ByteArray = RSACrypt.decryptByPrivateKey(encodedData, privateKey) Log.w("rsa", "解密后文字: \r\n${String(decodedData)}")完整代码object RSACrypt { /** * 加密算法RSA */ const val KEY_ALGORITHM = "RSA" /** * 签名算法 */ const val SIGNATURE_ALGORITHM = "MD5withRSA" /** * 获取公钥的key */ const val PUBLIC_KEY = "RSAPublicKey" /** * 获取私钥的key */ const val PRIVATE_KEY = "RSAPrivateKey" /** * RSA最大加密明文大小 */ private const val MAX_ENCRYPT_BLOCK = 245 /** * RSA最大解密密文大小 */ private const val MAX_DECRYPT_BLOCK = 256 /** * 生成密钥对(公钥和私钥) * @return * @throws Exception */ @Throws(Exception::class) fun genKeyPair(): Map<String?, String> { val keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM) keyPairGen.initialize(2048) val keyPair = keyPairGen.generateKeyPair() val publicKey = keyPair.public as RSAPublicKey val privateKey = keyPair.private as RSAPrivateKey val keyMap: MutableMap<String?, String> = HashMap(2) keyMap[PUBLIC_KEY] = Base64.encodeToString(publicKey.encoded, Base64.DEFAULT) keyMap[PRIVATE_KEY] = Base64.encodeToString(privateKey.encoded, Base64.DEFAULT) return keyMap } /** * 私钥解密 * * @param encryptedData 已加密数据 * @param privateKey 私钥(BASE64编码) * @return * @throws Exception */ @Throws(Exception::class) fun decryptByPrivateKey(encryptedData: ByteArray, privateKey: String?): ByteArray { val keyBytes: ByteArray = Base64.decode(privateKey, Base64.DEFAULT) val pkcs8KeySpec = PKCS8EncodedKeySpec(keyBytes) val keyFactory = KeyFactory.getInstance(KEY_ALGORITHM) val privateK: Key = keyFactory.generatePrivate(pkcs8KeySpec) val cipher = Cipher.getInstance(keyFactory.algorithm) cipher.init(Cipher.DECRYPT_MODE, privateK) val inputLen = encryptedData.size val out = ByteArrayOutputStream() var offSet = 0 var cache: ByteArray var i = 0 // 对数据分段解密 while (inputLen - offSet > 0) { cache = if (inputLen - offSet > MAX_DECRYPT_BLOCK) { cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK) } else { cipher.doFinal(encryptedData, offSet, inputLen - offSet) } out.write(cache, 0, cache.size) i++ offSet = i * MAX_DECRYPT_BLOCK } val decryptedData = out.toByteArray() out.close() return decryptedData } /** * * 公钥加密 * * @param data 源数据 * @param publicKey 公钥(BASE64编码) * @return * @throws Exception */ @Throws(Exception::class) fun encryptByPublicKey(data: ByteArray, publicKey: String?): ByteArray { val keyBytes: ByteArray = Base64.decode(publicKey, Base64.DEFAULT) val x509KeySpec = X509EncodedKeySpec(keyBytes) val keyFactory = KeyFactory.getInstance(KEY_ALGORITHM) val publicK: Key = keyFactory.generatePublic(x509KeySpec) // 对数据加密 val cipher = Cipher.getInstance(keyFactory.algorithm) cipher.init(Cipher.ENCRYPT_MODE, publicK) val inputLen = data.size val out = ByteArrayOutputStream() var offSet = 0 var cache: ByteArray var i = 0 // 对数据分段加密 while (inputLen - offSet > 0) { cache = if (inputLen - offSet > MAX_ENCRYPT_BLOCK) { cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK) } else { cipher.doFinal(data, offSet, inputLen - offSet) } out.write(cache, 0, cache.size) i++ offSet = i * MAX_ENCRYPT_BLOCK } val encryptedData = out.toByteArray() out.close() return encryptedData } }
2020年09月15日
520 阅读
0 评论
0 点赞
2020-08-17
Android加密算法之AES
什么是高级加密标准或AES?AES加密标准又称为高级加密标准Rijndael加密法,是美国国家标准技术研究所NIST旨在取代DES的21世纪的加密标准。AES加密如何工作?AES是一种对称的加密算法,可基于相同的密钥进行加密和解密。AES包含三个分组密码:AES-128,AES-192和AES-256。每个密码分别使用128 位,192位和256位的加密密钥对128 位块中的数据进行加密和解密。代码:object AESCrypt { private const val AES_MODE = "AES/CBC/PKCS7Padding" //"算法/模式/补码方式" private const val CHARSET = "UTF-8" private const val CIPHER = "AES" private const val HASH_ALGORITHM = "SHA-256" private val IV_BYTES = byteArrayOf(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) /** * Generates SHA256 hash of the password which is used as key * * @param password used to generated key * @return SHA256 of the password */ @Throws(NoSuchAlgorithmException::class, UnsupportedEncodingException::class) private fun generateKey(password: String): SecretKeySpec { val digest = MessageDigest.getInstance(HASH_ALGORITHM) val bytes = password.toByteArray(charset(CHARSET)) digest.update(bytes, 0, bytes.size) val key = digest.digest() return SecretKeySpec(key, CIPHER) } /** * Encrypt and encode message using 256-bit AES with key generated from password. * * @param password used to generated key * @param message the thing you want to encrypt assumed String UTF-8 * @return Base64 encoded CipherText * @throws GeneralSecurityException if problems occur during encryption */ @Throws(GeneralSecurityException::class) fun encrypt(password: String, message: String): String { try { val key = generateKey(password) val cipherText = encrypt(key, IV_BYTES, message.toByteArray(charset(CHARSET))) //NO_WRAP is important as was getting \n at the end return Base64.encodeToString(cipherText, Base64.NO_WRAP) } catch (e: UnsupportedEncodingException) { throw GeneralSecurityException(e) } } /** * More flexible AES encrypt that doesn't encode * * @param key AES key typically 128, 192 or 256 bit * @param iv Initiation Vector * @param message in bytes (assumed it's already been decoded) * @return Encrypted cipher text (not encoded) * @throws GeneralSecurityException if something goes wrong during encryption */ @Throws(GeneralSecurityException::class) fun encrypt(key: SecretKeySpec, iv: ByteArray, message: ByteArray): ByteArray { val cipher = Cipher.getInstance(AES_MODE) val ivSpec = IvParameterSpec(iv) cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec) return cipher.doFinal(message) } /** * Decrypt and decode ciphertext using 256-bit AES with key generated from password * * @param password used to generated key * @param base64EncodedCipherText the encrpyted message encoded with base64 * @return message in Plain text (String UTF-8) * @throws GeneralSecurityException if there's an issue decrypting */ @Throws(GeneralSecurityException::class) fun decrypt(password: String, base64EncodedCipherText: String): String { try { val key = generateKey(password) val decodedCipherText = Base64.decode(base64EncodedCipherText, Base64.NO_WRAP) val decryptedBytes = decrypt(key, IV_BYTES, decodedCipherText) return String(decryptedBytes, charset(CHARSET)) } catch (e: UnsupportedEncodingException) { throw GeneralSecurityException(e) } } /** * More flexible AES decrypt that doesn't encode * * @param key AES key typically 128, 192 or 256 bit * @param iv Initiation Vector * @param decodedCipherText in bytes (assumed it's already been decoded) * @return Decrypted message cipher text (not encoded) * @throws GeneralSecurityException if something goes wrong during encryption */ @Throws(GeneralSecurityException::class) fun decrypt(key: SecretKeySpec, iv: ByteArray, decodedCipherText: ByteArray): ByteArray { val cipher = Cipher.getInstance(AES_MODE) val ivSpec = IvParameterSpec(iv) cipher.init(Cipher.DECRYPT_MODE, key, ivSpec) return cipher.doFinal(decodedCipherText) } }添加扩展方法,方便调用private const val key: String = "Your AES Key" fun encrypt(input: String): String { return AESCrypt.encrypt(key, input) } fun decrypt(input: String): String { return AESCrypt.decrypt(key, input) } fun String.aesEncrypt(): String = encrypt(this) fun String.aesDecrypt(): String = decrypt(this)使用方法"明文".aesEncrypt() "密文".aesDecrypt()
2020年08月17日
349 阅读
0 评论
0 点赞
2020-04-10
Android P http网络请求失败
最近更新了android studio,以及到gradle和android sdk版本 ;手机Pixel 2 xl,系统Android P;网络请求okhttp3,每次网络请求总是:java.net.UnknownServiceException: CLEARTEXT communication ** not permitted by network security policy官网文档:Android致力于保护用户,他们的设备和数据安全。我们保证数据安全的方法之一是保护所有进入或离开Android设备的数据在传输中使用传输层安全性(TLS)。正如我们在Android P开发人员预览中所宣布的那样,我们通过阻止针对Android P的应用程序默认允许未加密的连接来进一步改进这些保护。这是我们多年来为更好地保护Android用户而做出的各种更改。为了防止意外的未加密连接,我们android:usesCleartextTraffic在Android Marshmallow中引入了manifest属性。在Android Nougat中,我们通过创建Network Security Config功能扩展了该属性,该功能允许应用程序指示他们不打算在没有加密的情况下发送网络流量。在Android Nougat和Oreo中,我们仍然允许明文连接。看来以后都要用https了,在Android P系统的设备上,如果应用使用的是非加密的明文流量的http网络请求,则会导致该应用无法进行网络请求,https则不会受影响,同样地,如果应用嵌套了webview,webview也只能使用https请求。目前找到的解决办法:1:改用https2:targetSdkVersion改为27以下3:在res的xml目录下,新建一个xml文件(名称自定义,如:network_security_config.xml),内容如下:<?xml version="1.0" encoding="utf-8"?> <network-security-config> <base-config cleartextTrafficPermitted="true" /> </network-security-config><application ... android:networkSecurityConfig="@xml/network_security_config" ... />更多参考:https://android-developers.googleblog.com/2018/04/protecting-users-with-tls-by-default-in.html原文:https://www.jianshu.com/p/fb7374b544aa
2020年04月10日
1,230 阅读
0 评论
0 点赞