0

我在 Java 中创建了一个 RSA 密钥对,并将密钥存储为 base64 编码的字符串。使用这些密钥生成密钥和加密/解密数据的代码如下所示。

我想使用下面显示的代码在服务器端加密一些 Java 数据,并在 Mac 客户端解密 Objective-C 中的加密数据。

在目标 C 中,我将拥有 base64 编码的公钥和包含加密数据的 NSData。

我不知道如何将 base64 编码的公钥转换为 SecKeyRef 然后解密数据?

Java 代码:

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Map;
import java.util.UUID;

import javax.crypto.Cipher;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;

public class Test {

static String PRIVATE_KEY;
static String PUBLIC_KEY;

public static void main(String args[]) throws Exception
{
    Test test = new Test();
    test.doSomething();

    String teststr = "wow this is a test string!";
    String resultstr = test.decrypt(test.encrypt(teststr));
    if (teststr.equals(resultstr)) {
        System.out.println("Equal");
        System.out.println(resultstr);
    }
}


public void doSomething() throws Exception{     
    KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
    kpg.initialize(1024);
    KeyPair keypair = kpg.genKeyPair();

    Key publicKey = keypair.getPublic();
    System.out.println("publicKey.getFormat():"+publicKey.getFormat());
    byte[] publicKeyBytes = publicKey.getEncoded();
    String base64PublicKeyString = Base64.encodeBase64String(publicKeyBytes);
    PUBLIC_KEY = base64PublicKeyString;
    System.out.println("Public Key");
    System.out.println(base64PublicKeyString);
    System.out.println("--------------------");

    Key privateKey = keypair.getPrivate();
    System.out.println("privateKey.getFormat():"+privateKey.getFormat());
    byte[] privateKeyBytes = privateKey.getEncoded();
    String base64PrivateKeyString = Base64.encodeBase64String(privateKeyBytes);
    PRIVATE_KEY = base64PrivateKeyString;
    System.out.println("Private Key");
    System.out.println(base64PrivateKeyString);
    System.out.println("--------------------");

}


private static final int DATA_BLOCK_SIZE = 117;

private static final int ENCRYPTED_BLOCK_SIZE = 128;

public static byte[] encrypt(String plaintext) throws Exception{
    Cipher cipher = Cipher.getInstance("RSA");
    byte[] privateKeyBytesFromString = Base64.decodeBase64(PRIVATE_KEY);
    KeyFactory kf = KeyFactory.getInstance("RSA");
    PKCS8EncodedKeySpec ks = new PKCS8EncodedKeySpec(privateKeyBytesFromString);
    PrivateKey pk = kf.generatePrivate(ks);

    cipher.init(Cipher.ENCRYPT_MODE, pk);
    byte[] bytes = plaintext.getBytes("UTF-8");

    ByteArrayOutputStream baos = new ByteArrayOutputStream();

    for(int i = 0; i < (bytes.length/DATA_BLOCK_SIZE + 1); i++)
    {
        int start = i * DATA_BLOCK_SIZE;
        int blockLength;
        if(i == bytes.length/DATA_BLOCK_SIZE)
        {
            blockLength = bytes.length - i * DATA_BLOCK_SIZE;
        } else {
            blockLength = DATA_BLOCK_SIZE;
        }

        if(blockLength > 0)
        {
            byte[] encrypted = cipher.doFinal(bytes, start, blockLength);
            baos.write(encrypted);
        }
    }

    return baos.toByteArray();
}

public String decrypt(byte[] encrypted) throws Exception{
    Cipher cipher = Cipher.getInstance("RSA");

    byte[] publicKeyBytesFromString = Base64.decodeBase64(PUBLIC_KEY);
    //System.out.println(base64KeyString);
    KeyFactory kf = KeyFactory.getInstance("RSA");

    X509EncodedKeySpec ks = new X509EncodedKeySpec(publicKeyBytesFromString);
    PublicKey pk = kf.generatePublic(ks);


    cipher.init(Cipher.DECRYPT_MODE, pk);
    ByteArrayOutputStream baos = new ByteArrayOutputStream();

    for(int i = 0; i < (encrypted.length/ENCRYPTED_BLOCK_SIZE); i++)
    {
        byte[] decrypted = cipher.doFinal(encrypted, i * ENCRYPTED_BLOCK_SIZE, ENCRYPTED_BLOCK_SIZE);
        baos.write(decrypted);
    }

    return new String(baos.toByteArray() ,"UTF-8");
}

}

我已经尝试过CryptoExercise中提供的代码,但出现以下错误:

2013-07-05 16:01:32.735 TestEncryption[1940:303] -[NSConcreteData _fastCharacterContents]: unrecognized selector sent to instance 0x100643dc0

