mobile/android/base/browserid/RSACryptoImplementation.java

branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
equal deleted inserted replaced
-1:000000000000 0:b5995b0fc493
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 package org.mozilla.gecko.browserid;
6
7 import java.math.BigInteger;
8 import java.security.GeneralSecurityException;
9 import java.security.KeyFactory;
10 import java.security.KeyPair;
11 import java.security.KeyPairGenerator;
12 import java.security.NoSuchAlgorithmException;
13 import java.security.Signature;
14 import java.security.interfaces.RSAPrivateKey;
15 import java.security.interfaces.RSAPublicKey;
16 import java.security.spec.InvalidKeySpecException;
17 import java.security.spec.KeySpec;
18 import java.security.spec.RSAPrivateKeySpec;
19 import java.security.spec.RSAPublicKeySpec;
20
21 import org.mozilla.gecko.sync.ExtendedJSONObject;
22 import org.mozilla.gecko.sync.NonObjectJSONException;
23
24 public class RSACryptoImplementation {
25 public static final String SIGNATURE_ALGORITHM = "SHA256withRSA";
26
27 /**
28 * Parameters are serialized as decimal strings. Hex-versus-decimal was
29 * reverse-engineered from what the Persona public verifier accepted. We
30 * expect to follow the JOSE/JWT spec as it solidifies, and that will probably
31 * mean unifying this base.
32 */
33 protected static final int SERIALIZATION_BASE = 10;
34
35 protected static class RSAVerifyingPublicKey implements VerifyingPublicKey {
36 protected final RSAPublicKey publicKey;
37
38 public RSAVerifyingPublicKey(RSAPublicKey publicKey) {
39 this.publicKey = publicKey;
40 }
41
42 /**
43 * Serialize to a JSON object.
44 * <p>
45 * Parameters are serialized as decimal strings. Hex-versus-decimal was
46 * reverse-engineered from what the Persona public verifier accepted.
47 */
48 @Override
49 public ExtendedJSONObject toJSONObject() {
50 ExtendedJSONObject o = new ExtendedJSONObject();
51 o.put("algorithm", "RS");
52 o.put("n", publicKey.getModulus().toString(SERIALIZATION_BASE));
53 o.put("e", publicKey.getPublicExponent().toString(SERIALIZATION_BASE));
54 return o;
55 }
56
57 @Override
58 public boolean verifyMessage(byte[] bytes, byte[] signature)
59 throws GeneralSecurityException {
60 final Signature signer = Signature.getInstance(SIGNATURE_ALGORITHM);
61 signer.initVerify(publicKey);
62 signer.update(bytes);
63 return signer.verify(signature);
64 }
65 }
66
67 protected static class RSASigningPrivateKey implements SigningPrivateKey {
68 protected final RSAPrivateKey privateKey;
69
70 public RSASigningPrivateKey(RSAPrivateKey privateKey) {
71 this.privateKey = privateKey;
72 }
73
74 @Override
75 public String getAlgorithm() {
76 return "RS" + (privateKey.getModulus().bitLength() + 7)/8;
77 }
78
79 /**
80 * Serialize to a JSON object.
81 * <p>
82 * Parameters are serialized as decimal strings. Hex-versus-decimal was
83 * reverse-engineered from what the Persona public verifier accepted.
84 */
85 @Override
86 public ExtendedJSONObject toJSONObject() {
87 ExtendedJSONObject o = new ExtendedJSONObject();
88 o.put("algorithm", "RS");
89 o.put("n", privateKey.getModulus().toString(SERIALIZATION_BASE));
90 o.put("d", privateKey.getPrivateExponent().toString(SERIALIZATION_BASE));
91 return o;
92 }
93
94 @Override
95 public byte[] signMessage(byte[] bytes)
96 throws GeneralSecurityException {
97 final Signature signer = Signature.getInstance(SIGNATURE_ALGORITHM);
98 signer.initSign(privateKey);
99 signer.update(bytes);
100 return signer.sign();
101 }
102 }
103
104 public static BrowserIDKeyPair generateKeyPair(final int keysize) throws NoSuchAlgorithmException {
105 final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
106 keyPairGenerator.initialize(keysize);
107 final KeyPair keyPair = keyPairGenerator.generateKeyPair();
108 RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
109 RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
110 return new BrowserIDKeyPair(new RSASigningPrivateKey(privateKey), new RSAVerifyingPublicKey(publicKey));
111 }
112
113 public static SigningPrivateKey createPrivateKey(BigInteger n, BigInteger d) throws NoSuchAlgorithmException, InvalidKeySpecException {
114 if (n == null) {
115 throw new IllegalArgumentException("n must not be null");
116 }
117 if (d == null) {
118 throw new IllegalArgumentException("d must not be null");
119 }
120 KeyFactory keyFactory = KeyFactory.getInstance("RSA");
121 KeySpec keySpec = new RSAPrivateKeySpec(n, d);
122 RSAPrivateKey privateKey = (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
123 return new RSASigningPrivateKey(privateKey);
124 }
125
126 public static VerifyingPublicKey createPublicKey(BigInteger n, BigInteger e) throws NoSuchAlgorithmException, InvalidKeySpecException {
127 if (n == null) {
128 throw new IllegalArgumentException("n must not be null");
129 }
130 if (e == null) {
131 throw new IllegalArgumentException("e must not be null");
132 }
133 KeyFactory keyFactory = KeyFactory.getInstance("RSA");
134 KeySpec keySpec = new RSAPublicKeySpec(n, e);
135 RSAPublicKey publicKey = (RSAPublicKey) keyFactory.generatePublic(keySpec);
136 return new RSAVerifyingPublicKey(publicKey);
137 }
138
139 public static SigningPrivateKey createPrivateKey(ExtendedJSONObject o) throws InvalidKeySpecException, NoSuchAlgorithmException {
140 String algorithm = o.getString("algorithm");
141 if (!"RS".equals(algorithm)) {
142 throw new InvalidKeySpecException("algorithm must equal RS, was " + algorithm);
143 }
144 try {
145 BigInteger n = new BigInteger(o.getString("n"), SERIALIZATION_BASE);
146 BigInteger d = new BigInteger(o.getString("d"), SERIALIZATION_BASE);
147 return createPrivateKey(n, d);
148 } catch (NullPointerException e) {
149 throw new InvalidKeySpecException("n and d must be integers encoded as strings, base " + SERIALIZATION_BASE);
150 } catch (NumberFormatException e) {
151 throw new InvalidKeySpecException("n and d must be integers encoded as strings, base " + SERIALIZATION_BASE);
152 }
153 }
154
155 public static VerifyingPublicKey createPublicKey(ExtendedJSONObject o) throws InvalidKeySpecException, NoSuchAlgorithmException {
156 String algorithm = o.getString("algorithm");
157 if (!"RS".equals(algorithm)) {
158 throw new InvalidKeySpecException("algorithm must equal RS, was " + algorithm);
159 }
160 try {
161 BigInteger n = new BigInteger(o.getString("n"), SERIALIZATION_BASE);
162 BigInteger e = new BigInteger(o.getString("e"), SERIALIZATION_BASE);
163 return createPublicKey(n, e);
164 } catch (NullPointerException e) {
165 throw new InvalidKeySpecException("n and e must be integers encoded as strings, base " + SERIALIZATION_BASE);
166 } catch (NumberFormatException e) {
167 throw new InvalidKeySpecException("n and e must be integers encoded as strings, base " + SERIALIZATION_BASE);
168 }
169 }
170
171 public static BrowserIDKeyPair fromJSONObject(ExtendedJSONObject o) throws InvalidKeySpecException, NoSuchAlgorithmException {
172 try {
173 ExtendedJSONObject privateKey = o.getObject(BrowserIDKeyPair.JSON_KEY_PRIVATEKEY);
174 ExtendedJSONObject publicKey = o.getObject(BrowserIDKeyPair.JSON_KEY_PUBLICKEY);
175 if (privateKey == null) {
176 throw new InvalidKeySpecException("privateKey must not be null");
177 }
178 if (publicKey == null) {
179 throw new InvalidKeySpecException("publicKey must not be null");
180 }
181 return new BrowserIDKeyPair(createPrivateKey(privateKey), createPublicKey(publicKey));
182 } catch (NonObjectJSONException e) {
183 throw new InvalidKeySpecException("privateKey and publicKey must be JSON objects");
184 }
185 }
186 }

mercurial