使用openssl 创建pkcs12 keystore 为啥会出错

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用openssl 创建pkcs12 keystore 为啥会出错相关的知识,希望对你有一定的参考价值。

参考技术A (1)Openssl生成公私钥
使用Openssl是为了生成公钥和私钥对,用于外部商户系统和xxx系统之间报文的安全性验证。如果使用者不需要生成公私钥,而是直接对报文进行处理,则参考第四部分,计算摘要及签名值。
1. 安装openssl步骤直接点击exe文件。出现需要安装vs2008插件的,直接忽略。

2. 在安装过程中找到OpenSSL相应的安装目录,进入bin目录下找到openssl.exe可执行文件,点击运行。然后分别按顺序输入如下命令:
a. genrsa –out private-rsa.key 1024
说明:该命令是生成一个没有加密的私钥
genrsa 生成私钥文件,私钥存储在private-rsa.key中,长度为1024。out后面指定输出文件名。
private-rsa.key 为生成的私钥文件,但该文件必须经过处理得到私钥。

b. req –new –x509 –key private-rsa.key –days 750 –outpublic-rsa.cer
说明:根据private-rsa.key生成证书public-rsa.cer
-new 表示新的请求
-509 表示输出的证书结构
750表示证书有效天数
-out public-rsa.cer -out后面表示公钥证书,用于验证数字签名,此公钥证书或者公钥需要提前发送给需要验证本单位或部门数据的接收方。

c. pkcs12 –export –name test-alias –in public-rsa.cer–inkey private-rsa.key –out 99bill-rsa.pfx
说明:生成PKCS12 格式Keystore
密码前后输入要一致,这个密码在用Keystore生成公私钥过程中会用到。
Public-rsa.cer, private-rsa.key是之前生成的。

附1:
下述代码是从99bill-rsa.pfx中获取私钥的Java版本代码。因为private-rsa.key中生成的私钥无法直接使用,必须进行一定的处理。
代码有几个注意点:
文件流初始化路径需要根据自己的实际路径来填写。
密码是在第二节中c步骤中的密码,本实例输入的是suning。
KeyStorekeyStore = KeyStore.getInstance("PKCS12");
FileInputStreamfileInputStream = newFileInputStream("D:/OpenSSL/bin/99bill-rsa.pfx");
char[]nPassword = "suning".toCharArray();
StringkeyAlias = null;
keyStore.load(fileInputStream,nPassword);
fileInputStream.close();
System.out.println("keystoretype=" + keyStore.getType());
Enumeration<String>enumeration = keyStore.aliases();
if(enumeration.hasMoreElements())

keyAlias = (String) enumeration.nextElement();
System.out.println("alias=[" + keyAlias +"]");

System.out.println("iskey entry=" + keyStore.isKeyEntry(keyAlias));
PrivateKeyprikey = (PrivateKey) keyStore.getKey(keyAlias, nPassword);
//私钥转成字符串
StringprivateStr = Base64.encodeBase64String(prikey.getEncoded()).trim();
//生成公钥字符串,还可以通过cer证书生成
Certificatecert = keyStore.getCertificate(keyAlias);
PublicKeypubkey = cert.getPublicKey();
StringpublicStr = Base64.encodeBase64String(pubKey.getEncoded()).trim();
注意:
1. 所用类的说明:
Base64:
import org.apache.commons.net.util.Base64;
Certificate:
import java.security.cert.Certificate;
2. 在openssl生成公私钥过程中,用户输入了密码。本例中密码为suning。

1. 摘要及生成方法
摘要的生成过程(digest方法全部实现了下述3个过程):
1. 根据key对传来的map数据排序;
2. 生成a1=b1&a2=b2&a3=b3形式的字符串,排除某些字符串Key值;
3. 调用digest方法进行md5编码;
以上三步均通过Digest.digest()方法实现:
String digest = Digest.digest(Map<String,String> map, String... keys);
传递的http报文体body内容如a1=b1&a2=b2&a3=b3形式的字符串,提取出需要加签的字符串并转成map形式。execludeKes是要排除的字段,是一个字符串数组。
计算摘要这一步很关键,因为选取的字段要求发送方和接收方必须一致,即发送方对哪些字段计算摘要,那么同样地接收方也必须对相同的字段计算摘要,否则会报6601的错误。
说明:a. Map是存储了计算摘要的字段
b. keys表示排除的字段,不能用于计算摘要的字段,如signature,signAlgorithm
2. 公钥证书及字符串转换方法
转换的目的:便于存储。(商户直接提供公钥证书也可以,但是对于向验签系统提供公钥字符串的商户,需要用下述代码把公钥转成字符串)
1. 公钥/私钥字符串转成公钥/私钥,主要是把字符串转成公钥PublicKey
X509EncodedKeySpec pubKeySpec = newX509EncodedKeySpec(Base64.decodeBase64(strPubKey));
KeyFactorykeyFactory = KeyFactory.getInstance(RSA);
PublicKeypubKey = keyFactory.generatePublic(pubKeySpec);
2. 公钥或私钥转成Base64字符串:
StringpublicStr = Base64.encodeBase64String(pubKey.getEncoded()).trim();
3. 公私钥验证方法
验证目的:公私钥生成之后,需要验证是否匹配。(之前许多商户生成公私钥混乱,无法确定公私钥是否匹配就添加到验签系统中)。此代码由用户自己用junit验证运行。验证公私钥生成是否正确,如果result为true,则说明公私钥生成正确;否则生成的公私钥有问题,不能使用。
String prik1 ="";
String pubb ="";
String data ="wkk";
String digest =Digest.digest(data);
PrivateKey privateKey =RSAUtil.getPrivateKey(prik1);
String sign =RSAUtil.sign(digest, privateKey);
boolean result =RSAUtil.vertiy(digest, sign,
RSAUtil.getPublicKey(pubb));
System.out.println(result);

