再见。
我正在尝试在服务器和Android应用程序之间实现RSA加密。我的目标是:
1.与应用共享公钥。
1.应用程序执行加密并将加密文本发送到服务器。
1.服务器使用私钥解密文本。
在Android应用程序之前,我也做了同样的步骤,但使用的是Java应用程序,它工作起来很有魅力,但问题是客户端是如何在该应用程序中创建的...在服务器端,我做了一个Soap Web服务,它返回公钥的字节数组:
@WebMethod(operationName = "getKey")
public byte[] getPublicKey() {
try {
// Message Context
MessageContext mctx = wsctx.getMessageContext();
// Getting parameter services
ParameterServices params = ParameterServices.NewParameterServices(getHttpServletRequestFromMsgContext(mctx), "Dumb Payload");
// Initializing KEYGEN Object
initKeyGen(params);
// Checking if there are no keys created
KEYGEN.updateKeys();
// Creating and sending public key
return KEYGEN.generatePublicKey();
} catch (Throwable e) {
LOGGER.error("Exception in KeyService", e);
return new byte[0];
}
}
在Java应用程序中,我使用javax.jws创建了客户端接口,如下所示:
@WebService(targetNamespace = "http://my.targetname.value/")
@SOAPBinding(style = SOAPBinding.Style.RPC)
public interface KeyService {
@WebMethod
byte[] getPublicKey();
}
这就是我非常确定问题所在的地方,我如何检索字节数组来生成公钥,就像这样:
try {
URL url = new URL(WSDLURL);
QName qname =
new QName(TARGETNAMESPACE, SERVICENAME);
Service service = Service.create(url, qname);
KeyService keyserv = service.<KeyService>getPort(KeyService.class);
byte[] key = keyserv.getPublicKey();
X509EncodedKeySpec spec = new X509EncodedKeySpec(key);
KeyFactory kf = KeyFactory.getInstance("RSA");
publicKey = kf.generatePublic(spec);
return true;
} catch (MalformedURLException|java.security.NoSuchAlgorithmException|java.security.spec.InvalidKeySpecException e) {
e.printStackTrace();
return false;
}
此行:
字节[]密钥=密钥服务器.获取公钥();
我没有对字节做任何事情,我只是获取它并完成,无论从服务器抛出方法java.security.Key.getEncoded()。
- 安卓Kotlin版本**
首先,我试着把JAXB导入到android中,在尝试的过程中我死了,后来我从这个问题中找到了android库ksoap2,实现几乎是一样的,依赖也是一样的(ksoap2-android-2.5.2,kxml2 - 2.2.1...)
class KeyServiceKSoap {
val NAMESPACE = "http://my.targetname.value/"
val URL = "http://192.168.0.11:8080/RSATest/keyService?wsdl"
val METHOD_NAME = "obtainKey"
val SOAP_ACTION = "http://my.targetname.value/RSATest/obtainKeyRequest"
private var thread: Thread? = null
fun fetchByteArray(getKey : MutableLiveData<String>) {
thread = object : Thread() {
override fun run() {
try {
Log.d("KeyService", "Starting...")
val request = SoapObject(NAMESPACE, METHOD_NAME)
val envelope = SoapSerializationEnvelope(
SoapEnvelope.VER12
)
envelope.env = "http://schemas.xmlsoap.org/soap/envelope/"
envelope.setOutputSoapObject(request)
envelope.headerOut = Array(1) { buildAuthHeaders() }
val androidHttpTransport = HttpTransportSE(
URL,
30000
)
androidHttpTransport.call(SOAP_ACTION, envelope)
val objectResult: SoapObject = envelope.bodyIn as SoapObject
getKey.postValue(objectResult.getProperty("return").toString())
} catch (sp: SoapFault) {
sp.printStackTrace()
getKey.postValue("FAILURE")
} catch (e: Exception) {
e.printStackTrace()
getKey.postValue("FAILURE")
}
}
}
thread!!.start()
}
private fun buildAuthHeaders() : Element {
val authorization = Element().createElement(NAMESPACE, "Authorization")
authorization.addChild(Node.TEXT, "BEARER")
return authorization
}
}
- 问题所在**
我的问题是我得到的响应是一个字符串,就像你在这行看到的:
对象结果. getProperty("返回"). toString()
如果你检查Java应用程序客户端,我直接得到数组值,而不是字符串。这里的问题是,我不知道什么样的编码是做方法java. security. Key. getEncoded()。我的意思是,如果我知道,例如,这个方法是返回一个Base64编码的字节数组,在Android应用程序中,我只需要像这样解码和完成:
private fun makeKey(encodedString : String) : Boolean {
return try {
val byteArr = Base64.decode(encodedString, Base64.DEFAULT);
val specifications = X509EncodedKeySpec(byteArr)
val factory: KeyFactory = KeyFactory.getInstance("RSA")
publicKey = factory.generatePublic(specifications)
true
} catch (e : Exception) {
e.printStackTrace()
false
}
}
- 但显然不是这样**,如果我加密并将编码后的字符串发送到服务器,而服务器试图解密此字符串,我将获得javax. crypto. BadPaddingException:解密错误,因为字节数组未使用Base64编码...
是否有方法在ksoap2 Android中接收字节数组,就像在Java应用程序中创建的接口一样?
如果别无选择,我必须解码字符串(在Android应用程序中生成RSA公钥)...什么是正确的方法?
2条答案
按热度按时间db2dz4w81#
我为您编写了一些代码,您可以在https://github.com/XSComSoft/stack-overflow中看到完整的代码
在测试代码包
org.exmaple.Stack74979278
的主副本和两个副本中,分别包含客户端代码和服务器代码如果使用
idea
作为maven项目打开,则可以在类org.example.stack74979278.Main
上简单地运行测试通信只需要依靠
java jws
下面是一些代码片段
密钥服务.java
密钥服务实现.java
RSAUtil.java语言
客户端生成器为cmd
wsimport -keep -p org.example.stack74979278 http://localhost/test?wsdl
,详见a客户端测试类
现在,您可以获取
public key byte
for android客户端并将encrypted base64 string
发送到服务器uemypmqf2#
我不知道该怎么开始,显然,我现在所做的,接收一个字节数组的方法,是正确的。
使用ksoap 2可以获得响应字符串,然后解码String以获得字节数组。
我为什么这么肯定呢?嗯,是因为让我头疼的事情和编码无关。而是我如何初始化Cipher。正如我之前所说,对于Java应用程序,一切都正常工作,但是当在Android应用程序中使用相同的实现时(Java应用程序〈=〉Android应用程序),解密失败。
我初始化密码的过程是这样的:
根据这个question的评论如果你这样初始化它,会发生什么是,Android应用程序和服务器将使用它们的独立实现.这就是为什么它总是在解密失败的时刻.
为了让一切变得清晰
如果您尝试执行与我相同的操作,请尝试在两端(服务器和应用程序)使用RSA/ECB/PKCS 1 Padding初始化所有密码
其他的东西,如KeyFactory和KeyPairGenerator,只能用“RSA”初始化。
P.D:我从来没有在我的SOAP服务中将键的字节数组编码为Base64......这让我认为在某些时候默认使用Base64编码