• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.apksig.internal.pkcs7;
18 
19 import static com.android.apksig.Constants.OID_RSA_ENCRYPTION;
20 import static com.android.apksig.internal.asn1.Asn1DerEncoder.ASN1_DER_NULL;
21 import static com.android.apksig.internal.oid.OidConstants.OID_DIGEST_SHA1;
22 import static com.android.apksig.internal.oid.OidConstants.OID_DIGEST_SHA256;
23 import static com.android.apksig.internal.oid.OidConstants.OID_SIG_DSA;
24 import static com.android.apksig.internal.oid.OidConstants.OID_SIG_EC_PUBLIC_KEY;
25 import static com.android.apksig.internal.oid.OidConstants.OID_SIG_RSA;
26 import static com.android.apksig.internal.oid.OidConstants.OID_SIG_SHA256_WITH_DSA;
27 import static com.android.apksig.internal.oid.OidConstants.OID_TO_JCA_DIGEST_ALG;
28 import static com.android.apksig.internal.oid.OidConstants.OID_TO_JCA_SIGNATURE_ALG;
29 
30 import com.android.apksig.internal.apk.v1.DigestAlgorithm;
31 import com.android.apksig.internal.asn1.Asn1Class;
32 import com.android.apksig.internal.asn1.Asn1Field;
33 import com.android.apksig.internal.asn1.Asn1OpaqueObject;
34 import com.android.apksig.internal.asn1.Asn1Type;
35 import com.android.apksig.internal.util.Pair;
36 
37 import java.security.InvalidKeyException;
38 import java.security.PublicKey;
39 import java.security.Signature;
40 import java.security.SignatureException;
41 
42 /**
43  * PKCS #7 {@code AlgorithmIdentifier} as specified in RFC 5652.
44  */
45 @Asn1Class(type = Asn1Type.SEQUENCE)
46 public class AlgorithmIdentifier {
47 
48     @Asn1Field(index = 0, type = Asn1Type.OBJECT_IDENTIFIER)
49     public String algorithm;
50 
51     @Asn1Field(index = 1, type = Asn1Type.ANY, optional = true)
52     public Asn1OpaqueObject parameters;
53 
AlgorithmIdentifier()54     public AlgorithmIdentifier() {}
55 
AlgorithmIdentifier(String algorithmOid, Asn1OpaqueObject parameters)56     public AlgorithmIdentifier(String algorithmOid, Asn1OpaqueObject parameters) {
57         this.algorithm = algorithmOid;
58         this.parameters = parameters;
59     }
60 
61     /**
62      * Returns the PKCS #7 {@code DigestAlgorithm} to use when signing using the specified digest
63      * algorithm.
64      */
getSignerInfoDigestAlgorithmOid( DigestAlgorithm digestAlgorithm)65     public static AlgorithmIdentifier getSignerInfoDigestAlgorithmOid(
66             DigestAlgorithm digestAlgorithm) {
67         switch (digestAlgorithm) {
68             case SHA1:
69                 return new AlgorithmIdentifier(OID_DIGEST_SHA1, ASN1_DER_NULL);
70             case SHA256:
71                 return new AlgorithmIdentifier(OID_DIGEST_SHA256, ASN1_DER_NULL);
72         }
73         throw new IllegalArgumentException("Unsupported digest algorithm: " + digestAlgorithm);
74     }
75 
76     /**
77      * Returns the JCA {@link Signature} algorithm and PKCS #7 {@code SignatureAlgorithm} to use
78      * when signing with the specified key and digest algorithm.
79      */
getSignerInfoSignatureAlgorithm( PublicKey publicKey, DigestAlgorithm digestAlgorithm, boolean deterministicDsaSigning)80     public static Pair<String, AlgorithmIdentifier> getSignerInfoSignatureAlgorithm(
81             PublicKey publicKey, DigestAlgorithm digestAlgorithm, boolean deterministicDsaSigning)
82             throws InvalidKeyException {
83         String keyAlgorithm = publicKey.getAlgorithm();
84         String jcaDigestPrefixForSigAlg;
85         switch (digestAlgorithm) {
86             case SHA1:
87                 jcaDigestPrefixForSigAlg = "SHA1";
88                 break;
89             case SHA256:
90                 jcaDigestPrefixForSigAlg = "SHA256";
91                 break;
92             default:
93                 throw new IllegalArgumentException(
94                         "Unexpected digest algorithm: " + digestAlgorithm);
95         }
96         if ("RSA".equalsIgnoreCase(keyAlgorithm) || OID_RSA_ENCRYPTION.equals(keyAlgorithm)) {
97             return Pair.of(
98                     jcaDigestPrefixForSigAlg + "withRSA",
99                     new AlgorithmIdentifier(OID_SIG_RSA, ASN1_DER_NULL));
100         } else if ("DSA".equalsIgnoreCase(keyAlgorithm)) {
101             AlgorithmIdentifier sigAlgId;
102             switch (digestAlgorithm) {
103                 case SHA1:
104                     sigAlgId =
105                             new AlgorithmIdentifier(OID_SIG_DSA, ASN1_DER_NULL);
106                     break;
107                 case SHA256:
108                     // DSA signatures with SHA-256 in SignedData are accepted by Android API Level
109                     // 21 and higher. However, there are two ways to specify their SignedData
110                     // SignatureAlgorithm: dsaWithSha256 (2.16.840.1.101.3.4.3.2) and
111                     // dsa (1.2.840.10040.4.1). The latter works only on API Level 22+. Thus, we use
112                     // the former.
113                     sigAlgId =
114                             new AlgorithmIdentifier(OID_SIG_SHA256_WITH_DSA, ASN1_DER_NULL);
115                     break;
116                 default:
117                     throw new IllegalArgumentException(
118                             "Unexpected digest algorithm: " + digestAlgorithm);
119             }
120             String signingAlgorithmName =
121                     jcaDigestPrefixForSigAlg + (deterministicDsaSigning ? "withDetDSA" : "withDSA");
122             return Pair.of(signingAlgorithmName, sigAlgId);
123         } else if ("EC".equalsIgnoreCase(keyAlgorithm)) {
124             return Pair.of(
125                     jcaDigestPrefixForSigAlg + "withECDSA",
126                     new AlgorithmIdentifier(OID_SIG_EC_PUBLIC_KEY, ASN1_DER_NULL));
127         } else {
128             throw new InvalidKeyException("Unsupported key algorithm: " + keyAlgorithm);
129         }
130     }
131 
getJcaSignatureAlgorithm( String digestAlgorithmOid, String signatureAlgorithmOid)132     public static String getJcaSignatureAlgorithm(
133             String digestAlgorithmOid,
134             String signatureAlgorithmOid) throws SignatureException {
135         // First check whether the signature algorithm OID alone is sufficient
136         String result = OID_TO_JCA_SIGNATURE_ALG.get(signatureAlgorithmOid);
137         if (result != null) {
138             return result;
139         }
140 
141         // Signature algorithm OID alone is insufficient. Need to combine digest algorithm OID
142         // with signature algorithm OID.
143         String suffix;
144         if (OID_SIG_RSA.equals(signatureAlgorithmOid)) {
145             suffix = "RSA";
146         } else if (OID_SIG_DSA.equals(signatureAlgorithmOid)) {
147             suffix = "DSA";
148         } else if (OID_SIG_EC_PUBLIC_KEY.equals(signatureAlgorithmOid)) {
149             suffix = "ECDSA";
150         } else {
151             throw new SignatureException(
152                     "Unsupported JCA Signature algorithm"
153                             + " . Digest algorithm: " + digestAlgorithmOid
154                             + ", signature algorithm: " + signatureAlgorithmOid);
155         }
156         String jcaDigestAlg = getJcaDigestAlgorithm(digestAlgorithmOid);
157         // Canonical name for SHA-1 with ... is SHA1with, rather than SHA1. Same for all other
158         // SHA algorithms.
159         if (jcaDigestAlg.startsWith("SHA-")) {
160             jcaDigestAlg = "SHA" + jcaDigestAlg.substring("SHA-".length());
161         }
162         return jcaDigestAlg + "with" + suffix;
163     }
164 
getJcaDigestAlgorithm(String oid)165     public static String getJcaDigestAlgorithm(String oid)
166             throws SignatureException {
167         String result = OID_TO_JCA_DIGEST_ALG.get(oid);
168         if (result == null) {
169             throw new SignatureException("Unsupported digest algorithm: " + oid);
170         }
171         return result;
172     }
173 }
174