1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.forgerock.json.jose.jws.handlers;
18
19 import java.nio.ByteBuffer;
20 import java.security.InvalidKeyException;
21 import java.security.NoSuchAlgorithmException;
22 import java.security.Signature;
23 import java.security.SignatureException;
24 import java.security.interfaces.ECKey;
25 import java.security.interfaces.ECPrivateKey;
26 import java.security.interfaces.ECPublicKey;
27 import java.util.Arrays;
28
29 import org.forgerock.json.jose.exceptions.JwsException;
30 import org.forgerock.json.jose.exceptions.JwsSigningException;
31 import org.forgerock.json.jose.jws.JwsAlgorithm;
32 import org.forgerock.json.jose.jws.JwsAlgorithmType;
33 import org.forgerock.json.jose.jws.SupportedEllipticCurve;
34 import org.forgerock.json.jose.utils.DerUtils;
35 import org.forgerock.json.jose.utils.Utils;
36 import org.forgerock.util.Reject;
37
38
39
40
41 public class ECDSASigningHandler implements SigningHandler {
42 private final ECPrivateKey signingKey;
43 private final ECPublicKey verificationKey;
44 private final SupportedEllipticCurve curve;
45
46
47
48
49
50
51 public ECDSASigningHandler(final ECPrivateKey signingKey) {
52 this.signingKey = signingKey;
53 this.verificationKey = null;
54 this.curve = validateKey(signingKey);
55 }
56
57
58
59
60
61
62 public ECDSASigningHandler(final ECPublicKey verificationKey) {
63 this.signingKey = null;
64 this.verificationKey = verificationKey;
65 this.curve = validateKey(verificationKey);
66 }
67
68 @Override
69 public byte[] sign(final JwsAlgorithm algorithm, final String data) {
70 return sign(algorithm, data.getBytes(Utils.CHARSET));
71 }
72
73 @Override
74 public byte[] sign(final JwsAlgorithm algorithm, final byte[] data) {
75 validateAlgorithm(algorithm);
76
77 try {
78 final Signature signature = Signature.getInstance(algorithm.getAlgorithm());
79 signature.initSign(signingKey);
80 signature.update(data);
81 return derDecode(signature.sign(), curve.getSignatureSize());
82 } catch (SignatureException | InvalidKeyException e) {
83 throw new JwsSigningException(e);
84 } catch (NoSuchAlgorithmException e) {
85 throw new JwsSigningException("Unsupported Signing Algorithm, " + algorithm.getAlgorithm(), e);
86 }
87 }
88
89 @Override
90 public boolean verify(final JwsAlgorithm algorithm, final byte[] data, final byte[] signature) {
91 validateAlgorithm(algorithm);
92
93 try {
94 final Signature validator = Signature.getInstance(algorithm.getAlgorithm());
95 validator.initVerify(verificationKey);
96 validator.update(data);
97 return validator.verify(derEncode(signature));
98 } catch (SignatureException | InvalidKeyException e) {
99 throw new JwsSigningException(e);
100 } catch (NoSuchAlgorithmException e) {
101 throw new JwsSigningException("Unsupported Signing Algorithm, " + algorithm.getAlgorithm(), e);
102 }
103 }
104
105 private void validateAlgorithm(JwsAlgorithm algorithm) {
106 Reject.ifNull(algorithm, "Algorithm must not be null.");
107 Reject.ifTrue(algorithm.getAlgorithmType() != JwsAlgorithmType.ECDSA, "Not an ECDSA algorithm.");
108 }
109
110
111
112
113
114 private SupportedEllipticCurve validateKey(final ECKey key) {
115 Reject.ifNull(key);
116 try {
117 return SupportedEllipticCurve.forKey(key);
118 } catch (IllegalArgumentException ex) {
119 throw new JwsException(ex);
120 }
121 }
122
123
124
125
126 private static byte[] derDecode(final byte[] signature, final int signatureSize) {
127 final ByteBuffer buffer = ByteBuffer.wrap(signature);
128 if (buffer.get() != DerUtils.SEQUENCE_TAG) {
129 throw new JwsSigningException("Unable to decode DER signature");
130 }
131
132 DerUtils.readLength(buffer);
133
134 final byte[] output = new byte[signatureSize];
135 final int componentSize = signatureSize >> 1;
136 DerUtils.readUnsignedInteger(buffer, output, 0, componentSize);
137 DerUtils.readUnsignedInteger(buffer, output, componentSize, componentSize);
138 return output;
139 }
140
141
142
143
144 private static byte[] derEncode(final byte[] signature) {
145 Reject.ifNull(signature);
146 SupportedEllipticCurve curve = SupportedEllipticCurve.forSignature(signature);
147
148 int midPoint = curve.getSignatureSize() >> 1;
149 final byte[] r = Arrays.copyOfRange(signature, 0, midPoint);
150 final byte[] s = Arrays.copyOfRange(signature, midPoint, signature.length);
151
152
153
154 final ByteBuffer params = ByteBuffer.allocate(signature.length + 6);
155 DerUtils.writeInteger(params, r);
156 DerUtils.writeInteger(params, s);
157
158 final int size = params.position();
159
160 final ByteBuffer sequence = ByteBuffer.allocate(size + 6);
161 sequence.put(DerUtils.SEQUENCE_TAG);
162 DerUtils.writeLength(sequence, size);
163 sequence.put((ByteBuffer) params.flip());
164 sequence.flip();
165 final byte[] encodedSignature = new byte[sequence.remaining()];
166 sequence.get(encodedSignature);
167 return encodedSignature;
168 }
169
170 }