![](/img/trans.png)
[英].Net encryption and java decryption with RSA/ECB/PKCS1Padding
[英]Difference between PKCS1-padding/RSA encryption ios objc and java
我正在為ios和Android開發應用程序。 我對加密任務比較陌生,在過去的3天里,我不停地撞牆,因為我無法運行RSA加密。
兩個客戶端都從Java服務器接收公鑰。 在android我有(顯然,因為它幾乎與服務器端的代碼相同)沒有麻煩,但ios部分似乎根本不兼容。 我想用公鑰加密一小段數據(aes密鑰),這就是我在Java中的做法:
try {
String publickey = "MCwwDQYJKoZIhvcNAQEBBQADGwAwGAIRAK+dBpbOKw+1VKMWoFxjU6UCAwEAAQ==";
byte[] bArr = Crypto.base64Decode(publicKey, false);
KeyFactory keyFactory = KeyFactory.getInstance("RSA", "BC");
EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKey);
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
Cipher cipher = Cipher.getInstance("RSA/NONE/PKCS1Padding", "BC");
cipher.init(1,publicKey);
int cipherBlockSize = cipher.getBlockSize();
ByteArrayOutputStream bArrOut = new ByteArrayOutputStream();
bArrOut.flush();
int pos = 0;
Log.i("ContentBufferLength", contentBuffer.length+"");
while (true) {
if (cipherBlockSize > contentBuffer.length - pos) {
cipherBlockSize = contentBuffer.length - pos;
}
Log.i("CipherBlockSize", cipherBlockSize+"");
byte[] tmp = cipher.doFinal(contentBuffer, pos, cipherBlockSize);
bArrOut.write(tmp);
pos += cipherBlockSize;
if (contentBuffer.length <= pos) {
break;
}
}
bArrOut.flush();
encryptedBuffer = bArrOut.toByteArray();
bArrOut.close();
} catch (Exception ex) {
throw ex;
}
// Log.i("Encrypted Buffer Length", encryptedBuffer.length+"");
return encryptedBuffer;
這是我的(不正常工作)ios代碼,從這里借來的:
http://blog.wingsofhermes.org/?p=75和蘋果加密練習。
-(NSString* )encryptWithPublicKey:(NSString*)key input:(NSString*) input {
const size_t BUFFER_SIZE = 16;
const size_t CIPHER_BUFFER_SIZE = 16;
//const uint32_t PADDING = kSecPaddingNone;
const uint32_t PADDING = kSecPaddingPKCS1;
static const UInt8 publicKeyIdentifier[] = "de.irgendwas.app";
NSData *publicTag;
publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)];
NSMutableDictionary *publicKey2 = [[NSMutableDictionary alloc] init];
[publicKey2 setObject:kSecClassKey forKey:kSecClass];
[publicKey2 setObject:kSecAttrKeyTypeRSA forKey:kSecAttrKeyType];
[publicKey2 setObject:publicTag forKey:kSecAttrApplicationTag];
SecItemDelete((CFDictionaryRef)publicKey2);
NSData *strippedPublicKeyData = [NSData dataFromBase64String:key];
unsigned char * bytes = (unsigned char *)[strippedPublicKeyData bytes];
size_t bytesLen = [strippedPublicKeyData length];
size_t i = 0;
if (bytes[i++] != 0x30)
[Exception raise:FAILURE function:__PRETTY_FUNCTION__ line:__LINE__ description:@"Could not set public key."];
/* Skip size bytes */
if (bytes[i] > 0x80)
i += bytes[i] - 0x80 + 1;
else
i++;
if (i >= bytesLen)
[Exception raise:FAILURE function:__PRETTY_FUNCTION__ line:__LINE__ description:@"Could not set public key."];
if (bytes[i] != 0x30)
[Exception raise:FAILURE function:__PRETTY_FUNCTION__ line:__LINE__ description:@"Could not set public key."];
/* Skip OID */
i += 15;
if (i >= bytesLen - 2)
[Exception raise:FAILURE function:__PRETTY_FUNCTION__ line:__LINE__ description:@"Could not set public key."];
if (bytes[i++] != 0x03)
[Exception raise:FAILURE function:__PRETTY_FUNCTION__ line:__LINE__ description:@"Could not set public key."];
/* Skip length and null */
if (bytes[i] > 0x80)
i += bytes[i] - 0x80 + 1;
else
i++;
if (i >= bytesLen)
[Exception raise:FAILURE function:__PRETTY_FUNCTION__ line:__LINE__ description:@"Could not set public key."];
if (bytes[i++] != 0x00)
[Exception raise:FAILURE function:__PRETTY_FUNCTION__ line:__LINE__ description:@"Could not set public key."];
if (i >= bytesLen)
[Exception raise:FAILURE function:__PRETTY_FUNCTION__ line:__LINE__ description:@"Could not set public key."];
strippedPublicKeyData = [NSData dataWithBytes:&bytes[i] length:bytesLen - i];
DLog(@"X.509 Formatted Public Key bytes:\n%@",[strippedPublicKeyData description]);
if (strippedPublicKeyData == nil)
[Exception raise:FAILURE function:__PRETTY_FUNCTION__ line:__LINE__ description:@"Could not set public key."];
CFTypeRef persistKey = nil;
[publicKey2 setObject:strippedPublicKeyData forKey:kSecValueData];
[publicKey2 setObject: (kSecAttrKeyClassPublic) forKey:kSecAttrKeyClass];
[publicKey2 setObject:[NSNumber numberWithBool:YES] forKey:kSecReturnPersistentRef];
OSStatus secStatus = SecItemAdd((CFDictionaryRef)publicKey2, &persistKey);
if (persistKey != nil) CFRelease(persistKey);
if ((secStatus != noErr) && (secStatus != errSecDuplicateItem))
[Exception raise:FAILURE function:__PRETTY_FUNCTION__ line:__LINE__ description:@"Could not set public key."];
SecKeyRef keyRef = nil;
[publicKey2 removeObjectForKey:kSecValueData];
[publicKey2 removeObjectForKey:kSecReturnPersistentRef];
[publicKey2 setObject:[NSNumber numberWithBool:YES] forKey:kSecReturnRef];
[publicKey2 setObject: kSecAttrKeyTypeRSA forKey:kSecAttrKeyType];
SecItemCopyMatching((CFDictionaryRef)publicKey2,(CFTypeRef *)&keyRef);
if (!keyRef)
[Exception raise:FAILURE function:__PRETTY_FUNCTION__ line:__LINE__ description:@"Could not set public key."];
uint8_t *plainBuffer;
uint8_t *cipherBuffer;
uint8_t *decryptedBuffer;
const char inputString[] = "1234";
int len = strlen(inputString);
// TODO: this is a hack since i know inputString length will be less than BUFFER_SIZE
if (len > BUFFER_SIZE) len = BUFFER_SIZE-1;
plainBuffer = (uint8_t *)calloc(BUFFER_SIZE, sizeof(uint8_t));
cipherBuffer = (uint8_t *)calloc(CIPHER_BUFFER_SIZE, sizeof(uint8_t));
decryptedBuffer = (uint8_t *)calloc(BUFFER_SIZE, sizeof(uint8_t));
strncpy( (char *)plainBuffer, inputString, len);
size_t plainBufferSize = strlen((char *)plainBuffer);
size_t cipherBufferSize = CIPHER_BUFFER_SIZE;
NSLog(@"SecKeyGetBlockSize() public = %lu", SecKeyGetBlockSize(keyRef));
// Error handling
// Encrypt using the public.
OSStatus status = noErr;
status = SecKeyEncrypt(keyRef,
PADDING,
plainBuffer,
plainBufferSize,
&cipherBuffer[0],
&cipherBufferSize
);
NSLog(@"encryption result code: %ld (size: %lu)", status, cipherBufferSize);
return [[[NSString stringWithFormat:@"%s",cipherBuffer] dataUsingEncoding:NSUTF8StringEncoding] base64EncodedString];
}
出於測試目的和簡單性,我試圖僅加密長度為4個字節的輸入。 這應該小到足以適合一個街區。 公鑰導入和加密過程似乎有效,但與android方法相比,我總是收到更長的輸出。
到目前為止我遇到的唯一區別是SecKeyGetBlockSize returns 16
並且在java中cipher.blocksize返回5.我認為其他11個字節是為pkcs1填充保留的,但是如何在ios/objc
強制執行相同的行為?
嘗試將密文分成多個部分,以便每個部分包含16個字符長並分別解碼它們。 我也遇到了同樣的問題,但是在PHP中存在了很長時間,而且上面的技巧對我有用。
這可能有助於您擺脫問題。
解碼Base64密鑰給出:
MCwwDQYJKoZIhvcNAQEBBQADGwAwGAIRAK+dBpbOKw+1VKMWoFxjU6UCAwEAAQ==
-> 302c300d06092a864886f70d0101010500031b003018021100af9d0696ce2b0fb554a316a05c6353a50203010001
將其解釋為DER編碼的ASN.1,我們發現:
30(2c) //SEQUENCE
30(0d) //SEQUENCE
06(09): 2a 86 48 86 f7 0d 01 01 01 //OID 1.2.840.113548.1.1.1 (RSA Encryption)
05(00): //NULL
03(1b): [00] 30 18 02 11 00 af 9d 06 96 ce 2b 0f b5 54 a3 16 a0 5c 63 53 a5 02 03 01 00 01 //BITSTRING
BITSTRING似乎也包含DER編碼的ASN.1:
30(18) //SEQUENCE
02(11): 00 af 9d 06 96 ce 2b 0f b5 54 a3 16 a0 5c 63 53 a5 02 03 01 00 01 //INTEGER
= 0xaf9d0696ce2b0fb554a316a05c6353a50203010001
通過IOS代碼,您可以看到它正在解析DER編碼的ASN.1。 它正確識別前兩個SEQUENCE標記,並跳過OID字段,甚至不驗證它是否為OID。 然后出現問題:IOS代碼期望下一個標記為BITSTRING(0x03)---但在我們的數據中,我們有一個額外的NULL(0x05)字段來表示公共指數是隱式的。 IOS代碼在遇到0x05標記時引發異常。 如果沒有NULL,我們會看到IOS代碼已經成功提取了BITSTRING的內容。
所以:NULL是一個可選字段,IOS代碼不允許它,或者IOS代碼期望不同的ASN.1結構。 例如,看起來BITSTRING也是DER編碼的ASN.1 INTEGER(可能是RSA模數)。 然而IOS代碼沒有嘗試解析它。 可能是IOS SecKeyEncrypt
例程期望模數的這種格式,或者可能是調用者應該提取模數的原始字節。
所以仍然需要一些實驗。 但是,如果此代碼要解析提供的數據對象,則必須使用以下附加條件:
/* Skip OID */
i += 15;
if (i >= bytesLen - 2)
[Exception raise:FAILURE function:__PRETTY_FUNCTION__ line:__LINE__ description:@"Could not set public key."];
if (bytes[i] == 0x05) /* This should handle the spurious ASN.1 NULL field */
i += 2;
if (bytes[i++] != 0x03)
在Android或Java中,生成的密鑰采用標准的ASN.1格式,在外部世界(客戶端,服務器端)可以正常工作,但在iOS中,生成的密鑰(公共,私有)采用原始格式,您必須將其轉換為適當的ASN.1格式使它們可行。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.