我有以下代码,用于生成Delphi 10.2 TurboPower Lockbox 2 RSA密钥,并将其表示为某个字符串:
//Object properties
object LbRSA1024: TLbRSA
PrimeTestIterations = 20
KeySize = aks1024
Left = 416
Top = 248
end
//Key generation - so simple!
LbRSA1024.GenerateKeyPair;
//Getting generated key as string
function TMainForm.GetPublicKey1024AsString: string;
var
str1, str2: TStringStream;
begin
Result:='';
if (LbRSA1024.PublicKey.Exponent.Int.dwUsed = 0)
or (LbRSA1024.PublicKey.Modulus.Int.dwUsed = 0) then
exit;
str1:= TStringStream.Create('');
str2:= TStringStream.Create('');
try
LbRSA1024.PublicKey.StoreToStream(str1);
str1.Position:=0;
//LbEncodeBase64(str1,str2);
TLbBase64.LbEncodeBase64(str1,str2);
Result:=str2.DataString;
finally
str1.Free;
str2.Free;
end;
end;
我获得了大约200个字符的公钥字符串(例如...dh2dMTy/ab...)我将他的字符串赋给Android Kotlin变量Public KeyString,并尝试使用从Delphi生成的这个公钥加密其他字符串。Android Kotlin代码为:
val publicKeyBytes: ByteArray = Base64.decode(publicKeyString, Base64.DEFAULT)
val X509PublicKey: X509EncodedKeySpec = X509EncodedKeySpec(publicKeyBytes)
val kf: KeyFactory = KeyFactory.getInstance("RSA")
val publicKey: PublicKey = kf.generatePublic(X509PublicKey)
val cipher: Cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
cipher.init(Cipher.ENCRYPT_MODE, publicKey)
val bytes = cipher.doFinal(s.toByteArray())
val result: String = String(bytes, Charsets.UTF_8)
通常它不起作用-密钥不被接受,错误消息是(我使用上面代码的一些变体进行了试验):
Exception in thread "main" java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: algid parse error, not a sequence
at sun.security.rsa.RSAKeyFactory.engineGeneratePublic (:-1)
...
Exception in thread "main" java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format
at sun.security.rsa.RSAKeyFactory.engineGeneratePublic (:-1)
at java.security.KeyFactory.generatePublic (:-1)
因此,Delphi Lockbox和Android/javax RSA可能具有不同的密钥格式。所以-我试着做了两件事。首先,我检查了Delphi密钥生成的代码--Speciicaly-LbRsa.pas class procedure TRSA.GenerateRSAKeysEx(var PrivateKey, PublicKey : TLbRSAKey; KeySize : TLbAsymKeySize; PrimeTestIterations : Byte; Callback : TLbRSACallback);
,但这段代码完全是泛型的--大整数被生成并存储为成员变量,然后用我在上面提供的代码流到字符串中。
然后我尝试在Android/javax中生成RSA密钥,并检查它们是否与密码箱生成的密钥相同。我使用了以下代码:
val REG_KEY: String = "REG_KEY"
val generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA /*, ANDROID_KEYSTORE */)
val builder = KeyGenParameterSpec.Builder(REG_KEY,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setKeySize(1024)
.setBlockModes(KeyProperties.BLOCK_MODE_ECB)
//.setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
generator.initialize(builder.build())
val keys = generator.generateKeyPair()
Log.i( "Cryptic", keys.private.toString())
Log.i( "Cryptic", keys.public.toString())
Log.i( "Cryptic", keys.private.encoded.contentToString())
Log.i( "Cryptic", keys.public.encoded.contentToString())
Log.i( "Cryptic", keys.private.encoded.toString(Charsets.UTF_8))
Log.i( "Cryptic", keys.public.encoded.toString(Charsets.UTF_8))
我遇到错误消息,因为私钥为空,但公钥是生成的:
java.lang.NullPointerException: keys.private.encoded must not be null
at com.batsoft.stockmobile.service.Cryptography.encryptString(Cryptography.kt:35)
我仍然在寻求将其转换为字符串,以看起来像锁箱生成的钥匙-只是为了比较。
但在现阶段,我已经感到困惑--为什么我要为密钥生成提供链接模式和填充方案?我的理解是,键只是编码的大整数。和链接模式,填充方案仅用于加密/解密?当然,我需要提供密钥大小,这是可以理解的。
所以-我的目标是配置Android/javax RSA密钥生成和RSA密钥使用,使其完全符合在Delphi 10.2 Lockbox 2中生成和使用的RSA密钥。我的目标是在Android程序中使用Lockbox生成的密钥。我已经描述了我已经采用的路径的数量,但我仍然没有设法以与Delphi键相同的格式生成javax键。由于我不知道密码箱中密钥的确切配置(我猜--除了密钥大小,没有其他密钥),我也无法在Android/javax上配置我的加密/解密。
如何通过更改Android/javax代码来实现这种整合?
附加信息:Delphi LbAsym.pas procedure TLbAsymmetricKey.StoreToStream(aStream : TStream);
非常重要,因为它将key-整数保存到流中。在不使用密码短语的情况下,代码非常简单:
aStream.Write(KeyBuf, Len);
PASCAL代码中的注解如下所示:
save key to ASN.1 format stream (encrypt if necessary)
那么,也许我的问题可以重新表述--如何将Android/javax生成的密钥保存为ASN.1格式或从ASN.1格式读取密钥?
附加信息2:我正在寻找将Java生成的key保存为PEM字符串的方法,这可以通过Android Kotlin代码来实现:
val generator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA /*, ANDROID_KEYSTORE */)
val builder = KeyGenParameterSpec.Builder(REG_KEY,
KeyProperties.PURPOSE_ENCRYPT and KeyProperties.PURPOSE_DECRYPT)
.setKeySize(1024)
.setBlockModes(KeyProperties.BLOCK_MODE_ECB)
//.setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
generator.initialize(builder.build())
val keys = generator.generateKeyPair()
//https://stackoverflow.com/questions/25129822/export-rsa-public-key-to-pem-string-using-java
val writer = StringWriter()
val pemWriter = PemWriter(writer)
pemWriter.writeObject(PemObject("PUBLIC KEY", keys.public.encoded))
pemWriter.flush()
pemWriter.close()
Log.i( "Cryptic", writer.toString())
请注意,这需要添加Gradle依赖项:
implementation 'org.bouncycastle:bcpkix-jdk15to18:1.68'
implementation 'org.bouncycastle:bcprov-jdk15to18:1.68'
为了使用Delphi生成的密钥(显然,Lockbox 2生成它们并将其保存为PEM字符串),我需要解析PEM字符串,并将它们指定为密钥javax Cipher。
附加信息3:这很奇怪。Java生成的字符串(来自附加信息2)是一个大约25个字符的GAN Delphi生成的(基于64位编码的)字符串(当然,我已经删除了一些与PEM文件有关的首字母和尾随字符串,它们只是像‘=公钥=’这样的常量),它可以在我的初始代码中完美地用作Public KeyString-使用这样的公钥加密工作得很好。Delphi公钥字符串大约短了25个字符,不起作用。
所以-我已经弄清楚了每一步的内部情况,并试图建立并行的步骤,但最终我找不到解决方案。
附加信息4:这里是Base64编码的公钥:
由Delphi生成:
MIGIAoGBALtEMVXxHBWzBx/AzO/aOHrYEQZB3VlqYBvqX/SHES7ehERXaCbUO5aEwyZcDrdh2dMTy/abNDaFJK4bEqghpC6yvCNvnTqjAz+bsD9UqS0w5CUh3KHwqhPv+HFGcF7rAuU9uoJcWXbTC9tUBEG7rdmdmMatIgL1Y4ebOACQHn1xAgIlKg==
Android Kotlin/Java生成:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCuQi7gMZwWL1iEhNVgdu23S/rYYhtntXQlfVVBjcGiSE8EXzjjnZHxcYHcIszV0F6F20msGK8MFernJpWg8k7J3GLH4TYkQwEEy6jWnRdEB3uqQWFCNQ/CflCHtq1o1iSS0qmXcHQuI7zZ0cHd5FNDg4Bl/DveftEje9yTgUXN3wIDAQAB
我不确定,但也许有一些在线服务可以对这些字符串进行Base64解码,然后根据某种方案从它们中提取大整数并检测格式。
1条答案
按热度按时间9udxz4iz1#
如注解中所述,Delphi代码生成PKCS#1格式的公钥,而Kotlin代码需要X.509/SPKI格式的密钥。
使用BouncyCastle,Kotlin代码可以导入PKCS#1公钥。这需要类
PEMParser
和JcaPEMKeyConverter
。示例:
该代码以PKCS#1格式导入PEM编码的公钥。PEM编码包括特定于格式的页眉和页脚,在正文中,每64个字符之后都有换行符。PEM编码的Delphi密钥为:
不幸的是,您的Delphi密钥的导入不起作用。会显示一条与提供程序相关的错误消息,例如BouncyCastleProvider:
实际上,Delphi代码生成的密钥具有一个偶数公共指数,其值为9514(0x252A):
不应该是这种情况(φ(N)或λ(N),则e不是互质的),s。因此,您应该检查Delphi代码中的密钥生成。
另一个问题是:
对密文(通常包含不符合UTF-8的字节序列)的UTF-8解码会损坏该密文。
如果要将密文转换为字符串,则必须应用二进制到文本的编码,如Base64。