如何使用 openssl 或任何其他在智能卡上签名的库创建 PKCS7 signedData 结构?

【中文标题】如何使用 openssl 或任何其他在智能卡上签名的库创建 PKCS7 signedData 结构?【英文标题】:how to create PKCS7 signedData structure with openssl or any other library with signing on smart card? 【发布时间】:2013-03-25 15:01:20 【问题描述】:

我需要创建 PKCS7 signedData 结构并在智能卡上执行签名。这几乎是 openssl 函数 PKCS7_sign 所做的,除了签名。也许有人可以为这个问题提供一些建议,即如何使用 openssl 或任何其他 c/c++ 跨平台库来做到这一点。 至于 openssl,PKCS7_sign 函数的标志 PKCS7_PARTIAL 或 PKCS7_STREAM 似乎很有用。如果我使用这些标志中的任何一个,我可以获得几乎完整的 PKCS7 结构。在这种情况下,结构是完整的,只是它不包含“数据”和“符号”。所以我只需要添加这些元素。但我没有找到如何做到这一点的方法。有人知道吗?

【问题讨论】:

不知道你是怎么处理智能卡的。 OpenSSL 唯一需要的是它的功能参数(证书、私钥和数据)。您必须从智能卡中提取证书和私钥并提供给 OpenSSL 功能。但是,如果您不能导出私钥,那就是一个问题。在我看来,保留这两个部分(在智能卡上和使用 OpenSSL 分开)。 无法从智能卡导出私钥。看起来这可能是一个奇怪的解决方法: - 生成公共指数 =1 的新 RSA 密钥对; - 使用 PKCS7_sing 函数和此密钥对创建 PKCS7 结构; - 使用 i2d_PKCS7_bio 获取生成的 PKCS7 结构。由于公共指数 =1,此结构包含准备 RSA 解密数据而不是符号; - 将此数据发送到智能卡进行解密并将其替换为结果。但这需要自己复制一些 openssl 函数,因为其中一个原始函数包含密钥检查。 即使无法导出私钥,也必须有某种方式使用该私钥对数据进行签名。为此,您可能需要修改 PKCS7_sign 的源代码以控制签名部分。在这里,您可以调用将使用智能卡对数据进行签名的函数,而不是使用 OpenSSL 签名。 @dbasic,谢谢。但问题是这不是微不足道的,至少对我来说是这样。 openssl PKCS7_sign 源代码中没有单独的函数用于签署 PKCS7 结构。取而代之的是,此操作分为一些功能,例如 PKCS7_dataInit 和 PKCS7_dataFinal,并且还使用了特殊类型的 BIO。 我正在解决同样的问题。到目前为止,我一直在尝试覆盖默认的签名方法(使用method = RSA_get_default_method,然后覆盖它的method->flags 和method->rsa_sign,用我的智能卡签名方法替换它)。请注意,您应该 method->flags |= RSA_METHOD_FLAG_NO_CHECK .. 我还在尝试,您是否设法解决了这个问题? 【参考方案1】:

您通常不希望(或在技术上被阻止)从智能卡中提取私钥。因为这是智能卡的关键——加密内存和连接的 CPU 的防篡改位,永远不会泄露您的私钥。

因此,您需要很好地要求芯片卡为您签名。

OpenSSL 可以做到这一点——但需要知道如何与芯片卡通信。这通常是通过“引擎”完成的。最常见的是 #15 的 pkcs#11 用于此 - 与供应商芯片卡(阅读器)驱动程序结合使用。

然后您通常需要获取插槽和密钥标识符:

# Extracting slot, auth ids and key id's for later use/reference
#
set `pkcs11-tool --module /usr/lib/opensc-pkcs11.so --list-slots | grep Slot | grep SCM`
SLOT=$2
set `pkcs15-tool --list-keys | grep ID`
AID=$4
KID=$7

之后你可以在卡片上做“事情”:

/usr/bin/openssl << EOM
engine dynamic -pre SO_PATH:/Library/OpenSC/lib/engines/engine_pkcs11.so  -pre ID:pkcs11 -pre LIST_ADD:1 -pre LOAD -pre MODULE_PATH:opensc-pkcs11.so
XXX -engine pkcs11 -b-key slot_$SLOT-id_$KID -keyform engine  ....
EOM

这样的事情之一就是签署 pkcs7。从代码 - 几乎做同样的事情。我通常使用 openssl 它的应用程序目录中的 app/util 便利 stuf 来简化生活。

【讨论】:

以上是关于使用openssl 创建pkcs12 keystore 为啥会出错的主要内容,如果未能解决你的问题,请参考以下文章

通过 Windows 中的 OpenSSL 为我的 Android 应用程序创建带有自签名证书的 PKCS#12 文件

使用OpenSSL创建多级CA证书链签发证书并导出为pkcs12/p12/pfx文件

使用OpenSSL创建多级CA证书链签发证书并导出为pkcs12/p12/pfx文件

指定 CSR 文件时,OpenSSL pkcs12 导出失败

openssl PKCS12 导出 - 在密码中使用单引号字符时出现错误行为

使用 openssl 将多个证书加载到 PKCS12