1package org.bouncycastle.operator.jcajce;
2
3import java.io.ByteArrayInputStream;
4import java.io.IOException;
5import java.security.AlgorithmParameters;
6import java.security.GeneralSecurityException;
7import java.security.MessageDigest;
8import java.security.NoSuchAlgorithmException;
9import java.security.NoSuchProviderException;
10import java.security.Signature;
11import java.security.cert.CertificateException;
12import java.security.cert.CertificateFactory;
13import java.security.cert.X509Certificate;
14import java.security.spec.PSSParameterSpec;
15import java.util.HashMap;
16import java.util.Map;
17
18import javax.crypto.Cipher;
19
20import org.bouncycastle.asn1.ASN1Encodable;
21import org.bouncycastle.asn1.ASN1ObjectIdentifier;
22import org.bouncycastle.asn1.DERNull;
23// BEGIN android-removed
24// import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
25// END android-removed
26import org.bouncycastle.asn1.kisa.KISAObjectIdentifiers;
27import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
28import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
29import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
30import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
31import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
32import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
33import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
34import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
35import org.bouncycastle.cert.X509CertificateHolder;
36import org.bouncycastle.jcajce.JcaJceHelper;
37import org.bouncycastle.operator.OperatorCreationException;
38
39class OperatorHelper
40{
41    private static final Map oids = new HashMap();
42    private static final Map asymmetricWrapperAlgNames = new HashMap();
43    private static final Map symmetricWrapperAlgNames = new HashMap();
44    private static final Map symmetricKeyAlgNames = new HashMap();
45
46    static
47    {
48        //
49        // reverse mappings
50        //
51        oids.put(new ASN1ObjectIdentifier("1.2.840.113549.1.1.5"), "SHA1WITHRSA");
52        // BEGIN android-removed
53        // oids.put(PKCSObjectIdentifiers.sha224WithRSAEncryption, "SHA224WITHRSA");
54        // END android-removed
55        oids.put(PKCSObjectIdentifiers.sha256WithRSAEncryption, "SHA256WITHRSA");
56        oids.put(PKCSObjectIdentifiers.sha384WithRSAEncryption, "SHA384WITHRSA");
57        oids.put(PKCSObjectIdentifiers.sha512WithRSAEncryption, "SHA512WITHRSA");
58        // BEGIN android-removed
59        // oids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, "GOST3411WITHGOST3410");
60        // oids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, "GOST3411WITHECGOST3410");
61        // END android-removed
62
63        oids.put(new ASN1ObjectIdentifier("1.2.840.113549.1.1.4"), "MD5WITHRSA");
64        // BEGIN android-removed
65        // oids.put(new ASN1ObjectIdentifier("1.2.840.113549.1.1.2"), "MD2WITHRSA");
66        // END android-removed
67        oids.put(new ASN1ObjectIdentifier("1.2.840.10040.4.3"), "SHA1WITHDSA");
68        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA1, "SHA1WITHECDSA");
69        // BEGIN android-removed
70        // oids.put(X9ObjectIdentifiers.ecdsa_with_SHA224, "SHA224WITHECDSA");
71        // END android-removed
72        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA256, "SHA256WITHECDSA");
73        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA384, "SHA384WITHECDSA");
74        oids.put(X9ObjectIdentifiers.ecdsa_with_SHA512, "SHA512WITHECDSA");
75        oids.put(OIWObjectIdentifiers.sha1WithRSA, "SHA1WITHRSA");
76        oids.put(OIWObjectIdentifiers.dsaWithSHA1, "SHA1WITHDSA");
77        // BEGIN android-removed
78        // oids.put(NISTObjectIdentifiers.dsa_with_sha224, "SHA224WITHDSA");
79        // END android-removed
80        oids.put(NISTObjectIdentifiers.dsa_with_sha256, "SHA256WITHDSA");
81
82        asymmetricWrapperAlgNames.put(PKCSObjectIdentifiers.rsaEncryption, "RSA/ECB/PKCS1Padding");
83
84        symmetricWrapperAlgNames.put(PKCSObjectIdentifiers.id_alg_CMS3DESwrap, "DESEDEWrap");
85        symmetricWrapperAlgNames.put(PKCSObjectIdentifiers.id_alg_CMSRC2wrap, "RC2Wrap");
86        symmetricWrapperAlgNames.put(NISTObjectIdentifiers.id_aes128_wrap, "AESWrap");
87        symmetricWrapperAlgNames.put(NISTObjectIdentifiers.id_aes192_wrap, "AESWrap");
88        symmetricWrapperAlgNames.put(NISTObjectIdentifiers.id_aes256_wrap, "AESWrap");
89        symmetricWrapperAlgNames.put(NTTObjectIdentifiers.id_camellia128_wrap, "CamelliaWrap");
90        symmetricWrapperAlgNames.put(NTTObjectIdentifiers.id_camellia192_wrap, "CamelliaWrap");
91        symmetricWrapperAlgNames.put(NTTObjectIdentifiers.id_camellia256_wrap, "CamelliaWrap");
92        symmetricWrapperAlgNames.put(KISAObjectIdentifiers.id_npki_app_cmsSeed_wrap, "SEEDWrap");
93        symmetricWrapperAlgNames.put(PKCSObjectIdentifiers.des_EDE3_CBC, "DESede");
94
95        symmetricKeyAlgNames.put(NISTObjectIdentifiers.aes, "AES");
96        symmetricKeyAlgNames.put(NISTObjectIdentifiers.id_aes128_CBC, "AES");
97        symmetricKeyAlgNames.put(NISTObjectIdentifiers.id_aes192_CBC, "AES");
98        symmetricKeyAlgNames.put(NISTObjectIdentifiers.id_aes256_CBC, "AES");
99        symmetricKeyAlgNames.put(PKCSObjectIdentifiers.des_EDE3_CBC, "DESede");
100        symmetricKeyAlgNames.put(PKCSObjectIdentifiers.RC2_CBC, "RC2");
101    }
102
103    private JcaJceHelper helper;
104
105    OperatorHelper(JcaJceHelper helper)
106    {
107        this.helper = helper;
108    }
109
110    Cipher createAsymmetricWrapper(ASN1ObjectIdentifier algorithm, Map extraAlgNames)
111        throws OperatorCreationException
112    {
113        try
114        {
115            String cipherName = null;
116
117            if (!extraAlgNames.isEmpty())
118            {
119                cipherName = (String)extraAlgNames.get(algorithm);
120            }
121
122            if (cipherName == null)
123            {
124                cipherName = (String)asymmetricWrapperAlgNames.get(algorithm);
125            }
126
127            if (cipherName != null)
128            {
129                try
130                {
131                    // this is reversed as the Sun policy files now allow unlimited strength RSA
132                    return helper.createCipher(cipherName);
133                }
134                catch (NoSuchAlgorithmException e)
135                {
136                    // try alternate for RSA
137                    if (cipherName.equals("RSA/ECB/PKCS1Padding"))
138                    {
139                        try
140                        {
141                            return helper.createCipher("RSA/NONE/PKCS1Padding");
142                        }
143                        catch (NoSuchAlgorithmException ex)
144                        {
145                            // Ignore
146                        }
147                    }
148                    // Ignore
149                }
150            }
151
152            return helper.createCipher(algorithm.getId());
153        }
154        catch (GeneralSecurityException e)
155        {
156            throw new OperatorCreationException("cannot create cipher: " + e.getMessage(), e);
157        }
158    }
159
160    Cipher createSymmetricWrapper(ASN1ObjectIdentifier algorithm)
161        throws OperatorCreationException
162    {
163        try
164        {
165            String cipherName = (String)symmetricWrapperAlgNames.get(algorithm);
166
167            if (cipherName != null)
168            {
169                try
170                {
171                    // this is reversed as the Sun policy files now allow unlimited strength RSA
172                    return helper.createCipher(cipherName);
173                }
174                catch (NoSuchAlgorithmException e)
175                {
176                    // Ignore
177                }
178            }
179            return helper.createCipher(algorithm.getId());
180        }
181        catch (GeneralSecurityException e)
182        {
183            throw new OperatorCreationException("cannot create cipher: " + e.getMessage(), e);
184        }
185    }
186
187    MessageDigest createDigest(AlgorithmIdentifier digAlgId)
188        throws GeneralSecurityException
189    {
190        MessageDigest dig;
191
192        try
193        {
194            dig = helper.createDigest(getDigestAlgName(digAlgId.getAlgorithm()));
195        }
196        catch (NoSuchAlgorithmException e)
197        {
198            //
199            // try an alternate
200            //
201            if (oids.get(digAlgId.getAlgorithm()) != null)
202            {
203                String  digestAlgorithm = (String)oids.get(digAlgId.getAlgorithm());
204
205                dig = helper.createDigest(digestAlgorithm);
206            }
207            else
208            {
209                throw e;
210            }
211        }
212
213        return dig;
214    }
215
216    Signature createSignature(AlgorithmIdentifier sigAlgId)
217        throws GeneralSecurityException
218    {
219        Signature   sig;
220
221        try
222        {
223            sig = helper.createSignature(getSignatureName(sigAlgId));
224        }
225        catch (NoSuchAlgorithmException e)
226        {
227            //
228            // try an alternate
229            //
230            if (oids.get(sigAlgId.getAlgorithm()) != null)
231            {
232                String  signatureAlgorithm = (String)oids.get(sigAlgId.getAlgorithm());
233
234                sig = helper.createSignature(signatureAlgorithm);
235            }
236            else
237            {
238                throw e;
239            }
240        }
241
242        return sig;
243    }
244
245    public Signature createRawSignature(AlgorithmIdentifier algorithm)
246    {
247        Signature   sig;
248
249        try
250        {
251            String algName = getSignatureName(algorithm);
252
253            algName = "NONE" + algName.substring(algName.indexOf("WITH"));
254
255            sig = helper.createSignature(algName);
256
257            // RFC 4056
258            // When the id-RSASSA-PSS algorithm identifier is used for a signature,
259            // the AlgorithmIdentifier parameters field MUST contain RSASSA-PSS-params.
260            if (algorithm.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
261            {
262                AlgorithmParameters params = helper.createAlgorithmParameters(algName);
263
264                params.init(algorithm.getParameters().toASN1Primitive().getEncoded(), "ASN.1");
265
266                PSSParameterSpec spec = (PSSParameterSpec)params.getParameterSpec(PSSParameterSpec.class);
267                sig.setParameter(spec);
268            }
269        }
270        catch (Exception e)
271        {
272            return null;
273        }
274
275        return sig;
276    }
277
278    private static String getSignatureName(
279        AlgorithmIdentifier sigAlgId)
280    {
281        ASN1Encodable params = sigAlgId.getParameters();
282
283        if (params != null && !DERNull.INSTANCE.equals(params))
284        {
285            if (sigAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
286            {
287                RSASSAPSSparams rsaParams = RSASSAPSSparams.getInstance(params);
288                return getDigestAlgName(rsaParams.getHashAlgorithm().getAlgorithm()) + "WITHRSAANDMGF1";
289            }
290        }
291
292        if (oids.containsKey(sigAlgId.getAlgorithm()))
293        {
294            return (String)oids.get(sigAlgId.getAlgorithm());
295        }
296
297        return sigAlgId.getAlgorithm().getId();
298    }
299
300    private static String getDigestAlgName(
301        ASN1ObjectIdentifier digestAlgOID)
302    {
303        if (PKCSObjectIdentifiers.md5.equals(digestAlgOID))
304        {
305            return "MD5";
306        }
307        else if (OIWObjectIdentifiers.idSHA1.equals(digestAlgOID))
308        {
309            return "SHA1";
310        }
311        // BEGIN android-removed
312        // else if (NISTObjectIdentifiers.id_sha224.equals(digestAlgOID))
313        // {
314        //     return "SHA224";
315        // }
316        // END android-removed
317        else if (NISTObjectIdentifiers.id_sha256.equals(digestAlgOID))
318        {
319            return "SHA256";
320        }
321        else if (NISTObjectIdentifiers.id_sha384.equals(digestAlgOID))
322        {
323            return "SHA384";
324        }
325        else if (NISTObjectIdentifiers.id_sha512.equals(digestAlgOID))
326        {
327            return "SHA512";
328        }
329        // BEGIN android-removed
330        // else if (TeleTrusTObjectIdentifiers.ripemd128.equals(digestAlgOID))
331        // {
332        //     return "RIPEMD128";
333        // }
334        // else if (TeleTrusTObjectIdentifiers.ripemd160.equals(digestAlgOID))
335        // {
336        //     return "RIPEMD160";
337        // }
338        // else if (TeleTrusTObjectIdentifiers.ripemd256.equals(digestAlgOID))
339        // {
340        //     return "RIPEMD256";
341        // }
342        // else if (CryptoProObjectIdentifiers.gostR3411.equals(digestAlgOID))
343        // {
344        //     return "GOST3411";
345        // }
346        // END android-removed
347        else
348        {
349            return digestAlgOID.getId();
350        }
351    }
352
353    public X509Certificate convertCertificate(X509CertificateHolder certHolder)
354        throws CertificateException
355    {
356
357        try
358        {
359            CertificateFactory certFact = helper.createCertificateFactory("X.509");
360
361            return (X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(certHolder.getEncoded()));
362        }
363        catch (IOException e)
364        {
365            throw new OpCertificateException("cannot get encoded form of certificate: " + e.getMessage(), e);
366        }
367        catch (NoSuchAlgorithmException e)
368        {
369            throw new OpCertificateException("cannot create certificate factory: " + e.getMessage(), e);
370        }
371        catch (NoSuchProviderException e)
372        {
373            throw new OpCertificateException("cannot find factory provider: " + e.getMessage(), e);
374        }
375    }
376
377    // TODO: put somewhere public so cause easily accessed
378    private static class OpCertificateException
379        extends CertificateException
380    {
381        private Throwable cause;
382
383        public OpCertificateException(String msg, Throwable cause)
384        {
385            super(msg);
386
387            this.cause = cause;
388        }
389
390        public Throwable getCause()
391        {
392            return cause;
393        }
394    }
395
396    String getKeyAlgorithmName(ASN1ObjectIdentifier oid)
397    {
398
399        String name = (String)symmetricKeyAlgNames.get(oid);
400
401        if (name != null)
402        {
403            return name;
404        }
405
406        return oid.getId();
407    }
408}
409