Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions android/src/main/java/com/RNRSA/CsrHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package com.RNRSA;

import android.util.Log;

import org.spongycastle.asn1.ASN1ObjectIdentifier;
import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.spongycastle.asn1.x500.X500Name;
import org.spongycastle.asn1.x509.AlgorithmIdentifier;
import org.spongycastle.asn1.x509.ExtensionsGenerator;
import org.spongycastle.operator.ContentSigner;
import org.spongycastle.operator.OperatorCreationException;
import org.spongycastle.pkcs.PKCS10CertificationRequest;
import org.spongycastle.pkcs.PKCS10CertificationRequestBuilder;
import org.spongycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.util.HashMap;
import java.util.Map;

public class CsrHelper {

private final static String DEFAULT_SIGNATURE_ALGORITHM = "SHA256withECDSA";
private final static String CN_PATTERN = "CN=%s";

private static class JCESigner implements ContentSigner {

private static Map<String, AlgorithmIdentifier> ALGOS = new HashMap<String, AlgorithmIdentifier>();

static {
ALGOS.put("SHA256withECDSA".toLowerCase(), new AlgorithmIdentifier(
new ASN1ObjectIdentifier("1.2.840.10045.4.3.2")));
// ALGOS.put("SHA256withRSA".toLowerCase(), new AlgorithmIdentifier(
// new ASN1ObjectIdentifier("1.2.840.113549.1.1.11")));
// ALGOS.put("SHA1withRSA".toLowerCase(), new AlgorithmIdentifier(
// new ASN1ObjectIdentifier("1.2.840.113549.1.1.5")));

}

private String mAlgo;
private Signature signature;
private ByteArrayOutputStream outputStream;

public JCESigner( String sigAlgo, String keyTag) {
mAlgo = sigAlgo.toLowerCase();
try {
KeyStore.Entry entry = getEntry(keyTag);
this.outputStream = new ByteArrayOutputStream();
this.signature = Signature.getInstance(sigAlgo);
PrivateKey key = ((KeyStore.PrivateKeyEntry) entry).getPrivateKey();
this.signature.initSign(key);
} catch (GeneralSecurityException gse) {
Log.e("generateCSR", "generateCSR: " + gse.getMessage());
throw new IllegalArgumentException(gse.getMessage());
}
catch (IOException gse) {
Log.e("generateCSR", "IOException: " + gse.getMessage());
throw new IllegalArgumentException(gse.getMessage());
}
}

public KeyStore.Entry getEntry (String keyTag) throws GeneralSecurityException, IOException {


KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
return ks.getEntry(keyTag, null);
}

@Override
public AlgorithmIdentifier getAlgorithmIdentifier() {
AlgorithmIdentifier id = ALGOS.get(mAlgo);
if (id == null) {
throw new IllegalArgumentException("Does not support algo: " +
mAlgo);
}
return id;
}

@Override
public OutputStream getOutputStream() {
return outputStream;
}

@Override
public byte[] getSignature() {
try {
signature.update(outputStream.toByteArray());
return signature.sign();
} catch (GeneralSecurityException gse) {
gse.printStackTrace();
return null;
}
}
}

//Create the certificate signing request (CSR) from private and public keys
public static PKCS10CertificationRequest generateCSR(PublicKey publicKey, String cn, String keyTag) throws IOException,
OperatorCreationException {

String principal = String.format(CN_PATTERN, cn);
ContentSigner signer = new JCESigner (DEFAULT_SIGNATURE_ALGORITHM, keyTag);
PKCS10CertificationRequestBuilder csrBuilder = new JcaPKCS10CertificationRequestBuilder(
new X500Name(principal), publicKey);

ExtensionsGenerator extensionsGenerator = new ExtensionsGenerator();
// extensionsGenerator.addExtension(Extension.basicConstraints, true, new BasicConstraints(
// true));
csrBuilder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest,
extensionsGenerator.generate());
PKCS10CertificationRequest csr = csrBuilder.build(signer);
return csr;
}
}

23 changes: 23 additions & 0 deletions android/src/main/java/com/RNRSA/RNRSAKeychainModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,29 @@ public void run() {
});
}

@ReactMethod
public void generateCSRWithEC(final String cn,final String keyTag, final int keySize, final Promise promise) {
final ReactApplicationContext reactContext = this.reactContext;

AsyncTask.execute(new Runnable() {
@Override
public void run() {
WritableNativeMap keys = new WritableNativeMap();

try {
RSA rsa = new RSA();
rsa.generateCSR(cn,keyTag, keySize, reactContext);
keys.putString("csr", rsa.getCSR());
promise.resolve(keys);
} catch (NoSuchAlgorithmException e) {
promise.reject("Error", e.getMessage());
} catch (Exception e) {
promise.reject("Error", e.getMessage());
}
}
});
}

