使用 javacard 生成签名 CSR 时出错(asn1 错误标记值)
error generating signed CSR with javacard (asn1 bad tag value)
我在 java 卡和 return public 密钥中生成了密钥对。我在 android 应用程序中再次生成 public 密钥,然后在 android
中生成 CSR
private byte[] CertReqGeneration() throws Exception
{
if(publickeyobj==null)
return null;
String info = "CN=cn, OU=ou, O=o, C=cn, ST=city";
X500Principal x500 = new X500Principal(info);
X500Name x500name;
x500name= X500Name.getInstance(x500.getEncoded());
CertificationRequestInfo csrInfo = new CertificationRequestInfo(x500name, publickeyobj, new DERSet());
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(csrInfo);
ASN1ObjectIdentifier oid = new ASN1ObjectIdentifier("0.0");
v.add(new DERSequence(oid));
v.add(new DERBitString(new byte[] {}));
byte[] encoded = new DERSequence(v).getEncoded();
byte[] PKCS10= DataSignGeneration(encoded);
byte[] encoded = Base64.encode(PKCS10);
String base64=new String(encoded);
return base64;
}
但是当我想在 ca 中发布它时,我收到错误 asn1 bad tag value met 0x8009310b
这是我的小程序签到码:
//sign
Signature signature=Signature.getInstance(Signature.ALG_RSA_SHA_PKCS1,false);
signature.init(thePrivateKey,Signature.MODE_SIGN);
signLength=signature.sign(buffer,(short)(ISO7816.OFFSET_CDATA & 0xFF), inputlength, buffer, (short)(0));
这是我的代码生成的签名 CSR
G1Xsg/fetpr1/RfbUzWmDqRqu2GvymoQwXUJS/tB36l1SAvrRyDtwCgVHB3ukYFSZJeavFISQ9+R4zD1qxjO6r/E2/3o9UfARm2GeTwEZFv8LVgULAPc/e64v5xkiQYO05QwHa5PtmaXXD4NtuSNvF9xgNjtdAkosKqEPLcme5k=
此错误似乎意味着私钥(在发出证书请求时创建)与 public 密钥(.crt 文件)不匹配。密钥对未成功加入有效的 SSL 证书。
解决方法:
首先将 .crt 文件导入本地计算机的个人证书存储区。 (开始按钮 > 运行:MMC > 文件菜单 > Add/Remove 管理单元 > 突出显示证书管理单元并单击添加按钮 > select 计算机帐户并单击完成 > 单击确定 > 钻取进入个人 > 证书 > 右键单击并 select 所有任务 > select 导入 > .crt 文件指南。)此时您的证书基本上是半证书。它仍然缺少它的私钥。
其次,双击您刚刚导入的 crt 证书文件,select 详细信息选项卡,一直向下滚动到指纹并突出显示指纹。在下部窗格中,阻止并复制指纹的所有字母。将指纹字符粘贴到记事本中。打开命令提示符并 运行 此命令:Certutil /?
您想要 运行 的命令是:
certutil -repairstore 我的“{在此处插入所有指纹字符}”
当您看到响应:“CertUtil:-repairstore 命令成功完成”时,您应该有一个与个人存储中的 .crt 文件关联的私钥。不再需要 运行 通过“完成证书请求...”向导。此时证书应该出现在 IIS 管理器的服务器证书列表中。尝试编辑网站的 https 绑定时,它也应该在 SSL 证书下拉列表中可用。
让我们先看看您生成的CSR。我用 an online ASN.1 decoder.
嗯,在线工具不会解码您的 CSR。所以它无效:
好的,让我们看看有效的 CSR 是什么样的。我使用 OpenSSL 生成了 CSR:
ghasemi@ghasemi-MS-7693:~$ openssl genrsa -out priv.key 1024
Generating RSA private key, 1024 bit long modulus
............................++++++
.......................++++++
e is 65537 (0x10001)
ghasemi@ghasemi-MS-7693:~$ ls
priv.key
ghasemi@ghasemi-MS-7693:~$ openssl req -new -key priv.key -out my_csr.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:IR
State or Province Name (full name) [Some-State]:Tehran
Locality Name (eg, city) []:Tehran
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Company
Organizational Unit Name (eg, section) []:Section
Common Name (e.g. server FQDN or YOUR name) []:Ebrahim
Email Address []:ebr.ghasemi at gmail
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
ghasemi@ghasemi-MS-7693:~$ ls
my_csr.csr priv.key
正如您在上面看到的,我生成了一个带有 1024 位密钥对的 CSR。看内容:
ghasemi@ghasemi-MS-7693:~$ cat my_csr.csr
-----BEGIN CERTIFICATE REQUEST-----
MIIByzCCATQCAQAwgYoxCzAJBgNVBAYTAklSMQ8wDQYDVQQIDAZUZWhyYW4xDzAN
BgNVBAcMBlRlaHJhbjEQMA4GA1UECgwHQ29tcGFueTEQMA4GA1UECwwHU2VjdGlv
bjEQMA4GA1UEAwwHRWJyYWhpbTEjMCEGCSqGSIb3DQEJARYUZWJyLmdoYXNlbWkg
YXQgZ21haWwwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOJK2YUOSREjrDbZ
auC3/509NOAL+xjqJ2x947CtSIyuPSLuplDW4oAkvaqYvA5ocIxeAGbW9WZF/OdM
8z5XoQF3+ogGsEna1TZN1TY5XOdrRgIu9gFA2KRNPjnEZIkt7FLBJUQlk5aithk4
lfy73XCoosjWUwBOKac5ZIKOuXEHAgMBAAGgADANBgkqhkiG9w0BAQsFAAOBgQCF
hQzkAgfongzP9EKV+oTwVhqkQ7bfy5TE85YIR3aymuzbvAc9KWdWydOxHiHqXbmZ
ECWvLlMsrlrpyDO2Xxy8UXFJkNgH4Rys267ETaoYh19htc4qZzNqK/1ejQBUidgY
QoafJxv3YhO+gYr2txyxPRN0etcquElwx0odF8KBVA==
-----END CERTIFICATE REQUEST-----
ghasemi@ghasemi-MS-7693:~$
嗯,明显的不同是长度以及开始和结束行。我的 CSR 真的比你的大。它以 -----BEGIN CERTIFICATE REQUEST-----
开头,也以 -----END CERTIFICATE REQUEST-----
结尾。
让我们使用 ASN.1 解码器检查其内容:
好的。如您所见,在 CSR 中有我在生成 CSR 时输入的纯文本字符,但在您的中没有。
怎么了?
您的小程序,returns 仅您的 CSR 信息的 签名。它不是 CSR,它只是它的签名。因此,您必须使用 CSR 信息 和 签名 生成 CSR。此外,CSR 信息中使用的 public 密钥必须是其私钥用于生成签名的 public 密钥,否则 CA 将无法验证 CSR 的有效性,并考虑您的 CSR 作为 forged/fake CSR。
因此,在卡内生成密钥对后,将 public 密钥导出到 android 应用程序。然后生成 CSR 信息并在卡内生成此信息的签名,然后使用 csr 信息和签名构建您的 CSR。
@Abraham 分析似乎正确:生成的 CSR 内容无效
我猜这段代码的目的是构建要签名的 ASN.1 结构,但 bouncycastle 会为您完成。
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(csrInfo);
ASN1ObjectIdentifier oid = new ASN1ObjectIdentifier("0.0");
v.add(new DERSequence(oid));
v.add(new DERBitString(new byte[] {}));
byte[] encoded = new DERSequence(v).getEncoded();
byte[] PKCS10= DataSignGeneration(encoded);
评论后我们可以看到需要创建一个未签名的 PKCS10 请求发送到 java 卡在那里签名,并得到结果来构建签名的 PKCS10 请求
Bouncycastle 不提供 API 来执行该操作。我建议查看 PKCS10 example and the source code of PKCS10CertificateRequestBuilder 以适应使用您的 Java 卡。如果你想在 android 中使用,你需要特殊的发行版 spongycastle。
这是一个创建未签名请求的工作示例(没有java卡,但应该很容易适应。只是签名过程)
public byte[] generateCSR (X500Name subject, PublicKey publicKey, String signatureAlgorithm) throws Exception{
//Create the unsigned CSR
CertificationRequestInfo info = new CertificationRequestInfo(
x500name, SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()),new DERSet());
//The CSR bytes to be signed
byte dataToSign[] = info.getEncoded(ASN1Encoding.DER);
//Send the CSR to the card
byte signedData[] = signOnJavaCard (dataToSign,signatureAlgorithm);
//Build the signed CSR
AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(signatureAlgorithm);
PKCS10CertificationRequest csr = new PKCS10CertificationRequest(
new CertificationRequest(info, sigAlgId, new DERBitString(signedData)));
byte signedCSR = csr.getEncoded();
//Verify signature validity
ContentVerifierProvider verifier = new JcaContentVerifierProviderBuilder().setProvider(new BouncyCastleProvider()).build(publicKey);
boolean valid = csr.isSignatureValid(verifier);
return signedCSR;
}
为了测试代码,我使用了本地签名方法
public byte signOnJavaCard (byte dataToSign[], String signatureAlgorithm)
Signature signature = Signature.getInstance(signatureAlgorithm);
signature.initSign(privateKey); //get the private key in some way...
signature.update(dataToSign);
return signature.sign();
}
我在 java 卡和 return public 密钥中生成了密钥对。我在 android 应用程序中再次生成 public 密钥,然后在 android
中生成 CSR private byte[] CertReqGeneration() throws Exception
{
if(publickeyobj==null)
return null;
String info = "CN=cn, OU=ou, O=o, C=cn, ST=city";
X500Principal x500 = new X500Principal(info);
X500Name x500name;
x500name= X500Name.getInstance(x500.getEncoded());
CertificationRequestInfo csrInfo = new CertificationRequestInfo(x500name, publickeyobj, new DERSet());
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(csrInfo);
ASN1ObjectIdentifier oid = new ASN1ObjectIdentifier("0.0");
v.add(new DERSequence(oid));
v.add(new DERBitString(new byte[] {}));
byte[] encoded = new DERSequence(v).getEncoded();
byte[] PKCS10= DataSignGeneration(encoded);
byte[] encoded = Base64.encode(PKCS10);
String base64=new String(encoded);
return base64;
}
但是当我想在 ca 中发布它时,我收到错误 asn1 bad tag value met 0x8009310b
这是我的小程序签到码:
//sign
Signature signature=Signature.getInstance(Signature.ALG_RSA_SHA_PKCS1,false);
signature.init(thePrivateKey,Signature.MODE_SIGN);
signLength=signature.sign(buffer,(short)(ISO7816.OFFSET_CDATA & 0xFF), inputlength, buffer, (short)(0));
这是我的代码生成的签名 CSR
G1Xsg/fetpr1/RfbUzWmDqRqu2GvymoQwXUJS/tB36l1SAvrRyDtwCgVHB3ukYFSZJeavFISQ9+R4zD1qxjO6r/E2/3o9UfARm2GeTwEZFv8LVgULAPc/e64v5xkiQYO05QwHa5PtmaXXD4NtuSNvF9xgNjtdAkosKqEPLcme5k=
此错误似乎意味着私钥(在发出证书请求时创建)与 public 密钥(.crt 文件)不匹配。密钥对未成功加入有效的 SSL 证书。
解决方法: 首先将 .crt 文件导入本地计算机的个人证书存储区。 (开始按钮 > 运行:MMC > 文件菜单 > Add/Remove 管理单元 > 突出显示证书管理单元并单击添加按钮 > select 计算机帐户并单击完成 > 单击确定 > 钻取进入个人 > 证书 > 右键单击并 select 所有任务 > select 导入 > .crt 文件指南。)此时您的证书基本上是半证书。它仍然缺少它的私钥。
其次,双击您刚刚导入的 crt 证书文件,select 详细信息选项卡,一直向下滚动到指纹并突出显示指纹。在下部窗格中,阻止并复制指纹的所有字母。将指纹字符粘贴到记事本中。打开命令提示符并 运行 此命令:Certutil /?
您想要 运行 的命令是:
certutil -repairstore 我的“{在此处插入所有指纹字符}”
当您看到响应:“CertUtil:-repairstore 命令成功完成”时,您应该有一个与个人存储中的 .crt 文件关联的私钥。不再需要 运行 通过“完成证书请求...”向导。此时证书应该出现在 IIS 管理器的服务器证书列表中。尝试编辑网站的 https 绑定时,它也应该在 SSL 证书下拉列表中可用。
让我们先看看您生成的CSR。我用 an online ASN.1 decoder.
嗯,在线工具不会解码您的 CSR。所以它无效:
好的,让我们看看有效的 CSR 是什么样的。我使用 OpenSSL 生成了 CSR:
ghasemi@ghasemi-MS-7693:~$ openssl genrsa -out priv.key 1024
Generating RSA private key, 1024 bit long modulus
............................++++++
.......................++++++
e is 65537 (0x10001)
ghasemi@ghasemi-MS-7693:~$ ls
priv.key
ghasemi@ghasemi-MS-7693:~$ openssl req -new -key priv.key -out my_csr.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:IR
State or Province Name (full name) [Some-State]:Tehran
Locality Name (eg, city) []:Tehran
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Company
Organizational Unit Name (eg, section) []:Section
Common Name (e.g. server FQDN or YOUR name) []:Ebrahim
Email Address []:ebr.ghasemi at gmail
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
ghasemi@ghasemi-MS-7693:~$ ls
my_csr.csr priv.key
正如您在上面看到的,我生成了一个带有 1024 位密钥对的 CSR。看内容:
ghasemi@ghasemi-MS-7693:~$ cat my_csr.csr
-----BEGIN CERTIFICATE REQUEST-----
MIIByzCCATQCAQAwgYoxCzAJBgNVBAYTAklSMQ8wDQYDVQQIDAZUZWhyYW4xDzAN
BgNVBAcMBlRlaHJhbjEQMA4GA1UECgwHQ29tcGFueTEQMA4GA1UECwwHU2VjdGlv
bjEQMA4GA1UEAwwHRWJyYWhpbTEjMCEGCSqGSIb3DQEJARYUZWJyLmdoYXNlbWkg
YXQgZ21haWwwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOJK2YUOSREjrDbZ
auC3/509NOAL+xjqJ2x947CtSIyuPSLuplDW4oAkvaqYvA5ocIxeAGbW9WZF/OdM
8z5XoQF3+ogGsEna1TZN1TY5XOdrRgIu9gFA2KRNPjnEZIkt7FLBJUQlk5aithk4
lfy73XCoosjWUwBOKac5ZIKOuXEHAgMBAAGgADANBgkqhkiG9w0BAQsFAAOBgQCF
hQzkAgfongzP9EKV+oTwVhqkQ7bfy5TE85YIR3aymuzbvAc9KWdWydOxHiHqXbmZ
ECWvLlMsrlrpyDO2Xxy8UXFJkNgH4Rys267ETaoYh19htc4qZzNqK/1ejQBUidgY
QoafJxv3YhO+gYr2txyxPRN0etcquElwx0odF8KBVA==
-----END CERTIFICATE REQUEST-----
ghasemi@ghasemi-MS-7693:~$
嗯,明显的不同是长度以及开始和结束行。我的 CSR 真的比你的大。它以 -----BEGIN CERTIFICATE REQUEST-----
开头,也以 -----END CERTIFICATE REQUEST-----
结尾。
让我们使用 ASN.1 解码器检查其内容:
好的。如您所见,在 CSR 中有我在生成 CSR 时输入的纯文本字符,但在您的中没有。
怎么了?
您的小程序,returns 仅您的 CSR 信息的 签名。它不是 CSR,它只是它的签名。因此,您必须使用 CSR 信息 和 签名 生成 CSR。此外,CSR 信息中使用的 public 密钥必须是其私钥用于生成签名的 public 密钥,否则 CA 将无法验证 CSR 的有效性,并考虑您的 CSR 作为 forged/fake CSR。
因此,在卡内生成密钥对后,将 public 密钥导出到 android 应用程序。然后生成 CSR 信息并在卡内生成此信息的签名,然后使用 csr 信息和签名构建您的 CSR。
@Abraham 分析似乎正确:生成的 CSR 内容无效
我猜这段代码的目的是构建要签名的 ASN.1 结构,但 bouncycastle 会为您完成。
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(csrInfo);
ASN1ObjectIdentifier oid = new ASN1ObjectIdentifier("0.0");
v.add(new DERSequence(oid));
v.add(new DERBitString(new byte[] {}));
byte[] encoded = new DERSequence(v).getEncoded();
byte[] PKCS10= DataSignGeneration(encoded);
评论后我们可以看到需要创建一个未签名的 PKCS10 请求发送到 java 卡在那里签名,并得到结果来构建签名的 PKCS10 请求
Bouncycastle 不提供 API 来执行该操作。我建议查看 PKCS10 example and the source code of PKCS10CertificateRequestBuilder 以适应使用您的 Java 卡。如果你想在 android 中使用,你需要特殊的发行版 spongycastle。
这是一个创建未签名请求的工作示例(没有java卡,但应该很容易适应。只是签名过程)
public byte[] generateCSR (X500Name subject, PublicKey publicKey, String signatureAlgorithm) throws Exception{
//Create the unsigned CSR
CertificationRequestInfo info = new CertificationRequestInfo(
x500name, SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()),new DERSet());
//The CSR bytes to be signed
byte dataToSign[] = info.getEncoded(ASN1Encoding.DER);
//Send the CSR to the card
byte signedData[] = signOnJavaCard (dataToSign,signatureAlgorithm);
//Build the signed CSR
AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(signatureAlgorithm);
PKCS10CertificationRequest csr = new PKCS10CertificationRequest(
new CertificationRequest(info, sigAlgId, new DERBitString(signedData)));
byte signedCSR = csr.getEncoded();
//Verify signature validity
ContentVerifierProvider verifier = new JcaContentVerifierProviderBuilder().setProvider(new BouncyCastleProvider()).build(publicKey);
boolean valid = csr.isSignatureValid(verifier);
return signedCSR;
}
为了测试代码,我使用了本地签名方法
public byte signOnJavaCard (byte dataToSign[], String signatureAlgorithm)
Signature signature = Signature.getInstance(signatureAlgorithm);
signature.initSign(privateKey); //get the private key in some way...
signature.update(dataToSign);
return signature.sign();
}