File: NSSSignatureGenerator.cpp

package info (click to toggle)
libpodofo 0.9.0-1.1
  • links: PTS
  • area: main
  • in suites: wheezy
  • size: 5,068 kB
  • sloc: cpp: 53,348; ansic: 339; sh: 96; makefile: 59
file content (168 lines) | stat: -rw-r--r-- 5,685 bytes parent folder | download | duplicates (10)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/** NSS signature generator
 *
 * Mozilla has two APIs for generating signatures (older SEC_PKCS7)
 * and newer SMIME (CMS). We are using newer API. 
 *
 * You have to have certificate (CERTCertificate * )which will be 
 * used for signing.
 */

#include "NSSSignatureGenerator.h"

#include <podofo.h>

void NSSSignatureGenerator::sm_write_stream(void *arg, const char *buf, unsigned long len) 
{
    NSSSignatureGenerator* pThis = static_cast<NSSSignatureGenerator*>(arg);
    pThis->signature.append(buf, len);
}

SECOidTag NSSSignatureGenerator::getDigestAlgor(CERTCertificate *pCert)
{
    SECAlgorithmID *algID = &pCert->signature;
    if(algID==NULL) return SEC_OID_MD5;
    SECOidTag algOIDTag = SECOID_FindOIDTag(&algID->algorithm);
    switch(algOIDTag)
    {
		case SEC_OID_MD5:
		case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION:
		case SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC:
			return SEC_OID_MD5;
		case SEC_OID_SHA1:
		case SEC_OID_ISO_SHA_WITH_RSA_SIGNATURE:
		case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
		case SEC_OID_PKCS5_PBE_WITH_SHA1_AND_DES_CBC:
		case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC4:
		case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC4:
		case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC:
		case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
		case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
		case SEC_OID_PKCS12_RSA_SIGNATURE_WITH_SHA1_DIGEST:
		case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST:
		case SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST:
		case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC4:
		case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC4:
		case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC:
		case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_2KEY_TRIPLE_DES_CBC:
		case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
		case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
		case SEC_OID_HMAC_SHA1:
			return SEC_OID_SHA1;
		case SEC_OID_SHA256:
		case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION:
		case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE:
		case SEC_OID_HMAC_SHA256:
			return SEC_OID_SHA256;
		case SEC_OID_SHA384:
		case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION:
		case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE:
		case SEC_OID_HMAC_SHA384:
			return SEC_OID_SHA384;
		case SEC_OID_SHA512:
		case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION:
		case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE:
		case SEC_OID_HMAC_SHA512:
			return SEC_OID_SHA512;
    }
    PODOFO_ASSERT(0);
    return SEC_OID_MD5;
}

// create message with signature
NSSCMSMessage* NSSSignatureGenerator::createSign(CERTCertificate *cert)
{
    NSSCMSSignedData *sigd = NULL;
    NSSCMSContentInfo *cinfo = NULL;
    NSSCMSSignerInfo *signerinfo = NULL;
    
    SECOidTag hash=getDigestAlgor(cert);
    
    NSSCMSMessage *cmsg = NSS_CMSMessage_Create(NULL); // create a message on its own pool
    if (cmsg == NULL) return NULL;
    if ((sigd = NSS_CMSSignedData_Create(cmsg)) == NULL) {
        NSS_CMSMessage_Destroy(cmsg); return NULL;
    }
    cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
    if (NSS_CMSContentInfo_SetContent_SignedData(cmsg, cinfo, sigd) != SECSuccess) {
        NSS_CMSMessage_Destroy(cmsg); return NULL;
    }
    
    // if !detatched, the contentinfo will alloc a data item for us
    cinfo = NSS_CMSSignedData_GetContentInfo(sigd);
    if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_TRUE ) != SECSuccess) {
        NSS_CMSMessage_Destroy(cmsg); return NULL;
    }
    
    signerinfo = NSS_CMSSignerInfo_Create(cmsg, cert, hash);
    if (signerinfo == NULL) { NSS_CMSMessage_Destroy(cmsg); return NULL; }
    
    // we want the cert chain included for this one
    if (NSS_CMSSignerInfo_IncludeCerts(signerinfo, NSSCMSCM_CertChainWithRoot, certUsageEmailSigner) != SECSuccess) 
    { NSS_CMSMessage_Destroy(cmsg); return NULL; }
    
    // SMIME RFC says signing time should always be added
    if (NSS_CMSSignerInfo_AddSigningTime(signerinfo, PR_Now()) != SECSuccess) { NSS_CMSMessage_Destroy(cmsg); return NULL; }
    
    if (NSS_CMSSignedData_AddSignerInfo(sigd, signerinfo) != SECSuccess) {
        NSS_CMSMessage_Destroy(cmsg); return NULL; }
    return cmsg;
}


NSSSignatureGenerator::NSSSignatureGenerator(CERTCertificate *pCert) 
    :pCert(pCert)
{
    pSignature = NULL;
}

NSSSignatureGenerator::~NSSSignatureGenerator() 
{
    delete pSignature;
    if(enc!=NULL) NSS_CMSEncoder_Cancel(enc);
    if(cmsg!=NULL) NSS_CMSMessage_Destroy(cmsg);
}

bool NSSSignatureGenerator::init()
{
    cmsg = createSign(pCert);
    if(cmsg==NULL) return false;
    // Prepare encoder
    enc = NSS_CMSEncoder_Start(cmsg,
                               sm_write_stream, this, // DER output callback
                               NULL, NULL,     // destination storage
                               NULL, NULL,	   // password callback
                               NULL, NULL,     // decrypt key callback
                               NULL, NULL );   // detached digests
}

bool NSSSignatureGenerator::appendData(const char *pData, unsigned int dataSize)
{
    if(enc == NULL) return false;
    
    if (NSS_CMSEncoder_Update(enc, pData, dataSize) != SECSuccess) {
        NSS_CMSEncoder_Cancel(enc);
        enc = NULL;
        return false;
    }
    return true;
}

bool NSSSignatureGenerator::finishData() 
{
    if(enc == NULL) return false;
    
    if (NSS_CMSEncoder_Finish(enc) != SECSuccess) {
        enc = NULL;
        return false;
    }
    enc = NULL;
    return true;
}

const PoDoFo::PdfData* NSSSignatureGenerator::getSignature() 
{
    if(pSignature==NULL) {
        pSignature = new PoDoFo::PdfData(signature.c_str(), signature.size());
    }
    return pSignature;
}