目标C代码:

#import "AppDelegate.h"
#import "NSData+Base64.h"
#include <CommonCrypto/CommonCryptor.h>


@implementation AppDelegate

#if DEBUG
#define LOGGING_FACILITY(X, Y)  \
NSAssert(X, Y);

#define LOGGING_FACILITY1(X, Y, Z)  \
NSAssert1(X, Y, Z);
#else
#define LOGGING_FACILITY(X, Y)  \
if (!(X)) {         \
NSLog(Y);       \
}

#define LOGGING_FACILITY1(X, Y, Z)  \
if (!(X)) {             \
NSLog(Y, Z);        \
}
#endif

- (void)dealloc
{
    [super dealloc];
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    // Insert code here to initialize your application
    [self decrypt];
}


- (void) decrypt
{
    NSString * keyStr = @"[Public key str generated using Java code above]";

    NSData *keyData = [NSData dataFromBase64String:keyStr];

    SecKeyRef keyref = [self addPeerPublicKey:@"My Key Name" keyBits:keyData];

    //NSLog(@"cc");
}

- (SecKeyRef)addPeerPublicKey:(NSString *)peerName keyBits:(NSData *)publicKey {
    OSStatus sanityCheck = noErr;
    SecKeyRef peerKeyRef = NULL;
    CFTypeRef persistPeer = NULL;

    LOGGING_FACILITY( peerName != nil, @"Peer name parameter is nil." );
    LOGGING_FACILITY( publicKey != nil, @"Public key parameter is nil." );

    NSData * peerTag = [[NSData alloc] initWithBytes:(const void *)[peerName UTF8String] length:[peerName length]];
    NSMutableDictionary * peerPublicKeyAttr = [[NSMutableDictionary alloc] init];

    [peerPublicKeyAttr setObject:(id)kSecClassKey forKey:(id)kSecClass];
    [peerPublicKeyAttr setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
    [peerPublicKeyAttr setObject:peerTag forKey:(id)kSecAttrApplicationTag];
    [peerPublicKeyAttr setObject:publicKey forKey:(id)kSecValueData];
    [peerPublicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnPersistentRef];

    sanityCheck = SecItemAdd((CFDictionaryRef) peerPublicKeyAttr, (CFTypeRef *)&persistPeer);

    // The nice thing about persistent references is that you can write their value out to disk and
    // then use them later. I don't do that here but it certainly can make sense for other situations
    // where you don't want to have to keep building up dictionaries of attributes to get a reference.
    //
    // Also take a look at SecKeyWrapper's methods (CFTypeRef)getPersistentKeyRefWithKeyRef:(SecKeyRef)key
    // & (SecKeyRef)getKeyRefWithPersistentKeyRef:(CFTypeRef)persistentRef.

    LOGGING_FACILITY1( sanityCheck == noErr || sanityCheck == errSecDuplicateItem, @"Problem adding the peer public key to the keychain, OSStatus == %d.", sanityCheck );

    if (persistPeer) {
        peerKeyRef = [self getKeyRefWithPersistentKeyRef:persistPeer];
    } else {
        [peerPublicKeyAttr removeObjectForKey:(id)kSecValueData];
        [peerPublicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnRef];
        // Let's retry a different way.
        sanityCheck = SecItemCopyMatching((CFDictionaryRef) peerPublicKeyAttr, (CFTypeRef *)&peerKeyRef);
    }

    LOGGING_FACILITY1( sanityCheck == noErr && peerKeyRef != NULL, @"Problem acquiring reference to the public key, OSStatus == %d.", sanityCheck );

    [peerTag release];
    [peerPublicKeyAttr release];
    if (persistPeer) CFRelease(persistPeer);
    return peerKeyRef;
}

- (SecKeyRef)getKeyRefWithPersistentKeyRef:(CFTypeRef)persistentRef {
    OSStatus sanityCheck = noErr;
    SecKeyRef keyRef = NULL;

    LOGGING_FACILITY(persistentRef != NULL, @"persistentRef object cannot be NULL." );

    NSMutableDictionary * queryKey = [[NSMutableDictionary alloc] init];

    // Set the SecKeyRef query dictionary.
    [queryKey setObject:(id)persistentRef forKey:(id)kSecValuePersistentRef];
    [queryKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnRef];

    // Get the persistent key reference.
    sanityCheck = SecItemCopyMatching((CFDictionaryRef)queryKey, (CFTypeRef *)&keyRef);
    [queryKey release];

    return keyRef;
}

@end

使用http://www.cocoawithlove.com/2009/06/base64-encoding-options-on-mac-and.html处理 Base64

//
//  NSData+Base64.m
//  base64
//
//  Created by Matt Gallagher on 2009/06/03.
//  Copyright 2009 Matt Gallagher. All rights reserved.
//
//  This software is provided 'as-is', without any express or implied
//  warranty. In no event will the authors be held liable for any damages
//  arising from the use of this software. Permission is granted to anyone to
//  use this software for any purpose, including commercial applications, and to
//  alter it and redistribute it freely, subject to the following restrictions:
//
//  1. The origin of this software must not be misrepresented; you must not
//     claim that you wrote the original software. If you use this software
//     in a product, an acknowledgment in the product documentation would be
//     appreciated but is not required.
//  2. Altered source versions must be plainly marked as such, and must not be
//     misrepresented as being the original software.
//  3. This notice may not be removed or altered from any source
//     distribution.
//

#import "NSData+Base64.h"

//
// Mapping from 6 bit pattern to ASCII character.
//
static unsigned char base64EncodeLookup[65] =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

//
// Definition for "masked-out" areas of the base64DecodeLookup mapping
//
#define xx 65

//
// Mapping from ASCII character to 6 bit pattern.
//
static unsigned char base64DecodeLookup[256] =
{
    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 
    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 
    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 62, xx, xx, xx, 63, 
    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, xx, xx, xx, xx, xx, xx, 
    xx,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 
    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, xx, xx, xx, xx, xx, 
    xx, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 
    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, xx, xx, xx, xx, xx, 
    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 
    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 
    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 
    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 
    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 
    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 
    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 
    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 
};

//
// Fundamental sizes of the binary and base64 encode/decode units in bytes
//
#define BINARY_UNIT_SIZE 3
#define BASE64_UNIT_SIZE 4

//
// NewBase64Decode
//
// Decodes the base64 ASCII string in the inputBuffer to a newly malloced
// output buffer.
//
//  inputBuffer - the source ASCII string for the decode
//  length - the length of the string or -1 (to specify strlen should be used)
//  outputLength - if not-NULL, on output will contain the decoded length
//
// returns the decoded buffer. Must be free'd by caller. Length is given by
//  outputLength.
//
void *NewBase64Decode(
    const char *inputBuffer,
    size_t length,
    size_t *outputLength)
{
    if (length == -1)
    {
        length = strlen(inputBuffer);
    }

    size_t outputBufferSize =
        ((length+BASE64_UNIT_SIZE-1) / BASE64_UNIT_SIZE) * BINARY_UNIT_SIZE;
    unsigned char *outputBuffer = (unsigned char *)malloc(outputBufferSize);

    size_t i = 0;
    size_t j = 0;
    while (i < length)
    {
        //
        // Accumulate 4 valid characters (ignore everything else)
        //
        unsigned char accumulated[BASE64_UNIT_SIZE];
        size_t accumulateIndex = 0;
        while (i < length)
        {
            unsigned char decode = base64DecodeLookup[inputBuffer[i++]];
            if (decode != xx)
            {
                accumulated[accumulateIndex] = decode;
                accumulateIndex++;

                if (accumulateIndex == BASE64_UNIT_SIZE)
                {
                    break;
                }
            }
        }

        //
        // Store the 6 bits from each of the 4 characters as 3 bytes
        //
        // (Uses improved bounds checking suggested by Alexandre Colucci)
        //
        if(accumulateIndex >= 2)  
            outputBuffer[j] = (accumulated[0] << 2) | (accumulated[1] >> 4);  
        if(accumulateIndex >= 3)  
            outputBuffer[j + 1] = (accumulated[1] << 4) | (accumulated[2] >> 2);  
        if(accumulateIndex >= 4)  
            outputBuffer[j + 2] = (accumulated[2] << 6) | accumulated[3];
        j += accumulateIndex - 1;
    }

    if (outputLength)
    {
        *outputLength = j;
    }
    return outputBuffer;
}

//
// NewBase64Encode
//
// Encodes the arbitrary data in the inputBuffer as base64 into a newly malloced
// output buffer.
//
//  inputBuffer - the source data for the encode
//  length - the length of the input in bytes
//  separateLines - if zero, no CR/LF characters will be added. Otherwise
//      a CR/LF pair will be added every 64 encoded chars.
//  outputLength - if not-NULL, on output will contain the encoded length
//      (not including terminating 0 char)
//
// returns the encoded buffer. Must be free'd by caller. Length is given by
//  outputLength.
//
char *NewBase64Encode(
    const void *buffer,
    size_t length,
    bool separateLines,
    size_t *outputLength)
{
    const unsigned char *inputBuffer = (const unsigned char *)buffer;

    #define MAX_NUM_PADDING_CHARS 2
    #define OUTPUT_LINE_LENGTH 64
    #define INPUT_LINE_LENGTH ((OUTPUT_LINE_LENGTH / BASE64_UNIT_SIZE) * BINARY_UNIT_SIZE)
    #define CR_LF_SIZE 2

    //
    // Byte accurate calculation of final buffer size
    //
    size_t outputBufferSize =
            ((length / BINARY_UNIT_SIZE)
                + ((length % BINARY_UNIT_SIZE) ? 1 : 0))
                    * BASE64_UNIT_SIZE;
    if (separateLines)
    {
        outputBufferSize +=
            (outputBufferSize / OUTPUT_LINE_LENGTH) * CR_LF_SIZE;
    }

    //
    // Include space for a terminating zero
    //
    outputBufferSize += 1;

    //
    // Allocate the output buffer
    //
    char *outputBuffer = (char *)malloc(outputBufferSize);
    if (!outputBuffer)
    {
        return NULL;
    }

    size_t i = 0;
    size_t j = 0;
    const size_t lineLength = separateLines ? INPUT_LINE_LENGTH : length;
    size_t lineEnd = lineLength;

    while (true)
    {
        if (lineEnd > length)
        {
            lineEnd = length;
        }

        for (; i + BINARY_UNIT_SIZE - 1 < lineEnd; i += BINARY_UNIT_SIZE)
        {
            //
            // Inner loop: turn 48 bytes into 64 base64 characters
            //
            outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2];
            outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i] & 0x03) << 4)
                | ((inputBuffer[i + 1] & 0xF0) >> 4)];
            outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i + 1] & 0x0F) << 2)
                | ((inputBuffer[i + 2] & 0xC0) >> 6)];
            outputBuffer[j++] = base64EncodeLookup[inputBuffer[i + 2] & 0x3F];
        }

        if (lineEnd == length)
        {
            break;
        }

        //
        // Add the newline
        //
        outputBuffer[j++] = '\r';
        outputBuffer[j++] = '\n';
        lineEnd += lineLength;
    }

    if (i + 1 < length)
    {
        //
        // Handle the single '=' case
        //
        outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2];
        outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i] & 0x03) << 4)
            | ((inputBuffer[i + 1] & 0xF0) >> 4)];
        outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i + 1] & 0x0F) << 2];
        outputBuffer[j++] = '=';
    }
    else if (i < length)
    {
        //
        // Handle the double '=' case
        //
        outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2];
        outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0x03) << 4];
        outputBuffer[j++] = '=';
        outputBuffer[j++] = '=';
    }
    outputBuffer[j] = 0;

    //
    // Set the output length and return the buffer
    //
    if (outputLength)
    {
        *outputLength = j;
    }
    return outputBuffer;
}

