/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.j2objc.security;
import java.io.IOException;
import java.math.BigInteger;
import java.security.Key;
import java.security.interfaces.RSAKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import sun.security.util.DerInputStream;
public abstract class IosRSAKey implements RSAKey, Key {
protected transient long iosSecKey;
protected BigInteger modulus;
static final String PUBLIC_KEY_TAG = "com.google.j2objc.security.publickey";
static final String PRIVATE_KEY_TAG = "com.google.j2objc.security.privatekey";
private static final long serialVersionUID = 1L;
public IosRSAKey(BigInteger modulus) {
this.modulus = modulus;
}
public IosRSAKey(long iosSecKey) {
this.iosSecKey = iosSecKey;
}
@Override
public String getAlgorithm() {
return "RSA";
}
@Override
public String getFormat() {
return "X.509";
}
@Override
public BigInteger getModulus() {
if (modulus == null) {
decodeParameters();
}
return modulus;
}
long getSecKeyRef() {
return iosSecKey;
}
private static native long decode(byte[] encoded) /*-[
CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
(const UInt8 *)encoded->buffer_, encoded->size_, kCFAllocatorNull);
jlong secKey = (jlong) SecCertificateCreateWithData(NULL, data);
CFRelease(data);
return secKey;
]-*/;
protected abstract void decodeParameters();
public static class IosRSAPublicKey extends IosRSAKey implements RSAPublicKey {
private BigInteger publicExponent;
private static final long serialVersionUID = 1L;
public IosRSAPublicKey(long iosSecKey) {
super(iosSecKey);
}
public IosRSAPublicKey(RSAPublicKeySpec spec) {
super(spec.getModulus());
this.publicExponent = spec.getPublicExponent();
}
public IosRSAPublicKey(byte[] encoded) {
this(decode(encoded));
}
@Override
public native byte[] getEncoded() /*-[
NSData *publicKey = nil;
NSData *publicTag = [ComGoogleJ2objcSecurityIosRSAKey_PUBLIC_KEY_TAG
dataUsingEncoding:NSUTF8StringEncoding];
NSMutableDictionary *publicKeyQuery = [[NSMutableDictionary alloc] init];
[publicKeyQuery setObject:(id)kSecClassKey forKey:(id)kSecClass];
[publicKeyQuery setObject:publicTag forKey:(id)kSecAttrApplicationTag];
[publicKeyQuery setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
[publicKeyQuery setObject:[NSNumber numberWithBool:true] forKey:(id)kSecReturnData];
OSStatus result =
SecItemCopyMatching((CFDictionaryRef)publicKeyQuery, (CFTypeRef *)&publicKey);
[publicKeyQuery release];
IOSByteArray *bytes = nil;
if (result == noErr && publicKey.length > 0) {
bytes = [IOSByteArray arrayWithBytes:(jbyte *)publicKey.bytes count:publicKey.length];
[publicKey release];
}
return bytes;
]-*/;
@Override
public BigInteger getPublicExponent() {
if (publicExponent == null) {
decodeParameters();
}
return publicExponent;
}
@Override
protected void decodeParameters() {
byte[] bytes = getEncoded();
if (bytes == null) {
return;
}
try {
DerInputStream in = new DerInputStream(bytes);
in.getBitString(); // Ignore: bitstring of mod + exp.
in.getBitString();
modulus = new BigInteger(in.getBitString());
in.getBitString();
publicExponent = new BigInteger(in.getBitString());
} catch (IOException e) {
// Should never happen, since bytes are extracted from a valid iOS secKeyRef.
throw new AssertionError("failed decoding key parameters: " + e);
}
}
}
public static class IosRSAPrivateKey extends IosRSAKey implements RSAPrivateKey {
private BigInteger privateExponent;
private static final long serialVersionUID = 1L;
public IosRSAPrivateKey(long iosSecKey) {
super(iosSecKey);
}
public IosRSAPrivateKey(RSAPrivateKeySpec spec) {
super(spec.getModulus());
this.privateExponent = spec.getPrivateExponent();
}
public IosRSAPrivateKey(byte[] encoded) {
this(decode(encoded));
}
@Override
public native byte[] getEncoded() /*-[
NSData *privateKey = nil;
NSData *privateTag = [ComGoogleJ2objcSecurityIosRSAKey_PRIVATE_KEY_TAG
dataUsingEncoding:NSUTF8StringEncoding];
NSMutableDictionary *privateKeyQuery = [[NSMutableDictionary alloc] init];
[privateKeyQuery setObject:(id)kSecClassKey forKey:(id)kSecClass];
[privateKeyQuery setObject:privateTag forKey:(id)kSecAttrApplicationTag];
[privateKeyQuery setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
[privateKeyQuery setObject:[NSNumber numberWithBool:true] forKey:(id)kSecReturnData];
OSStatus result =
SecItemCopyMatching((CFDictionaryRef)privateKeyQuery, (CFTypeRef *)&privateKey);
[privateKeyQuery release];
IOSByteArray *bytes = nil;
if (result == noErr && privateKey.length > 0) {
bytes = [IOSByteArray arrayWithBytes:(jbyte *)privateKey.bytes count:privateKey.length];
[privateKey release];
}
return bytes;
]-*/;
@Override
public BigInteger getPrivateExponent() {
if (privateExponent == null) {
decodeParameters();
}
return privateExponent;
}
protected void decodeParameters() {
byte[] bytes = getEncoded();
if (bytes == null) {
return;
}
try {
DerInputStream in = new DerInputStream(bytes);
in.getBitString(); // Ignore: bitstring of mod + exp.
in.getBitString();
modulus = new BigInteger(in.getBitString());
in.getBitString();
privateExponent = new BigInteger(in.getBitString());
} catch (IOException e) {
// Should never happen, since bytes are extracted from a valid iOS secKeyRef.
throw new AssertionError("failed decoding key parameters: " + e);
}
}
}
}