我有以下代碼用于 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 變數 publicKeyString 并嘗試使用從 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 密鑰生成的代碼 - 特別是 - LbRsa.pasclass procedure TRSA.GenerateRSAKeysEx(var PrivateKey, PublicKey : TLbRSAKey; KeySize : TLbAsymKeySize; PrimeTestIterations : Byte; Callback : TLbRSACallback);但是這個代碼是完全通用的 - 生成大整數并將其存盤為成員變數,然后使用我上面已經提供的代碼流式傳輸到字串。
然后我嘗試在 Android/javax 中生成 RSA 密鑰并檢查它們是否與 LockBox 生成的密鑰相同。我使用了以下代碼:
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)
我仍在尋求將其轉換為字串以看起來像 LockBox 生成的密鑰 - 只是為了比較。
但是現階段我已經很困惑了-為什么我必須為密鑰生成提供鏈接模式和填充方案?我的理解是密鑰只是編碼的大整數。并且鏈接模式,填充方案僅用于加密/解密?當然,我需要提供密鑰大小,這是可以理解的。
所以 - 我的目標是配置 Android/javax RSA 密鑰生成和 RSA 密鑰使用,使其完全符合 RSA 密鑰生成并在 Delphi 10.2 LockBox 2 中使用。我的目標是在 Android 程式中使用 LockBox 生成的密鑰。我已經描述了我已經采用的路徑數量,但我仍然沒有設法生成與 Delphi 密鑰格式相同的 javax 密鑰。因為我不知道他們在 LockBox 中的密鑰的確切配置(我猜 - 除了密鑰大小之外,沒有其他密鑰)我也無法在 Android/javax 上配置我的加密/解密。
如何通過更改 Android/javax 代碼來實作這種一致性?
附加資訊:Delphi LbAsym.pasprocedure TLbAsymmetricKey.StoreToStream(aStream : TStream);非常重要,因為它將鍵整數保存到流中。在不使用密碼短語的情況下,代碼非常緊張:
aStream.Write(KeyBuf, Len);
Pascal 代碼中的注釋如下:
save key to ASN.1 format stream (encrypt if necessary)
所以,也許我的問題需要重新表述——如何以 ASN.1 格式保存 Android/javax 生成的密鑰或從 ASN.1 格式讀取密鑰?
附加資訊 2:我正在尋找將 Java 生成的密鑰保存為 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 生成的字串(來自 Additional Info 2)是一個長約 25 個字符的 gan Delphi 生成的(基于 64 編碼的)字串(當然,我已經洗掉了一些帶有 PEM 檔案的初始和尾隨字串,它們只是像 '===PUBLIC KEY 這樣的常量===') 并且它可以在我的初始代碼中完美地用作 publicKeyString - 使用這種公鑰的加密作業完美。Delphi 公鑰字串短了大約 25 個字符并且不起作用。
所以 - 我已經弄清楚每個步驟中發生了什么并嘗試建立并行步驟,但最終我未能找到解決方案。
附加資訊 4:這里是 base64 編碼的公鑰:
從德爾福生成:
MIGIAoGBALtEMVXxHBWzBx/AzO/aOHrYEQZB3VlqYBvqX/SHES7ehERXaCbUO5aEwyZcDrdh2dMTy/abNDaFJK4bEqghpC6yvCNvnTqjAz bsD9UqS0w5CUh3KHwqhPv HFGcF7rAuU9uoJcWXbTC9tUBEG7rdmdmMatIgL1Y4ebOACQHn1xAgIlKg==
從 Android Kotlin/java 生成:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCuQi7gMZwWL1iEhNVgdu23S/rYYhtntXQlfVVBjcGiSE8EXzjjnZHxcYHcIszV0F6F20msGK8MFernJpWg8k7J3GLH4TYkQwEEy6jWnRdEB3uqQWFCNQ/CflCHtq1o1iSS0qmXcHQuI7zZ0cHd5FNDg4Bl/DveftEje9yTgUXN3wIDAQAB
我不確定,但也許有一些在線服務可以對這些字串進行base64解碼,然后根據某種方案從中提取大整數并檢測格式。
uj5u.com熱心網友回復:
如評論中所述,Delphi 代碼生成 PKCS#1 格式的公鑰,而 Kotlin 代碼需要 X.509/SPKI 格式的密鑰。
使用 BouncyCastle,Kotlin 代碼可以匯入 PKCS#1 公鑰。這需要類PEMParser和JcaPEMKeyConverter.
例子:
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.bouncycastle.openssl.PEMParser
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter
import javax.crypto.Cipher
import java.security.PublicKey
import java.io.FileReader
import java.util.Base64
...
val inputFile: String = "<path to PKCS#1 PEM file>"
// Key import
var publicKey: PublicKey? = null
FileReader(inputFile).use { fileReader ->
PEMParser(fileReader).use { pemParser ->
val spki: SubjectPublicKeyInfo = pemParser.readObject() as SubjectPublicKeyInfo
val converter = JcaPEMKeyConverter()
converter.setProvider(BouncyCastleProvider())
publicKey = converter.getPublicKey(spki)
}
}
// Encryption
val cipher: Cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
cipher.init(Cipher.ENCRYPT_MODE, publicKey)
val ciphertext: ByteArray = cipher.doFinal("The quick brown fox jumps over the lazy dog".toByteArray())
val ciphertextB64: String = Base64.getEncoder().encodeToString(ciphertext);
println(ciphertextB64)
該代碼以 PKCS#1 格式匯入 PEM 編碼的公鑰。PEM 編碼包括特定格式的頁眉和頁腳,并且在正文中,每 64 個字符后有換行符。PEM 編碼的 Delphi 密鑰是:
-----BEGIN RSA PUBLIC KEY-----
MIGIAoGBALtEMVXxHBWzBx/AzO/aOHrYEQZB3VlqYBvqX/SHES7ehERXaCbUO5aE
wyZcDrdh2dMTy/abNDaFJK4bEqghpC6yvCNvnTqjAz bsD9UqS0w5CUh3KHwqhPv
HFGcF7rAuU9uoJcWXbTC9tUBEG7rdmdmMatIgL1Y4ebOACQHn1xAgIlKg==
-----END RSA PUBLIC KEY-----
不幸的是,您的 Delphi 密鑰的匯入不起作用。顯示了一個依賴于提供程式的錯誤訊息,例如在 BouncyCastleProvider 的情況下:
PEMException: unable to convert key pair: encoded key spec not recognized: RSA publicExponent is even
實際上,Delphi 代碼生成的密鑰有一個偶數公開指數,其值為 9514 (0x252A):
0:d=0 hl=3 l= 136 cons: SEQUENCE
3:d=1 hl=3 l= 129 prim: INTEGER :BB443155F11C15B3071FC0CCEFDA387AD8110641DD596A601BEA5FF487112EDE8444576826D43B9684C3265C0EB761D9D313CBF69B34368524AE1B12A821A42EB2BC236F9D3AA3033F9BB03F54A92D30E42521DCA1F0AA13EFF87146705EEB02E53DBA825C5976D30BDB540441BBADD99D98C6AD2202F563879B3800901E7D71
135:d=1 hl=2 l= 2 prim: INTEGER :252A
這不應該是這種情況(φ(n) 或 λ(n) 并且 e 將不是互質數),s。在這里。因此,您應該檢查 Delphi 代碼中的密鑰生成。
另一個問題是:
val result: String = String(bytes, Charsets.UTF_8)
密文(通常包含不符合 UTF-8 的位元組序列)的 UTF-8 解碼會破壞該密文。
如果要將密文轉換為字串,則必須應用二進制到文本的編碼,例如 Base64。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/513253.html
上一篇:例程可以知道它運行的單元嗎?
下一篇:軟邊緣圓形影像
