1package org.bouncycastle.jce.provider;
2
3import java.io.IOException;
4import java.io.ObjectInputStream;
5import java.io.ObjectOutputStream;
6import java.math.BigInteger;
7
8import javax.crypto.interfaces.DHPublicKey;
9import javax.crypto.spec.DHParameterSpec;
10import javax.crypto.spec.DHPublicKeySpec;
11
12import org.bouncycastle.asn1.ASN1Integer;
13import org.bouncycastle.asn1.ASN1ObjectIdentifier;
14import org.bouncycastle.asn1.ASN1Sequence;
15import org.bouncycastle.asn1.pkcs.DHParameter;
16import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
17import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
18import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
19import org.bouncycastle.asn1.x9.DHDomainParameters;
20import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
21import org.bouncycastle.crypto.params.DHPublicKeyParameters;
22import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
23
24public class JCEDHPublicKey
25    implements DHPublicKey
26{
27    static final long serialVersionUID = -216691575254424324L;
28
29    private BigInteger              y;
30    private DHParameterSpec         dhSpec;
31    private SubjectPublicKeyInfo    info;
32
33    JCEDHPublicKey(
34        DHPublicKeySpec    spec)
35    {
36        this.y = spec.getY();
37        this.dhSpec = new DHParameterSpec(spec.getP(), spec.getG());
38    }
39
40    JCEDHPublicKey(
41        DHPublicKey    key)
42    {
43        this.y = key.getY();
44        this.dhSpec = key.getParams();
45    }
46
47    JCEDHPublicKey(
48        DHPublicKeyParameters  params)
49    {
50        this.y = params.getY();
51        this.dhSpec = new DHParameterSpec(params.getParameters().getP(), params.getParameters().getG(), params.getParameters().getL());
52    }
53
54    JCEDHPublicKey(
55        BigInteger        y,
56        DHParameterSpec   dhSpec)
57    {
58        this.y = y;
59        this.dhSpec = dhSpec;
60    }
61
62    JCEDHPublicKey(
63        SubjectPublicKeyInfo    info)
64    {
65        this.info = info;
66
67        ASN1Integer              derY;
68        try
69        {
70            derY = (ASN1Integer)info.parsePublicKey();
71        }
72        catch (IOException e)
73        {
74            throw new IllegalArgumentException("invalid info structure in DH public key");
75        }
76
77        this.y = derY.getValue();
78
79        ASN1Sequence seq = ASN1Sequence.getInstance(info.getAlgorithmId().getParameters());
80        ASN1ObjectIdentifier id = info.getAlgorithmId().getAlgorithm();
81
82        // we need the PKCS check to handle older keys marked with the X9 oid.
83        if (id.equals(PKCSObjectIdentifiers.dhKeyAgreement) || isPKCSParam(seq))
84        {
85            DHParameter             params = DHParameter.getInstance(seq);
86
87            if (params.getL() != null)
88            {
89                this.dhSpec = new DHParameterSpec(params.getP(), params.getG(), params.getL().intValue());
90            }
91            else
92            {
93                this.dhSpec = new DHParameterSpec(params.getP(), params.getG());
94            }
95        }
96        else if (id.equals(X9ObjectIdentifiers.dhpublicnumber))
97        {
98            DHDomainParameters params = DHDomainParameters.getInstance(seq);
99
100            this.dhSpec = new DHParameterSpec(params.getP().getValue(), params.getG().getValue());
101        }
102        else
103        {
104            throw new IllegalArgumentException("unknown algorithm type: " + id);
105        }
106    }
107
108    public String getAlgorithm()
109    {
110        return "DH";
111    }
112
113    public String getFormat()
114    {
115        return "X.509";
116    }
117
118    public byte[] getEncoded()
119    {
120        if (info != null)
121        {
122            return KeyUtil.getEncodedSubjectPublicKeyInfo(info);
123        }
124
125        return KeyUtil.getEncodedSubjectPublicKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.dhKeyAgreement, new DHParameter(dhSpec.getP(), dhSpec.getG(), dhSpec.getL())), new ASN1Integer(y));
126    }
127
128    public DHParameterSpec getParams()
129    {
130        return dhSpec;
131    }
132
133    public BigInteger getY()
134    {
135        return y;
136    }
137
138    private boolean isPKCSParam(ASN1Sequence seq)
139    {
140        if (seq.size() == 2)
141        {
142            return true;
143        }
144
145        if (seq.size() > 3)
146        {
147            return false;
148        }
149
150        ASN1Integer l = ASN1Integer.getInstance(seq.getObjectAt(2));
151        ASN1Integer p = ASN1Integer.getInstance(seq.getObjectAt(0));
152
153        if (l.getValue().compareTo(BigInteger.valueOf(p.getValue().bitLength())) > 0)
154        {
155            return false;
156        }
157
158        return true;
159    }
160
161    private void readObject(
162        ObjectInputStream   in)
163        throws IOException, ClassNotFoundException
164    {
165        this.y = (BigInteger)in.readObject();
166        this.dhSpec = new DHParameterSpec((BigInteger)in.readObject(), (BigInteger)in.readObject(), in.readInt());
167    }
168
169    private void writeObject(
170        ObjectOutputStream  out)
171        throws IOException
172    {
173        out.writeObject(this.getY());
174        out.writeObject(dhSpec.getP());
175        out.writeObject(dhSpec.getG());
176        out.writeInt(dhSpec.getL());
177    }
178}
179