@implementation NSData (Base64)

//
// dataFromBase64String:
//
// Creates an NSData object containing the base64 decoded representation of
// the base64 string 'aString'
//
// Parameters:
//    aString - the base64 string to decode
//
// returns the autoreleased NSData representation of the base64 string
//
+ (NSData *)dataFromBase64String:(NSString *)aString
{
    NSData *data = [aString dataUsingEncoding:NSASCIIStringEncoding];
    size_t outputLength;
    void *outputBuffer = NewBase64Decode([data bytes], [data length], &outputLength);
    NSData *result = [NSData dataWithBytes:outputBuffer length:outputLength];
    free(outputBuffer);
    return result;
}

//
// base64EncodedString
//
// Creates an NSString object that contains the base 64 encoding of the
// receiver's data. Lines are broken at 64 characters long.
//
// returns an autoreleased NSString being the base 64 representation of the
//  receiver.
//
- (NSString *)base64EncodedString
{
    size_t outputLength;
    char *outputBuffer =
        NewBase64Encode([self bytes], [self length], true, &outputLength);

    NSString *result =
        [[[NSString alloc]
            initWithBytes:outputBuffer
            length:outputLength
            encoding:NSASCIIStringEncoding]
        autorelease];
    free(outputBuffer);
    return result;
}

// added by Hiroshi Hashiguchi
- (NSString *)base64EncodedStringWithSeparateLines:(BOOL)separateLines
{
    size_t outputLength;
    char *outputBuffer =
    NewBase64Encode([self bytes], [self length], separateLines, &outputLength);

    NSString *result =
    [[[NSString alloc]
      initWithBytes:outputBuffer
      length:outputLength
      encoding:NSASCIIStringEncoding]
     autorelease];
    free(outputBuffer);
    return result;
}


@end
4

1 回答 1

0

在处理不同的编程语言/平台时,我发现 base64 编码/解码的细微变化存在问题。

我建议首先验证公钥的解码值是否正确。

附言。分享您正在尝试的objective-c代码,您更有可能以这种方式获得更具体的答案(尽管请检查上述内容)。

于 2013-07-04T13:39:34.530 回答