我编写了一个 API,以便我的不同应用程序可以使用它。它只是检查应用程序是否已在服务器上注册。如果没有,则它会生成公钥/私钥对,并通过将 CSR 发送到我的应用程序来获取签名证书服务器。下次它使用签名的证书和私钥。最初我有原始的默认 keystore 文件(b/c所有通信都是通过 SSL 进行的,即最初使用默认 keystore ,然后使用用户生成的 keystore 注册后)
我的 API 工作正常。我遇到的问题在某种程度上与我缺乏知识或错误的方法有关,因此我需要帮助。
我正在使用以下类来保存/检索我的 key 对
public class KeyIOHandler {
public static void writePublicKeyToPreferences(KeyPair key, Context context) {
StringWriter publicStringWriter = new StringWriter();
try {
PemWriter pemWriter = new PemWriter(publicStringWriter);
pemWriter.writeObject(new PemObject("myapp.PUBLIC KEY", key.getPublic().getEncoded()));
pemWriter.flush();
pemWriter.close();
SharedPreferences preferences = context.getSharedPreferences("SHARED_PREFERENCES",0);
preferences.edit().putString("RSA_PUBLIC_KEY", publicStringWriter.toString()).commit();
Log.e("Public Key", publicStringWriter.toString());
} catch (IOException e) {
Log.e("RSA", e.getMessage());
e.printStackTrace();
}
}
public static void writePrivateKeyToPreferences(KeyPair keyPair, Context context) {
StringWriter privateStringWriter = new StringWriter();
try {
PemWriter pemWriter = new PemWriter(privateStringWriter);
pemWriter.writeObject(new PemObject("myapp.PRIVATE KEY", keyPair.getPrivate().getEncoded()));
pemWriter.flush();
pemWriter.close();
SharedPreferences preferences = context.getSharedPreferences("SHARED_PREFERENCES",0);
preferences.edit().putString("RSA_PRIVATE_KEY", privateStringWriter.toString()).commit();
Log.e("Private Key",privateStringWriter.toString());
} catch (IOException e) {
Log.e("RSA", e.getMessage());
e.printStackTrace();
}
}
public static PublicKey getRSAPublicKeyFromString(String publicKeyPEM) throws Exception {
publicKeyPEM = stripPublicKeyHeaders(publicKeyPEM);
KeyFactory keyFactory = KeyFactory.getInstance("RSA", "SC");
byte[] publicKeyBytes = Base64.decode(publicKeyPEM.getBytes("UTF-8"));
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(publicKeyBytes);
return keyFactory.generatePublic(x509KeySpec);
}
public static PrivateKey getRSAPrivateKeyFromString(String privateKeyPEM) throws Exception {
privateKeyPEM = stripPrivateKeyHeaders(privateKeyPEM);
KeyFactory fact = KeyFactory.getInstance("RSA", "SC");
byte[] clear = Base64.decode(privateKeyPEM);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(clear);
PrivateKey priv = fact.generatePrivate(keySpec);
Arrays.fill(clear, (byte) 0);
return priv;
}
public static String stripPublicKeyHeaders(String key) {
//strip the headers from the key string
StringBuilder strippedKey = new StringBuilder();
String lines[] = key.split("\n");
for (String line : lines) {
if (!line.contains("BEGIN PUBLIC KEY") && !line.contains("END PUBLIC KEY") && !isNullOrEmpty(line.trim())) {
strippedKey.append(line.trim());
}
}
return strippedKey.toString().trim();
}
public static String stripPrivateKeyHeaders(String key) {
StringBuilder strippedKey = new StringBuilder();
String lines[] = key.split("\n");
for (String line : lines) {
if (!line.contains("BEGIN PRIVATE KEY") && !line.contains("END PRIVATE KEY") && !isNullOrEmpty(line.trim())) {
strippedKey.append(line.trim());
}
}
return strippedKey.toString().trim();
}
public static boolean isNullOrEmpty(String str) {
return str == null || str.isEmpty();
}
}
通过使用以下方法,我正在生成我的 keystore ,但我很困惑如何以及在哪里安全地存储此 keystore ,以便我可以在应用程序的余生中使用它。(除非 key 被盗)。正如在共享首选项中一样,我无法存储任何对象,因此在哪里保存此 keystore 。现在我的类中有静态 keystore 对象。(这是我知道的最糟糕的情况,但只是为了使其工作,我将其设为静态)
private boolean addToStore(){
try {
Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);
clientPkcs12 = KeyStore.getInstance("PKCS12");
clientPkcs12.load(null, null);
clientPkcs12.setKeyEntry("clientCert", keyPair.getPrivate(), "123456".toCharArray(), chain);
}
catch(Exception ex){
Log.e("",ex.getCause().getMessage());
}
return false;
}
我已经浏览了几篇文章,其中这是一个值得注意的nelenkov.blogspot,但没有得到如何实现我的目标或者我在保存 keystore 时错了?我的意思是我每次都必须创建 keystore 吗?
最佳答案
我不知道当您拥有 keystore
时,为什么要将您的凭据保留在共享首选项中。将 keystore 文件加载到内存后,您可以随时添加/检索 key 。当您完成后,只需通过调用将其保存在文件中:
FileOutputStream out = new FileOutputStream(keystoreFile);
keystore.store(out, password);
out.close();
从文件加载:
keystore.load(new FileInputStream(keystoreFile, password);
@answer 评论
您应该只使用 keystore
。来自共享首选项的数据不安全,并且很容易从中检索私钥。您没有使用任何类型的加密,因此您的 key 以纯 PEM 文本存储。但是,如果您使用Keystore
,您的 key 将受到密码保护(在 key 级别和 keystore 级别),因此如果任何人在没有密码的情况下获取Keystore
文件,那么他将更难获得打破它。保存Keystore
文件的最佳位置是您的应用程序内部空间磁盘,如果没有root手机(创建文件时Context.MODE_PRIVATE
),则无法访问该磁盘。
关于java - 如何安全地保存我的 keystore ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33346429/