RSA_SHA1.java revision b852fcf48a8909164d7f323dd02a35d2a8056a61
1/*
2 * Copyright 2007 Google, Inc.
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
17package net.oauth.signature;
18
19import java.io.ByteArrayInputStream;
20import java.io.UnsupportedEncodingException;
21import java.security.GeneralSecurityException;
22import java.security.KeyFactory;
23import java.security.PrivateKey;
24import java.security.PublicKey;
25import java.security.Signature;
26import java.security.cert.CertificateFactory;
27import java.security.cert.X509Certificate;
28import java.security.spec.EncodedKeySpec;
29import java.security.spec.PKCS8EncodedKeySpec;
30import java.security.spec.X509EncodedKeySpec;
31
32import net.oauth.OAuth;
33import net.oauth.OAuthAccessor;
34import net.oauth.OAuthException;
35
36/**
37 * Class to handle RSA-SHA1 signatures on OAuth requests. A consumer
38 * that wishes to use public-key signatures on messages does not need
39 * a shared secret with the service provider, but it needs a private
40 * RSA signing key. You create it like this:
41 *
42 * OAuthConsumer c = new OAuthConsumer(callback_url, consumer_key,
43 *                                     null, provider);
44 * c.setProperty(RSA_SHA1.PRIVATE_KEY, consumer_privateRSAKey);
45 *
46 * consumer_privateRSAKey must be an RSA signing key and
47 * of type java.security.PrivateKey, String, or byte[]. In the latter two
48 * cases, the key must be PKCS#8-encoded (byte[]) or PKCS#8-encoded and
49 * then Base64-encoded (String).
50 *
51 * A service provider that wishes to verify signatures made by such a
52 * consumer does not need a shared secret with the consumer, but it needs
53 * to know the consumer's public key. You create the necessary
54 * OAuthConsumer object (on the service provider's side) like this:
55 *
56 * OAuthConsumer c = new OAuthConsumer(callback_url, consumer_key,
57 *                                     null, provider);
58 * c.setProperty(RSA_SHA1.PUBLIC_KEY, consumer_publicRSAKey);
59 *
60 * consumer_publicRSAKey must be the consumer's public RSAkey and
61 * of type java.security.PublicKey, String, or byte[]. In the latter two
62 * cases, the key must be X509-encoded (byte[]) or X509-encoded and
63 * then Base64-encoded (String).
64 *
65 * Alternatively, a service provider that wishes to verify signatures made
66 * by such a consumer can use a X509 certificate containing the consumer's
67 * public key. You create the necessary OAuthConsumer object (on the service
68 * provider's side) like this:
69 *
70 * OAuthConsumer c = new OAuthConsumer(callback_url, consumer_key,
71 *                                     null, provider);
72 * c.setProperty(RSA_SHA1.X509_CERTIFICATE, consumer_cert);
73 *
74 * consumer_cert must be a X509 Certificate containing the consumer's public
75 * key and be of type java.security.cert.X509Certificate, String,
76 * or byte[]. In the latter two cases, the certificate must be DER-encoded
77 * (byte[]) or PEM-encoded (String).
78 *
79 * @author Dirk Balfanz
80 * @hide
81 *
82 */
83public class RSA_SHA1 extends OAuthSignatureMethod {
84
85    final static public String PRIVATE_KEY = "RSA-SHA1.PrivateKey";
86    final static public String PUBLIC_KEY = "RSA-SHA1.PublicKey";
87    final static public String X509_CERTIFICATE = "RSA-SHA1.X509Certificate";
88
89    private PrivateKey privateKey = null;
90    private PublicKey publicKey = null;
91
92    @Override
93    protected void initialize(String name, OAuthAccessor accessor)
94    throws OAuthException {
95        super.initialize(name, accessor);
96
97        Object privateKeyObject = accessor.consumer.getProperty(PRIVATE_KEY);
98        try {
99            if (privateKeyObject != null) {
100                if (privateKeyObject instanceof PrivateKey) {
101                    privateKey = (PrivateKey)privateKeyObject;
102                } else if (privateKeyObject instanceof String) {
103                    privateKey = getPrivateKeyFromPem((String)privateKeyObject);
104                } else if (privateKeyObject instanceof byte[]) {
105                    privateKey = getPrivateKeyFromDer((byte[])privateKeyObject);
106                } else {
107                    throw new IllegalArgumentException(
108                            "Private key set through RSA_SHA1.PRIVATE_KEY must be of " +
109                            "type PrivateKey, String, or byte[], and not " +
110                            privateKeyObject.getClass().getName());
111                }
112            }
113
114            Object publicKeyObject = accessor.consumer.getProperty(PUBLIC_KEY);
115            if (publicKeyObject != null) {
116                if (publicKeyObject instanceof PublicKey) {
117                    publicKey = (PublicKey)publicKeyObject;
118                } else if (publicKeyObject instanceof String) {
119                    publicKey = getPublicKeyFromPem((String)publicKeyObject);
120                } else if (publicKeyObject instanceof byte[]) {
121                    publicKey = getPublicKeyFromDer((byte[])publicKeyObject);
122                } else {
123                    throw new IllegalArgumentException(
124                            "Public key set through RSA_SHA1.PRIVATE_KEY must be of " +
125                            "type PublicKey, String, or byte[], and not " +
126                            publicKeyObject.getClass().getName());
127                }
128            } else {  // public key was null. perhaps they gave us a X509 cert.
129                Object certObject = accessor.consumer.getProperty(X509_CERTIFICATE);
130                if (certObject != null) {
131                    if (certObject instanceof X509Certificate) {
132                        publicKey = ((X509Certificate) certObject).getPublicKey();
133                    } else if (certObject instanceof String) {
134                        publicKey = getPublicKeyFromPemCert((String)certObject);
135                    } else if (certObject instanceof byte[]) {
136                        publicKey = getPublicKeyFromDerCert((byte[])certObject);
137                    } else {
138                        throw new IllegalArgumentException(
139                                "X509Certificate set through RSA_SHA1.X509_CERTIFICATE" +
140                                " must be of type X509Certificate, String, or byte[]," +
141                                " and not " + certObject.getClass().getName());
142                    }
143                }
144            }
145        } catch (GeneralSecurityException e) {
146            throw new OAuthException(e);
147        }
148    }
149
150    private PublicKey getPublicKeyFromPemCert(String certObject)
151            throws GeneralSecurityException {
152        CertificateFactory fac = CertificateFactory.getInstance("X509");
153        ByteArrayInputStream in = new ByteArrayInputStream(certObject.getBytes());
154        X509Certificate cert = (X509Certificate)fac.generateCertificate(in);
155        return cert.getPublicKey();
156    }
157
158    private PublicKey getPublicKeyFromDerCert(byte[] certObject)
159            throws GeneralSecurityException {
160        CertificateFactory fac = CertificateFactory.getInstance("X509");
161        ByteArrayInputStream in = new ByteArrayInputStream(certObject);
162        X509Certificate cert = (X509Certificate)fac.generateCertificate(in);
163        return cert.getPublicKey();
164    }
165
166    private PublicKey getPublicKeyFromDer(byte[] publicKeyObject)
167            throws GeneralSecurityException {
168        KeyFactory fac = KeyFactory.getInstance("RSA");
169        EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(publicKeyObject);
170        return fac.generatePublic(pubKeySpec);
171    }
172
173    private PublicKey getPublicKeyFromPem(String publicKeyObject)
174            throws GeneralSecurityException {
175        return getPublicKeyFromDer(decodeBase64(publicKeyObject));
176    }
177
178    private PrivateKey getPrivateKeyFromDer(byte[] privateKeyObject)
179            throws GeneralSecurityException {
180        KeyFactory fac = KeyFactory.getInstance("RSA");
181        EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(privateKeyObject);
182        return fac.generatePrivate(privKeySpec);
183    }
184
185    private PrivateKey getPrivateKeyFromPem(String privateKeyObject)
186            throws GeneralSecurityException {
187        return getPrivateKeyFromDer(decodeBase64(privateKeyObject));
188    }
189
190    @Override
191    protected String getSignature(String baseString) throws OAuthException {
192        try {
193            byte[] signature = sign(baseString.getBytes(OAuth.ENCODING));
194            return base64Encode(signature);
195        } catch (UnsupportedEncodingException e) {
196            throw new OAuthException(e);
197        } catch (GeneralSecurityException e) {
198            throw new OAuthException(e);
199        }
200    }
201
202    @Override
203    protected boolean isValid(String signature, String baseString)
204            throws OAuthException {
205        try {
206            return verify(decodeBase64(signature),
207                          baseString.getBytes(OAuth.ENCODING));
208        } catch (UnsupportedEncodingException e) {
209            throw new OAuthException(e);
210        } catch (GeneralSecurityException e) {
211            throw new OAuthException(e);
212        }
213    }
214
215    private byte[] sign(byte[] message) throws GeneralSecurityException {
216        if (privateKey == null) {
217            throw new IllegalStateException("need to set private key with " +
218                                            "OAuthConsumer.setProperty when " +
219                                            "generating RSA-SHA1 signatures.");
220        }
221        Signature signer = Signature.getInstance("SHA1withRSA");
222        signer.initSign(privateKey);
223        signer.update(message);
224        return signer.sign();
225    }
226
227    private boolean verify(byte[] signature, byte[] message)
228            throws GeneralSecurityException {
229        if (publicKey == null) {
230            throw new IllegalStateException("need to set public key with " +
231                                            " OAuthConsumer.setProperty when " +
232                                            "verifying RSA-SHA1 signatures.");
233        }
234        Signature verifier = Signature.getInstance("SHA1withRSA");
235        verifier.initVerify(publicKey);
236        verifier.update(message);
237        return verifier.verify(signature);
238    }
239}
240