Android中的keystore可以帮我们保管秘钥,调用其方法可以进行加解密 签名等操作,其支持直接生成keystore,也支持外部导入,下面介绍如何生成一个可导入的外部keystore文件:
/**
* @param password 密码
* @param issuerStr 颁发机构信息
* @param subjectStr 使用者信息
* @param certificateCRL 颁发地址
* @return
*/
public Map<String, byte[]> createCert(Context context, String alias, String password,
String issuerStr, String subjectStr, String certificateCRL) {
Log.d(TAG,"createCert : "+alias);
Map<String, byte[]> result = new HashMap<String, byte[]>();
ByteArrayOutputStream out = null;
try {
// 生成JKS证书
// KeyStore keyStore = KeyStore.getInstance("JKS");
// 标志生成PKCS12证书
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(null, null);
KeyPair keyPair = getKey();
// issuer与 subject相同的证书就是CA证书
Certificate cert = generateCertificateV3(issuerStr, subjectStr, keyPair, result, certificateCRL, null);
// cretkey随便写,标识别名
keyStore.setKeyEntry(alias, keyPair.getPrivate(), password.toCharArray(), new Certificate[] { cert });
out = new ByteArrayOutputStream();
cert.verify(keyPair.getPublic());
keyStore.store(out, password.toCharArray());
byte[] keyStoreData = out.toByteArray();
result.put("keyStoreData", keyStoreData);
Log.d(TAG,"keyStoreData= "+new String(Base64.encode(keyStoreData, Base64.DEFAULT)));
return result;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
}
}
}
return result;
}
private KeyPair getKey() throws NoSuchAlgorithmException {
// 密钥对 生成器,RSA算法 生成的 提供者是 BouncyCastle
KeyPairGenerator generator = null;
KeyPair keyPair = null;
generator = KeyPairGenerator.getInstance("RSA",new BouncyCastleProvider());
generator.initialize(2048); // 密钥长度 2048
// 证书中的密钥 公钥和私钥
keyPair = generator.generateKeyPair();
return keyPair;
}
/**
* @param issuerStr
* @param subjectStr
* @param keyPair
* @param result
* @param certificateCRL
* @param extensions
* @return
*/
public Certificate generateCertificateV3(String issuerStr, String subjectStr, KeyPair keyPair, Map<String, byte[]> result,
String certificateCRL, List<Extension> extensions) {
Log.d(TAG,"generateCertificateV3");
ByteArrayInputStream bout = null;
X509Certificate cert = null;
try {
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
Date notBefore = new Date();
Calendar rightNow = Calendar.getInstance();
rightNow.setTime(notBefore);
// 日期加10年
rightNow.add(Calendar.YEAR, 10);
Date notAfter = rightNow.getTime();
// 证书序列号
BigInteger serial = BigInteger.probablePrime(256, new Random());
X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(
new X500Name(issuerStr), serial, notBefore, notAfter,new X500Name(subjectStr), publicKey);
JcaContentSignerBuilder jBuilder = new JcaContentSignerBuilder( "SHA1withRSA");
SecureRandom secureRandom = new SecureRandom();
jBuilder.setSecureRandom(secureRandom);
ContentSigner singer = jBuilder.setProvider( new BouncyCastleProvider()).build(privateKey);
// 分发点
ASN1ObjectIdentifier cRLDistributionPoints = new ASN1ObjectIdentifier( "2.5.29.31");
GeneralName generalName = new GeneralName( GeneralName.uniformResourceIdentifier, certificateCRL);
GeneralNames seneralNames = new GeneralNames(generalName);
DistributionPointName distributionPoint = new DistributionPointName( seneralNames);
DistributionPoint[] points = new DistributionPoint[1];
points[0] = new DistributionPoint(distributionPoint, null, null);
CRLDistPoint cRLDistPoint = new CRLDistPoint(points);
builder.addExtension(cRLDistributionPoints, true, cRLDistPoint);
// 用途
ASN1ObjectIdentifier keyUsage = new ASN1ObjectIdentifier( "2.5.29.15");
// | KeyUsage.nonRepudiation | KeyUsage.keyCertSign
builder.addExtension(keyUsage, true, new KeyUsage( KeyUsage.digitalSignature | KeyUsage.keyEncipherment));
// 基本限制 X509Extension.java
ASN1ObjectIdentifier basicConstraints = new ASN1ObjectIdentifier("2.5.29.19");
builder.addExtension(basicConstraints, true, new BasicConstraints(true));
// privKey:使用自己的私钥进行签名,CA证书
if (extensions != null){
for (Extension ext : extensions) {
builder.addExtension(
new ASN1ObjectIdentifier(ext.getOid()),
ext.isCritical(),
ASN1Primitive.fromByteArray(ext.getValue()));
}
}
X509CertificateHolder holder = builder.build(singer);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
bout = new ByteArrayInputStream(holder.toASN1Structure() .getEncoded());
cert = (X509Certificate) cf.generateCertificate(bout);
byte[] certBuf = holder.getEncoded();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
// 证书数据
result.put("certificateData", certBuf);
Log.d(TAG,"certificateData= "+new String(Base64.encode(certBuf, Base64.DEFAULT)));
//公钥
result.put("publicKey", publicKey.getEncoded());
Log.d(TAG,"publicKey= "+new String(Base64.encode(publicKey.getEncoded(), Base64.DEFAULT)));
//私钥
result.put("privateKey", privateKey.getEncoded());
Log.d(TAG,"privateKey= "+new String(Base64.encode(privateKey.getEncoded(), Base64.DEFAULT)));
//证书有效开始时间
result.put("notBefore", format.format(notBefore).getBytes("utf-8"));
Log.d(TAG,"notBefore= "+new String(Base64.encode(format.format(notBefore).getBytes("utf-8"), Base64.DEFAULT)));
//证书有效结束时间
result.put("notAfter", format.format(notAfter).getBytes("utf-8"));
Log.d(TAG,"notAfter= "+new String(Base64.encode(format.format(notAfter).getBytes("utf-8"), Base64.DEFAULT)));
} catch (Exception e) {
e.printStackTrace();
} finally {
if (bout != null) {
try {
bout.close();
} catch (IOException e) {
}
}
}
return cert;
}
里面的Extension是自建的实体类,如下
public class Extension {
private String oid;
private boolean critical;
private byte[] value;
public String getOid() {
return oid;
}
public byte[] getValue() {
return value;
}
public boolean isCritical() {
return critical;
}
}
需要导入org.bouncycastle包,Android studio在manifest中加入
implementation group: 'org.bouncycastle', name: 'bcpkix-jdk15on', version: '1.62'
如不可使用可直接下载资源文件中的jar包放入lib中,gradle版本不能小于3.3.2,否则编译不通过
使用方法
File file = new File("/sdcard/test.p12");
String issuerStr = "CN=China,OU=Software,O=Test,L=Beijing,ST=Beijing,C=CN";
String subjectStr = "CN=China,OU=Software,O=Test,L=Beijing,ST=Beijing,C=CN";
String certificateCRL = "https://test.cn";
Map<String, byte[]> result = createCert(context,"Test","123456", issuerStr, subjectStr, certificateCRL);
if(!result.isEmpty()){
FileOutputStream outPutStream = null; // ca.jks
try {
outPutStream = new FileOutputStream(file);
outPutStream.write(result.get("keyStoreData"));
outPutStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
生成keystore文件了,那么如何导入到系统中呢,接着往下看
public void loadPKCS12(File keyStoreFile,String password){
Log.d(TAG,"loadPKCS12");
if (!keyStoreFile.exists()) {
Log.e(TAG,keyStoreFile.getAbsolutePath()+" does not exist");
return ;
}
KeyStore fileKeyStore = null;
try (InputStream in = new FileInputStream(keyStoreFile)) {
fileKeyStore = KeyStore.getInstance("PKCS12");
fileKeyStore.load(in, password.toCharArray());
Enumeration<String> aliases = fileKeyStore.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
Log.d(TAG,"Load Keystore : "+alias);
//加载进keystore
KeyStore.Entry entry = fileKeyStore.getEntry(alias, null);
keyStore.setEntry(alias, entry, null);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (UnrecoverableEntryException e) {
e.printStackTrace();
}
}
只需传入之前生成的p12文件,和密码(123456),就可以将此秘钥文件加载如系统