- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我需要在 Android 应用程序中即时创建 SSL 自签名证书,并能够在同一应用程序中从 https 服务器使用它。我发现这段代码可以创建一个证书,尽管我不确定它是否是正确的证书。我还没有找到太多关于如何将它添加到我的应用程序上的 BouncyCaSTLe keystore 以及如何在创建 HTTPs 服务器时使用它的信息。有人可以指出我这样做的例子吗?谢谢你。
static X509Certificate generateSelfSignedX509Certificate() throws Exception {
// yesterday
Date validityBeginDate = new Date(System.currentTimeMillis() - 24 * 60 * 60 * 1000);
// in 2 years
Date validityEndDate = new Date(System.currentTimeMillis() + 2 * 365 * 24 * 60 * 60 * 1000);
// GENERATE THE PUBLIC/PRIVATE RSA KEY PAIR
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC");
keyPairGenerator.initialize(1024, new SecureRandom());
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// GENERATE THE X509 CERTIFICATE
X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
X500Principal dnName = new X500Principal("CN=John Doe");
certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
certGen.setSubjectDN(dnName);
certGen.setIssuerDN(dnName); // use the same
certGen.setNotBefore(validityBeginDate);
certGen.setNotAfter(validityEndDate);
certGen.setPublicKey(keyPair.getPublic());
certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
X509Certificate cert = certGen.generate(keyPair.getPrivate(), "BC");
// DUMP CERTIFICATE AND KEY PAIR
return cert;
// System.out.println(cert);
}
最佳答案
以下解决方案适用于在 Android 上使用 Spongy CaSTLe (Bouncy CaSTLe) 生成自签名证书。我已经使用 Android 10 (Q) 和 Android Pie 测试了代码。
此代码是 Netty 的 io.netty.handler.ssl.util.SelfSignedCertificate 的修改版本。原始版本需要充气城堡;这似乎在 Android 10 上默认不存在,导致 java.lang.NoClassDefFoundError: org.spongycaSTLe.jce.provider.BouncyCaSTLeProvider。因此,我不得不复制代码并修改它以使其与 Spongy CaSTLe 一起工作。
build.gradle
dependencies {
implementation 'com.madgag.spongycastle:bcpkix-jdk15on:1.58.0.0'
}
SelfSignedCertificate.java
import android.util.Base64;
import android.util.Log;
import org.spongycastle.asn1.x500.X500Name;
import org.spongycastle.cert.X509CertificateHolder;
import org.spongycastle.cert.X509v3CertificateBuilder;
import org.spongycastle.cert.jcajce.JcaX509CertificateConverter;
import org.spongycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.spongycastle.operator.ContentSigner;
import org.spongycastle.operator.jcajce.JcaContentSignerBuilder;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Date;
public final class SelfSignedCertificate {
private static final String TAG = SelfSignedCertificate.class.getSimpleName();
/**
* Current time minus 1 year, just in case software clock goes back due to time synchronization
*/
private static final Date DEFAULT_NOT_BEFORE = new Date(System.currentTimeMillis() - 86400000L * 365);
/**
* The maximum possible value in X.509 specification: 9999-12-31 23:59:59
*/
private static final Date DEFAULT_NOT_AFTER = new Date(253402300799000L);
/**
* FIPS 140-2 encryption requires the key length to be 2048 bits or greater.
* Let's use that as a sane default but allow the default to be set dynamically
* for those that need more stringent security requirements.
*/
private static final int DEFAULT_KEY_LENGTH_BITS = 2048;
/**
* FQDN to use if none is specified.
*/
private static final String DEFAULT_FQDN = "example.com";
/**
* 7-bit ASCII, as known as ISO646-US or the Basic Latin block of the
* Unicode character set
*/
private static final Charset US_ASCII = Charset.forName("US-ASCII");
private static final Provider provider = new BouncyCastleProvider();
private final File certificate;
private final File privateKey;
private final X509Certificate cert;
private final PrivateKey key;
/**
* Creates a new instance.
*/
public SelfSignedCertificate() throws CertificateException {
this(DEFAULT_NOT_BEFORE, DEFAULT_NOT_AFTER);
}
/**
* Creates a new instance.
*
* @param notBefore Certificate is not valid before this time
* @param notAfter Certificate is not valid after this time
*/
public SelfSignedCertificate(Date notBefore, Date notAfter) throws CertificateException {
this("example.com", notBefore, notAfter);
}
/**
* Creates a new instance.
*
* @param fqdn a fully qualified domain name
*/
public SelfSignedCertificate(String fqdn) throws CertificateException {
this(fqdn, DEFAULT_NOT_BEFORE, DEFAULT_NOT_AFTER);
}
/**
* Creates a new instance.
*
* @param fqdn a fully qualified domain name
* @param notBefore Certificate is not valid before this time
* @param notAfter Certificate is not valid after this time
*/
public SelfSignedCertificate(String fqdn, Date notBefore, Date notAfter) throws CertificateException {
// Bypass entropy collection by using insecure random generator.
// We just want to generate it without any delay because it's for testing purposes only.
this(fqdn, new SecureRandom(), DEFAULT_KEY_LENGTH_BITS, notBefore, notAfter);
}
/**
* Creates a new instance.
*
* @param fqdn a fully qualified domain name
* @param random the {@link java.security.SecureRandom} to use
* @param bits the number of bits of the generated private key
*/
public SelfSignedCertificate(String fqdn, SecureRandom random, int bits) throws CertificateException {
this(fqdn, random, bits, DEFAULT_NOT_BEFORE, DEFAULT_NOT_AFTER);
}
/**
* Creates a new instance.
*
* @param fqdn a fully qualified domain name
* @param random the {@link java.security.SecureRandom} to use
* @param bits the number of bits of the generated private key
* @param notBefore Certificate is not valid before this time
* @param notAfter Certificate is not valid after this time
*/
public SelfSignedCertificate(String fqdn, SecureRandom random, int bits, Date notBefore, Date notAfter)
throws CertificateException {
// Generate an RSA key pair.
final KeyPair keypair;
try {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(bits, random);
keypair = keyGen.generateKeyPair();
} catch (NoSuchAlgorithmException e) {
// Should not reach here because every Java implementation must have RSA key pair generator.
throw new Error(e);
}
String[] paths;
try {
// Try Bouncy Castle if the current JVM didn't have sun.security.x509.
paths = generateCertificate(fqdn, keypair, random, notBefore, notAfter);
} catch (Throwable t2) {
Log.d(TAG, "Failed to generate a self-signed X.509 certificate using Bouncy Castle:", t2);
throw new CertificateException("No provider succeeded to generate a self-signed certificate. See debug log for the root cause.", t2);
}
certificate = new File(paths[0]);
privateKey = new File(paths[1]);
key = keypair.getPrivate();
FileInputStream certificateInput = null;
try {
certificateInput = new FileInputStream(certificate);
cert = (X509Certificate) CertificateFactory.getInstance("X509").generateCertificate(certificateInput);
} catch (Exception e) {
throw new CertificateEncodingException(e);
} finally {
if (certificateInput != null) {
try {
certificateInput.close();
} catch (IOException e) {
Log.w(TAG, "Failed to close a file: " + certificate, e);
}
}
}
}
/**
* Returns the generated X.509 certificate file in PEM format.
*/
public File certificate() {
return certificate;
}
/**
* Returns the generated RSA private key file in PEM format.
*/
public File privateKey() {
return privateKey;
}
/**
* Returns the generated X.509 certificate.
*/
public X509Certificate cert() {
return cert;
}
/**
* Returns the generated RSA private key.
*/
public PrivateKey key() {
return key;
}
/**
* Deletes the generated X.509 certificate file and RSA private key file.
*/
public void delete() {
safeDelete(certificate);
safeDelete(privateKey);
}
private static String[] generateCertificate(String fqdn, KeyPair keypair, SecureRandom random, Date notBefore, Date notAfter)
throws Exception {
PrivateKey key = keypair.getPrivate();
// Prepare the information required for generating an X.509 certificate.
X500Name owner = new X500Name("CN=" + fqdn);
X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(
owner, new BigInteger(64, random), notBefore, notAfter, owner, keypair.getPublic());
ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(key);
X509CertificateHolder certHolder = builder.build(signer);
X509Certificate cert = new JcaX509CertificateConverter().setProvider(provider).getCertificate(certHolder);
cert.verify(keypair.getPublic());
return newSelfSignedCertificate(fqdn, key, cert);
}
private static String[] newSelfSignedCertificate(String fqdn, PrivateKey key, X509Certificate cert) throws IOException, CertificateEncodingException {
String keyText = "-----BEGIN PRIVATE KEY-----\n" + Base64.encodeToString(key.getEncoded(), Base64.DEFAULT) + "\n-----END PRIVATE KEY-----\n";
File keyFile = File.createTempFile("keyutil_" + fqdn + '_', ".key");
keyFile.deleteOnExit();
OutputStream keyOut = new FileOutputStream(keyFile);
try {
keyOut.write(keyText.getBytes(US_ASCII));
keyOut.close();
keyOut = null;
} finally {
if (keyOut != null) {
safeClose(keyFile, keyOut);
safeDelete(keyFile);
}
}
String certText = "-----BEGIN CERTIFICATE-----\n" + Base64.encodeToString(cert.getEncoded(), Base64.DEFAULT) + "\n-----END CERTIFICATE-----\n";
File certFile = File.createTempFile("keyutil_" + fqdn + '_', ".crt");
certFile.deleteOnExit();
OutputStream certOut = new FileOutputStream(certFile);
try {
certOut.write(certText.getBytes(US_ASCII));
certOut.close();
certOut = null;
} finally {
if (certOut != null) {
safeClose(certFile, certOut);
safeDelete(certFile);
safeDelete(keyFile);
}
}
return new String[]{certFile.getPath(), keyFile.getPath()};
}
private static void safeDelete(File certFile) {
if (!certFile.delete()) {
Log.w(TAG, "Failed to delete a file: " + certFile);
}
}
private static void safeClose(File keyFile, OutputStream keyOut) {
try {
keyOut.close();
} catch (IOException e) {
Log.w(TAG, "Failed to close a file: " + keyFile, e);
}
}
}
用法
private SslContext getSslContext() throws CertificateException, SSLException {
SelfSignedCertificate ssc = new SelfSignedCertificate(BuildConfig.APPLICATION_ID);
return SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).protocols("TLSv1.2").build();
}
我通过此 SslContext 来创建 ChannelPipeline 以启动支持 HTTPS 的 Netty 服务器,但您可以按照自己喜欢的方式使用生成的证书。
关于android - 如何在 Android 中以编程方式生成自签名证书并将其用于 https 服务器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24255103/
我最近在/ drawable中添加了一些.gifs,以便可以将它们与按钮一起使用。这个工作正常(没有错误)。现在,当我重建/运行我的应用程序时,出现以下错误: Error: Gradle: Execu
Android 中有返回内部存储数据路径的方法吗? 我有 2 部 Android 智能手机(Samsung s2 和 s7 edge),我在其中安装了一个应用程序。我想使用位于这条路径中的 sqlit
这个问题在这里已经有了答案: What's the difference between "?android:" and "@android:" in an android layout xml f
我只想知道 android 开发手机、android 普通手机和 android root 手机之间的实际区别。 我们不能从实体店或除 android marketplace 以外的其他地方购买开发手
自Gradle更新以来,我正在努力使这个项目达到标准。这是一个团队项目,它使用的是android-apt插件。我已经进行了必要的语法更改(编译->实现和apt->注释处理器),但是编译器仍在告诉我存在
我是android和kotlin的新手,所以请原谅要解决的一个非常简单的问题! 我已经使用导航体系结构组件创建了一个基本应用程序,使用了底部的导航栏和三个导航选项。每个导航选项都指向一个专用片段,该片
我目前正在使用 Facebook official SDK for Android . 我现在正在使用高级示例应用程序,但我不知道如何让它获取应用程序墙/流/状态而不是登录的用户。 这可能吗?在那种情
我在下载文件时遇到问题, 我可以在模拟器中下载文件,但无法在手机上使用。我已经定义了上网和写入 SD 卡的权限。 我在服务器上有一个 doc 文件,如果用户单击下载。它下载文件。这在模拟器中工作正常但
这个问题在这里已经有了答案: What is the difference between gravity and layout_gravity in Android? (22 个答案) 关闭 9
任何人都可以告诉我什么是 android 缓存和应用程序缓存,因为当我们谈论缓存清理应用程序时,它的作用是,缓存清理概念是清理应用程序缓存还是像内存管理一样主存储、RAM、缓存是不同的并且据我所知,缓
假设应用程序 Foo 和 Eggs 在同一台 Android 设备上。任一应用程序都可以获取设备上所有应用程序的列表。一个应用程序是否有可能知道另一个应用程序是否已经运行以及运行了多长时间? 最佳答案
我有点困惑,我只看到了从 android 到 pc 或者从 android 到 pc 的例子。我需要制作一个从两部手机 (android) 连接的 android 应用程序进行视频聊天。我在想,我知道
用于使用 Android 以编程方式锁定屏幕。我从 Stackoverflow 之前关于此的问题中得到了一些好主意,并且我做得很好,但是当我运行该代码时,没有异常和错误。而且,屏幕没有锁定。请在这段代
文档说: android:layout_alignParentStart If true, makes the start edge of this view match the start edge
我不知道这两个属性和高度之间的区别。 以一个TextView为例,如果我将它的layout_width设置为wrap_content,并将它的width设置为50 dip,会发生什么情况? 最佳答案
这两个属性有什么关系?如果我有 android:noHistory="true",那么有 android:finishOnTaskLaunch="true" 有什么意义吗? 最佳答案 假设您的应用中有
我是新手,正在尝试理解以下 XML 代码: 查看 developer.android.com 上的文档,它说“starStyle”是 R.attr 中的常量, public static final
在下面的代码中,为什么当我设置时单选按钮的外观会发生变化 android:layout_width="fill_parent" 和 android:width="fill_parent" 我说的是
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 9
假设我有一个函数 fun myFunction(name:String, email:String){},当我调用这个函数时 myFunction('Ali', 'ali@test.com ') 如何
我是一名优秀的程序员,十分优秀!