@ReactMethod
public void deletePrivateKey(final String keyTag, final Promise promise) {
AsyncTask.execute(new Runnable() {
Expand Down
65 changes: 63 additions & 2 deletions android/src/main/java/com/RNRSA/RSA.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
import android.util.Base64;
import android.content.Context;


import java.security.SecureRandom;
import java.security.spec.ECGenParameterSpec;
import java.util.Calendar;
import java.math.BigInteger;
import java.io.Reader;
Expand Down Expand Up @@ -37,43 +40,51 @@
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.BadPaddingException;
import javax.security.cert.X509Certificate;
import javax.security.auth.x500.X500Principal;

import java.io.IOException;



import org.spongycastle.asn1.ASN1InputStream;
import org.spongycastle.asn1.ASN1Encodable;
import org.spongycastle.asn1.ASN1ObjectIdentifier;
import org.spongycastle.asn1.ASN1Primitive;
import org.spongycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.spongycastle.asn1.pkcs.PrivateKeyInfo;
import org.spongycastle.asn1.pkcs.RSAPublicKey;
import org.spongycastle.asn1.pkcs.RSAPrivateKey;
import org.spongycastle.asn1.x509.SubjectPublicKeyInfo;
import org.spongycastle.asn1.x509.RSAPublicKeyStructure;
import org.spongycastle.operator.OperatorCreationException;
import org.spongycastle.pkcs.PKCS10CertificationRequest;
import org.spongycastle.util.io.pem.PemObject;
import org.spongycastle.util.io.pem.PemWriter;
import org.spongycastle.util.io.pem.PemReader;
import org.spongycastle.asn1.pkcs.RSAPublicKey;
import org.spongycastle.openssl.PEMParser;
import org.spongycastle.util.io.pem.PemObject;


import static android.security.keystore.KeyProperties.*;

import static java.nio.charset.StandardCharsets.UTF_8;
import java.nio.charset.Charset;


public class RSA {
public static Charset CharsetUTF_8;

public static final String ALGORITHM = KeyProperties.KEY_ALGORITHM_RSA;

private static final String PUBLIC_HEADER = "RSA PUBLIC KEY";
private static final String PRIVATE_HEADER = "RSA PRIVATE KEY";
private static final String CSR_HEADER = "CERTIFICATE REQUEST";

private String keyTag;

private PublicKey publicKey;
private PrivateKey privateKey;
private PKCS10CertificationRequest csr;

public RSA() {
this.setupCharset();
Expand Down Expand Up @@ -328,6 +339,56 @@ public void generate(String keyTag, int keySize, Context context) throws IOExcep

KeyPair keyPair = kpg.genKeyPair();
this.publicKey = keyPair.getPublic();

}

@TargetApi(18)
public void generateCSR(String cn,String keyTag, int keySize, Context context) throws IOException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchProviderException {

KeyPairGenerator kpg = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
if (android.os.Build.VERSION.SDK_INT >= 23) {

kpg.initialize(new KeyGenParameterSpec.Builder(
keyTag,
KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
.setDigests(KeyProperties.DIGEST_SHA256,
KeyProperties.DIGEST_SHA512,
KeyProperties.DIGEST_NONE)
.setKeySize(keySize)
.build());
} else {
Calendar endDate = Calendar.getInstance();
endDate.add(Calendar.YEAR, 1);
KeyPairGeneratorSpec.Builder keyPairGeneratorSpec = new KeyPairGeneratorSpec.Builder(context)
.setAlias(keyTag)
.setSubject(new X500Principal(
String.format("CN=%s", keyTag, context.getPackageName())
))
.setSerialNumber(BigInteger.ONE)
.setStartDate(Calendar.getInstance().getTime())
.setEndDate(endDate.getTime());
if (android.os.Build.VERSION.SDK_INT >= 19) {
keyPairGeneratorSpec.setKeySize(keySize).setKeyType(KeyProperties.KEY_ALGORITHM_EC);
}
kpg.initialize(keyPairGeneratorSpec.build());
}


KeyPair keyPair = kpg.genKeyPair();
this.publicKey = keyPair.getPublic();

try {
this.csr = CsrHelper.generateCSR(this.publicKey, cn, keyTag);
} catch (OperatorCreationException e) {
e.printStackTrace();
}
}


public String getCSR() throws IOException {
byte CSRder[] = this.csr.getEncoded();
return dataToPem(CSR_HEADER, CSRder);
}

}

1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ declare module 'react-native-rsa-native' {
namespace RSAKeychain {
export function generate(keyTag: string, keySize: number): Promise<PublicKey>;
export function generateKeys(keyTag: string, keySize: number): Promise<PublicKey>;
export function generateCSRWithEC(cn: String,keyTag: string, keySize: number): Promise<PublicKey>;
export function deletePrivateKey(keyTag: string): Promise<boolean>;
export function encrypt(data: string, keyTag: string): Promise<string>;
export function decrypt(data: string, keyTag: string): Promise<string>;
Expand Down