1 /** NSS signature generator
2 *
3 * Mozilla has two APIs for generating signatures (older SEC_PKCS7)
4 * and newer SMIME (CMS). We are using newer API.
5 *
6 * You have to have certificate (CERTCertificate * )which will be
7 * used for signing.
8 */
9
10 #include "NSSSignatureGenerator.h"
11
12 #include <podofo.h>
13
sm_write_stream(void * arg,const char * buf,unsigned long len)14 void NSSSignatureGenerator::sm_write_stream(void *arg, const char *buf, unsigned long len)
15 {
16 NSSSignatureGenerator* pThis = static_cast<NSSSignatureGenerator*>(arg);
17 pThis->signature.append(buf, len);
18 }
19
getDigestAlgor(CERTCertificate * pCert)20 SECOidTag NSSSignatureGenerator::getDigestAlgor(CERTCertificate *pCert)
21 {
22 SECAlgorithmID *algID = &pCert->signature;
23 if(algID==NULL) return SEC_OID_MD5;
24 SECOidTag algOIDTag = SECOID_FindOIDTag(&algID->algorithm);
25 switch(algOIDTag)
26 {
27 case SEC_OID_MD5:
28 case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION:
29 case SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC:
30 return SEC_OID_MD5;
31 case SEC_OID_SHA1:
32 case SEC_OID_ISO_SHA_WITH_RSA_SIGNATURE:
33 case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
34 case SEC_OID_PKCS5_PBE_WITH_SHA1_AND_DES_CBC:
35 case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC4:
36 case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC4:
37 case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC:
38 case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
39 case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
40 case SEC_OID_PKCS12_RSA_SIGNATURE_WITH_SHA1_DIGEST:
41 case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST:
42 case SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST:
43 case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC4:
44 case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC4:
45 case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC:
46 case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_2KEY_TRIPLE_DES_CBC:
47 case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
48 case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
49 case SEC_OID_HMAC_SHA1:
50 return SEC_OID_SHA1;
51 case SEC_OID_SHA256:
52 case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION:
53 case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE:
54 case SEC_OID_HMAC_SHA256:
55 return SEC_OID_SHA256;
56 case SEC_OID_SHA384:
57 case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION:
58 case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE:
59 case SEC_OID_HMAC_SHA384:
60 return SEC_OID_SHA384;
61 case SEC_OID_SHA512:
62 case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION:
63 case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE:
64 case SEC_OID_HMAC_SHA512:
65 return SEC_OID_SHA512;
66 }
67 PODOFO_ASSERT(0);
68 return SEC_OID_MD5;
69 }
70
71 // create message with signature
createSign(CERTCertificate * cert)72 NSSCMSMessage* NSSSignatureGenerator::createSign(CERTCertificate *cert)
73 {
74 NSSCMSSignedData *sigd = NULL;
75 NSSCMSContentInfo *cinfo = NULL;
76 NSSCMSSignerInfo *signerinfo = NULL;
77
78 SECOidTag hash=getDigestAlgor(cert);
79
80 NSSCMSMessage *cmsg = NSS_CMSMessage_Create(NULL); // create a message on its own pool
81 if (cmsg == NULL) return NULL;
82 if ((sigd = NSS_CMSSignedData_Create(cmsg)) == NULL) {
83 NSS_CMSMessage_Destroy(cmsg); return NULL;
84 }
85 cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
86 if (NSS_CMSContentInfo_SetContent_SignedData(cmsg, cinfo, sigd) != SECSuccess) {
87 NSS_CMSMessage_Destroy(cmsg); return NULL;
88 }
89
90 // if !detatched, the contentinfo will alloc a data item for us
91 cinfo = NSS_CMSSignedData_GetContentInfo(sigd);
92 if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_TRUE ) != SECSuccess) {
93 NSS_CMSMessage_Destroy(cmsg); return NULL;
94 }
95
96 signerinfo = NSS_CMSSignerInfo_Create(cmsg, cert, hash);
97 if (signerinfo == NULL) { NSS_CMSMessage_Destroy(cmsg); return NULL; }
98
99 // we want the cert chain included for this one
100 if (NSS_CMSSignerInfo_IncludeCerts(signerinfo, NSSCMSCM_CertChainWithRoot, certUsageEmailSigner) != SECSuccess)
101 { NSS_CMSMessage_Destroy(cmsg); return NULL; }
102
103 // SMIME RFC says signing time should always be added
104 if (NSS_CMSSignerInfo_AddSigningTime(signerinfo, PR_Now()) != SECSuccess) { NSS_CMSMessage_Destroy(cmsg); return NULL; }
105
106 if (NSS_CMSSignedData_AddSignerInfo(sigd, signerinfo) != SECSuccess) {
107 NSS_CMSMessage_Destroy(cmsg); return NULL; }
108 return cmsg;
109 }
110
111
NSSSignatureGenerator(CERTCertificate * pCert)112 NSSSignatureGenerator::NSSSignatureGenerator(CERTCertificate *pCert)
113 :pCert(pCert)
114 {
115 pSignature = NULL;
116 }
117
~NSSSignatureGenerator()118 NSSSignatureGenerator::~NSSSignatureGenerator()
119 {
120 delete pSignature;
121 if(enc!=NULL) NSS_CMSEncoder_Cancel(enc);
122 if(cmsg!=NULL) NSS_CMSMessage_Destroy(cmsg);
123 }
124
init()125 bool NSSSignatureGenerator::init()
126 {
127 cmsg = createSign(pCert);
128 if(cmsg==NULL) return false;
129 // Prepare encoder
130 enc = NSS_CMSEncoder_Start(cmsg,
131 sm_write_stream, this, // DER output callback
132 NULL, NULL, // destination storage
133 NULL, NULL, // password callback
134 NULL, NULL, // decrypt key callback
135 NULL, NULL ); // detached digests
136 }
137
appendData(const char * pData,unsigned int dataSize)138 bool NSSSignatureGenerator::appendData(const char *pData, unsigned int dataSize)
139 {
140 if(enc == NULL) return false;
141
142 if (NSS_CMSEncoder_Update(enc, pData, dataSize) != SECSuccess) {
143 NSS_CMSEncoder_Cancel(enc);
144 enc = NULL;
145 return false;
146 }
147 return true;
148 }
149
finishData()150 bool NSSSignatureGenerator::finishData()
151 {
152 if(enc == NULL) return false;
153
154 if (NSS_CMSEncoder_Finish(enc) != SECSuccess) {
155 enc = NULL;
156 return false;
157 }
158 enc = NULL;
159 return true;
160 }
161
getSignature()162 const PoDoFo::PdfData* NSSSignatureGenerator::getSignature()
163 {
164 if(pSignature==NULL) {
165 pSignature = new PoDoFo::PdfData(signature.c_str(), signature.size());
166 }
167 return pSignature;
168 }
169