来自 android、java 上 PEM 文件的公钥
PublicKey from PEM file on android, java
基本上我正在尝试验证使用 openssl 的签名,如下所示:
openssl dgst -sha256 -verify prime192v1-pub-v1.pem -signature signatureFile.bin < dataFile.bin
... 为了在 android 上执行此操作,我需要创建 PublicKey
对象。我使用的方法在 kf.generatePublic(new X509EncodedKeySpec(encoded))
.
行抛出 java.security.spec.InvalidKeySpecException: Unexpected key type
import org.spongycastle.util.encoders.Base64;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
public class SO {
public static PublicKey getPublicKeyFromString(String key) throws IOException, GeneralSecurityException {
String publicKeyPEM = key;
publicKeyPEM = publicKeyPEM.replace("-----BEGIN PUBLIC KEY-----\n", "");
publicKeyPEM = publicKeyPEM.replace("-----END PUBLIC KEY-----", "");
byte[] encoded = Base64.decode(publicKeyPEM);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePublic(new X509EncodedKeySpec(encoded));
}
}
我是这样调用方法的:
SO.getPublicKeyFromString(
"-----BEGIN PUBLIC KEY-----\n" +
"MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEXMHnQfWiM4oCaLfx296llgz7iaVv\n" +
"avMPppkzVNZAxtlNLhFlXnNWD0Mw9yzP8/Go\n" +
"-----END PUBLIC KEY-----"
);
有人知道我做错了什么吗?
我成功了。 public 键是椭圆曲线 (p192) public 键,应该以不同方式加载。使用 PublicKey
我能够像使用 openssl 命令一样验证签名。
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.io.Reader;
import java.io.StringReader;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
public class SO {
public PublicKey getPublicKey() throws Exception {
Security.addProvider(new BouncyCastleProvider());
Reader rdr = new StringReader(
"-----BEGIN PUBLIC KEY-----\n" +
"MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEXMHnQfWiM4oCaLfx296llgz7iaVv\n" +
"avMPppkzVNZAxtlNLhFlXnNWD0Mw9yzP8/Go\n" +
"-----END PUBLIC KEY-----\n"
); // or from file etc.
org.bouncycastle.util.io.pem.PemObject spki = new org.bouncycastle.util.io.pem.PemReader(rdr).readPemObject();
PublicKey key = KeyFactory.getInstance("EC", "BC").generatePublic(new X509EncodedKeySpec(spki.getContent()));
return key;
}
public static boolean verify(byte[] data, byte[] signatureBytes, PublicKey publicKey) throws Exception {
Signature signature = Signature.getInstance("SHA256withECDSA", "BC");
signature.initVerify(publicKey);
signature.update(data);
return signature.verify(signatureBytes);
}
}
我为此苦苦思索了一段时间,想出了这个解决方案。我决定不让安全系统过载,因为我在应用程序的其他区域使用了 android 加密系统的其他元素。
public class EncryptionInterface {
private static final int bufferSize_ = 32;
private static final String headerTag = "-----BEGIN PUBLIC KEY-----";
private static final String footerTag = "-----END PUBLIC KEY-----";
/**
*
* @param ctx
* @return
* @throws Exception
*/
public static PublicKey getPublicKey(Context ctx) throws Exception {
PublicKey generatedPublic;
Security.addProvider(new BouncyCastleProvider());
String publicKeyString = getKeyFromFile(ctx);
byte[] decoded = Base64.decode(publicKeyString, Base64.DEFAULT);
org.spongycastle.asn1.pkcs.RSAPublicKey pkcs1PublicKey = org.spongycastle.asn1.pkcs.RSAPublicKey.getInstance(decoded);
BigInteger modulus = pkcs1PublicKey.getModulus();
BigInteger publicExponent = pkcs1PublicKey.getPublicExponent();
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, publicExponent);
KeyFactory kf = KeyFactory.getInstance("RSA");
generatedPublic = kf.generatePublic(keySpec);
return generatedPublic;
}
/**
* Load the key/PEM from a file and strip the header/footer.
* @param ctx
* @throws Exception
*/
public static String getKeyFromFile(Context ctx) throws Exception
{
String certPath = "publickey.pem";
InputStream input = ctx.getAssets().open(certPath);
byte[] buffer = new byte[bufferSize_];
int len = 0;
ByteArrayOutputStream keyBuffer = new ByteArrayOutputStream();
while ((len = input.read(buffer)) != -1) {
keyBuffer.write(buffer, 0, len);
}
String str = new String(keyBuffer.toByteArray());
str = str.replace(headerTag, "");
str = str.replace(footerTag, "");
return str;
}
}
基本上我正在尝试验证使用 openssl 的签名,如下所示:
openssl dgst -sha256 -verify prime192v1-pub-v1.pem -signature signatureFile.bin < dataFile.bin
... 为了在 android 上执行此操作,我需要创建 PublicKey
对象。我使用的方法在 kf.generatePublic(new X509EncodedKeySpec(encoded))
.
java.security.spec.InvalidKeySpecException: Unexpected key type
import org.spongycastle.util.encoders.Base64;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
public class SO {
public static PublicKey getPublicKeyFromString(String key) throws IOException, GeneralSecurityException {
String publicKeyPEM = key;
publicKeyPEM = publicKeyPEM.replace("-----BEGIN PUBLIC KEY-----\n", "");
publicKeyPEM = publicKeyPEM.replace("-----END PUBLIC KEY-----", "");
byte[] encoded = Base64.decode(publicKeyPEM);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePublic(new X509EncodedKeySpec(encoded));
}
}
我是这样调用方法的:
SO.getPublicKeyFromString(
"-----BEGIN PUBLIC KEY-----\n" +
"MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEXMHnQfWiM4oCaLfx296llgz7iaVv\n" +
"avMPppkzVNZAxtlNLhFlXnNWD0Mw9yzP8/Go\n" +
"-----END PUBLIC KEY-----"
);
有人知道我做错了什么吗?
我成功了。 public 键是椭圆曲线 (p192) public 键,应该以不同方式加载。使用 PublicKey
我能够像使用 openssl 命令一样验证签名。
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.io.Reader;
import java.io.StringReader;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
public class SO {
public PublicKey getPublicKey() throws Exception {
Security.addProvider(new BouncyCastleProvider());
Reader rdr = new StringReader(
"-----BEGIN PUBLIC KEY-----\n" +
"MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEXMHnQfWiM4oCaLfx296llgz7iaVv\n" +
"avMPppkzVNZAxtlNLhFlXnNWD0Mw9yzP8/Go\n" +
"-----END PUBLIC KEY-----\n"
); // or from file etc.
org.bouncycastle.util.io.pem.PemObject spki = new org.bouncycastle.util.io.pem.PemReader(rdr).readPemObject();
PublicKey key = KeyFactory.getInstance("EC", "BC").generatePublic(new X509EncodedKeySpec(spki.getContent()));
return key;
}
public static boolean verify(byte[] data, byte[] signatureBytes, PublicKey publicKey) throws Exception {
Signature signature = Signature.getInstance("SHA256withECDSA", "BC");
signature.initVerify(publicKey);
signature.update(data);
return signature.verify(signatureBytes);
}
}
我为此苦苦思索了一段时间,想出了这个解决方案。我决定不让安全系统过载,因为我在应用程序的其他区域使用了 android 加密系统的其他元素。
public class EncryptionInterface {
private static final int bufferSize_ = 32;
private static final String headerTag = "-----BEGIN PUBLIC KEY-----";
private static final String footerTag = "-----END PUBLIC KEY-----";
/**
*
* @param ctx
* @return
* @throws Exception
*/
public static PublicKey getPublicKey(Context ctx) throws Exception {
PublicKey generatedPublic;
Security.addProvider(new BouncyCastleProvider());
String publicKeyString = getKeyFromFile(ctx);
byte[] decoded = Base64.decode(publicKeyString, Base64.DEFAULT);
org.spongycastle.asn1.pkcs.RSAPublicKey pkcs1PublicKey = org.spongycastle.asn1.pkcs.RSAPublicKey.getInstance(decoded);
BigInteger modulus = pkcs1PublicKey.getModulus();
BigInteger publicExponent = pkcs1PublicKey.getPublicExponent();
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, publicExponent);
KeyFactory kf = KeyFactory.getInstance("RSA");
generatedPublic = kf.generatePublic(keySpec);
return generatedPublic;
}
/**
* Load the key/PEM from a file and strip the header/footer.
* @param ctx
* @throws Exception
*/
public static String getKeyFromFile(Context ctx) throws Exception
{
String certPath = "publickey.pem";
InputStream input = ctx.getAssets().open(certPath);
byte[] buffer = new byte[bufferSize_];
int len = 0;
ByteArrayOutputStream keyBuffer = new ByteArrayOutputStream();
while ((len = input.read(buffer)) != -1) {
keyBuffer.write(buffer, 0, len);
}
String str = new String(keyBuffer.toByteArray());
str = str.replace(headerTag, "");
str = str.replace(footerTag, "");
return str;
}
}