From d2ad69103045a942cb012746205cfed9cfd32719 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Tue, 26 Apr 2011 18:04:07 +0200 Subject: Corrected documentation error and added examples to Users Guide --- lib/public_key/doc/src/Makefile | 3 +- lib/public_key/doc/src/part.xml | 3 +- lib/public_key/doc/src/public_key.xml | 24 +- lib/public_key/doc/src/using_public_key.xml | 504 ++++++++++++++++++++++++++++ 4 files changed, 520 insertions(+), 14 deletions(-) create mode 100644 lib/public_key/doc/src/using_public_key.xml diff --git a/lib/public_key/doc/src/Makefile b/lib/public_key/doc/src/Makefile index 298c28a740..afb17399da 100644 --- a/lib/public_key/doc/src/Makefile +++ b/lib/public_key/doc/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2008-2010. All Rights Reserved. +# Copyright Ericsson AB 2008-2011. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -52,6 +52,7 @@ XML_CHAPTER_FILES = \ introduction.xml \ public_key_records.xml \ cert_records.xml \ + using_public_key.xml \ notes.xml BOOK_FILES = book.xml diff --git a/lib/public_key/doc/src/part.xml b/lib/public_key/doc/src/part.xml index c338a71613..ea3123b5bd 100644 --- a/lib/public_key/doc/src/part.xml +++ b/lib/public_key/doc/src/part.xml @@ -1,4 +1,4 @@ - + @@ -38,5 +38,6 @@ + diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml index c5f57214b1..d60d91cd83 100644 --- a/lib/public_key/doc/src/public_key.xml +++ b/lib/public_key/doc/src/public_key.xml @@ -43,7 +43,7 @@

All records used in this manual - are generated from asn1 specifications + are generated from ASN.1 specifications and are documented in the User's Guide. See Public key records and X.509 Certificate records. @@ -150,12 +150,12 @@ Decodes a public key asn1 der encoded entity. Asn1Type = atom() - - Asn1 type present in the public_key applications + ASN.1 type present in the public_key applications asn1 specifications. Der = der_encoded() -

Decodes a public key asn1 der encoded entity.

+

Decodes a public key ASN.1 der encoded entity.

@@ -165,25 +165,25 @@ Asn1Type = atom() Asn1 type present in the public_key applications - asn1 specifications. + ASN.1 specifications. Entity = term() - The erlang representation of Asn1Type -

Encodes a public key entity with asn1 DER encoding.

+

Encodes a public key entity with ASN.1 DER encoding.

pem_decode(PemBin) -> [pem_entry()] Decode PEM binary data and return - entries as asn1 der encoded entities. + entries as ASN.1 der encoded entities. PemBin = binary() Example {ok, PemBin} = file:read_file("cert.pem").

Decode PEM binary data and return - entries as asn1 der encoded entities.

+ entries as ASN.1 der encoded entities.

@@ -258,13 +258,13 @@ pkix_decode_cert(Cert, otp|plain) -> #'Certificate'{} | #'OTPCertificate'{} - Decodes an asn1 der encoded pkix x509 certificate. + Decodes an ASN.1 der encoded pkix x509 certificate. Cert = der_encoded() -

Decodes an asn1 der encoded pkix certificate. The otp option - will use the customized asn1 specification OTP-PKIX.asn1 for +

Decodes an ASN.1 der encoded pkix certificate. The otp option + will use the customized ASN.1 specification OTP-PKIX.asn1 for decoding and also recursively decode most of the standard parts.

@@ -276,7 +276,7 @@ certificate. Asn1Type = atom() - The asn1 type can be 'Certificate', 'OTPCertificate' or a subtype of either . + The ASN.1 type can be 'Certificate', 'OTPCertificate' or a subtype of either .

Der encodes a pkix x509 certificate or part of such a @@ -394,7 +394,7 @@ signed or in the case that digest type is none it is the hashed value of "plain text" i.e. the digest. DigestType = rsa_digest_type() | dsa_digest_type() - Key = rsa_public_key() | dsa_public_key() + Key = rsa_private_key() | dsa_private_key()

Creates a digital signature.

diff --git a/lib/public_key/doc/src/using_public_key.xml b/lib/public_key/doc/src/using_public_key.xml new file mode 100644 index 0000000000..f0eaeb8654 --- /dev/null +++ b/lib/public_key/doc/src/using_public_key.xml @@ -0,0 +1,504 @@ + + + + +
+ + 20112011 + Ericsson AB. All Rights Reserved. + + + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + + + Using the public_key API + using_public_key.xml +
+ +
+ General information + +

This chapter is dedicated to showing some + examples of how to use the public_key API. Keys and certificates + used in the following sections are generated only for the purpose + of testing the public key application.

+ +

Note that some shell printouts, in the following examples, + have been abbreviated for increased readability.

+ +
+ +
+ PEM files +

Pulic key data (keys, certificates etc) may be stored in PEM format. PEM files + comes from the Private Enhanced Mail Internet standard and has a + structure that looks like this:

+ + <text> + -----BEGIN <SOMETHING>----- + <Attribute> : <Value> + <Base64 encoded DER data> + -----END <SOMETHING>----- + <text> + +

A file can contain several BEGIN/END blocks. Text lines between + blocks are ignored. Attributes, if present, are currently ignored except + for Proc-Type and DEK-Info that are used when the DER data is + encrypted.

+ +
+ DSA private key + +

Note file handling is not done by the public_key application.

+ 1> {ok, PemBin} = file:read_file("dsa.pem"). +{ok,<<"-----BEGIN DSA PRIVATE KEY-----\nMIIBuw"...>>} + +

This PEM file only has one entry a private DSA key.

+ 2> [DSAEntry] = public_key:pem_decode(PemBin). +[{'DSAPrivateKey',<<48,130,1,187,2,1,0,2,129,129,0,183, + 179,230,217,37,99,144,157,21,228,204, + 162,207,61,246,...>>, + not_encrypted}] + + 3> Key = public_key:pem_entry_decode(DSAEntry). +#'DSAPrivateKey'{version = 0, + p = 12900045185019966618...6593, + q = 1216700114794736143432235288305776850295620488937, + g = 10442040227452349332...47213, + y = 87256807980030509074...403143, + x = 510968529856012146351317363807366575075645839654} +
+ +
+ RSA private key encrypted with a password. + + 1> {ok, PemBin} = file:read_file("rsa.pem"). +{ok,<<"Bag Attribut"...>>} + +

This PEM file only has one entry a private RSA key.

+ 2>[RSAEntry] = public_key:pem_decode(PemBin). +[{'RSAPrivateKey',<<224,108,117,203,152,40,15,77,128,126, + 221,195,154,249,85,208,202,251,109, + 119,120,57,29,89,19,9,...>>, + {"DES-EDE3-CBC",<<"kÙeø¼pµL">>}}] + + + +

In this example the password is "abcd1234".

+ 3> Key = public_key:pem_entry_decode(RSAEntry, "abcd1234"). + #'RSAPrivateKey'{version = 'two-prime', + modulus = 1112355156729921663373...2737107, + publicExponent = 65537, + privateExponent = 58064406231183...2239766033, + prime1 = 11034766614656598484098...7326883017, + prime2 = 10080459293561036618240...77738643771, + exponent1 = 77928819327425934607...22152984217, + exponent2 = 36287623121853605733...20588523793, + coefficient = 924840412626098444...41820968343, + otherPrimeInfos = asn1_NOVALUE} +
+ +
+ X509 Certificates + + 1> {ok, PemBin} = file:read_file("cacerts.pem"). +{ok,<<"-----BEGIN CERTIFICATE-----\nMIIC7jCCAl"...>>} + +

This file includes two certificates

+ 2> [CertEntry1, CertEntry2] = public_key:pem_decode(PemBin). +[{'Certificate',<<48,130,2,238,48,130,2,87,160,3,2,1,2,2, + 9,0,230,145,97,214,191,2,120,150,48,13, + ...>>, + not_encrypted}, + {'Certificate',<<48,130,3,200,48,130,3,49,160,3,2,1,2,2,1, + 1,48,13,6,9,42,134,72,134,247,...>>>, + not_encrypted}] + +

Certificates may of course be decoded as usual ...

+ 2> Cert = public_key:pem_entry_decode(CertEntry1). +#'Certificate'{ + tbsCertificate = + #'TBSCertificate'{ + version = v3,serialNumber = 16614168075301976214, + signature = + #'AlgorithmIdentifier'{ + algorithm = {1,2,840,113549,1,1,5}, + parameters = <<5,0>>}, + issuer = + {rdnSequence, + [[#'AttributeTypeAndValue'{ + type = {2,5,4,3}, + value = <<19,8,101,114,108,97,110,103,67,65>>}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,11}, + value = <<19,10,69,114,108,97,110,103,32,79,84,80>>}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,10}, + value = <<19,11,69,114,105,99,115,115,111,110,32,65,66>>}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,7}, + value = <<19,9,83,116,111,99,107,104,111,108,109>>}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,6}, + value = <<19,2,83,69>>}], + [#'AttributeTypeAndValue'{ + type = {1,2,840,113549,1,9,1}, + value = <<22,22,112,101,116,101,114,64,101,114,...>>}]]}, + validity = + #'Validity'{ + notBefore = {utcTime,"080109082929Z"}, + notAfter = {utcTime,"080208082929Z"}}, + subject = + {rdnSequence, + [[#'AttributeTypeAndValue'{ + type = {2,5,4,3}, + value = <<19,8,101,114,108,97,110,103,67,65>>}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,11}, + value = <<19,10,69,114,108,97,110,103,32,79,84,80>>}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,10}, + value = <<19,11,69,114,105,99,115,115,111,110,32,...>>}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,7}, + value = <<19,9,83,116,111,99,107,104,111,108,...>>}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,6}, + value = <<19,2,83,69>>}], + [#'AttributeTypeAndValue'{ + type = {1,2,840,113549,1,9,1}, + value = <<22,22,112,101,116,101,114,64,...>>}]]}, + subjectPublicKeyInfo = + #'SubjectPublicKeyInfo'{ + algorithm = + #'AlgorithmIdentifier'{ + algorithm = {1,2,840,113549,1,1,1}, + parameters = <<5,0>>}, + subjectPublicKey = + {0,<<48,129,137,2,129,129,0,203,209,187,77,73,231,90,...>>}}, + issuerUniqueID = asn1_NOVALUE, + subjectUniqueID = asn1_NOVALUE, + extensions = + [#'Extension'{ + extnID = {2,5,29,19}, + critical = true, + extnValue = [48,3,1,1,255]}, + #'Extension'{ + extnID = {2,5,29,15}, + critical = false, + extnValue = [3,2,1,6]}, + #'Extension'{ + extnID = {2,5,29,14}, + critical = false, + extnValue = [4,20,27,217,65,152,6,30,142|...]}, + #'Extension'{ + extnID = {2,5,29,17}, + critical = false, + extnValue = [48,24,129,22,112,101,116,101|...]}]}, + signatureAlgorithm = + #'AlgorithmIdentifier'{ + algorithm = {1,2,840,113549,1,1,5}, + parameters = <<5,0>>}, + signature = + {0, + <<163,186,7,163,216,152,63,47,154,234,139,73,154,96,120, + 165,2,52,196,195,109,167,192,...>>}} + + +

Parts of certificates can be decoded with + public_key:der_decode/2 using that parts ASN.1 type. + Although application specific certificate + extension requires application specific ASN.1 decode/encode-functions. + Example, the first value of the rdnSequence above is of ASN.1 type + 'X520CommonName'. ({2,5,4,3} = ?id-at-commonName)

+ + public_key:der_decode('X520CommonName', <<19,8,101,114,108,97,110,103,67,65>>). +{printableString,"erlangCA"} + +

... but certificates can also be decode using the pkix_decode_cert/2 that + can customize and recursively decode standard parts of a certificate.

+ 3>{_, DerCert, _} = CertEntry1. + 4> public_key:pkix_decode_cert(DerCert, otp). +#'OTPCertificate'{ + tbsCertificate = + #'OTPTBSCertificate'{ + version = v3,serialNumber = 16614168075301976214, + signature = + #'SignatureAlgorithm'{ + algorithm = {1,2,840,113549,1,1,5}, + parameters = 'NULL'}, + issuer = + {rdnSequence, + [[#'AttributeTypeAndValue'{ + type = {2,5,4,3}, + value = {printableString,"erlangCA"}}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,11}, + value = {printableString,"Erlang OTP"}}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,10}, + value = {printableString,"Ericsson AB"}}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,7}, + value = {printableString,"Stockholm"}}], + [#'AttributeTypeAndValue'{type = {2,5,4,6},value = "SE"}], + [#'AttributeTypeAndValue'{ + type = {1,2,840,113549,1,9,1}, + value = "peter@erix.ericsson.se"}]]}, + validity = + #'Validity'{ + notBefore = {utcTime,"080109082929Z"}, + notAfter = {utcTime,"080208082929Z"}}, + subject = + {rdnSequence, + [[#'AttributeTypeAndValue'{ + type = {2,5,4,3}, + value = {printableString,"erlangCA"}}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,11}, + value = {printableString,"Erlang OTP"}}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,10}, + value = {printableString,"Ericsson AB"}}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,7}, + value = {printableString,"Stockholm"}}], + [#'AttributeTypeAndValue'{type = {2,5,4,6},value = "SE"}], + [#'AttributeTypeAndValue'{ + type = {1,2,840,113549,1,9,1}, + value = "peter@erix.ericsson.se"}]]}, + subjectPublicKeyInfo = + #'OTPSubjectPublicKeyInfo'{ + algorithm = + #'PublicKeyAlgorithm'{ + algorithm = {1,2,840,113549,1,1,1}, + parameters = 'NULL'}, + subjectPublicKey = + #'RSAPublicKey'{ + modulus = + 1431267547247997...37419, + publicExponent = 65537}}, + issuerUniqueID = asn1_NOVALUE, + subjectUniqueID = asn1_NOVALUE, + extensions = + [#'Extension'{ + extnID = {2,5,29,19}, + critical = true, + extnValue = + #'BasicConstraints'{ + cA = true,pathLenConstraint = asn1_NOVALUE}}, + #'Extension'{ + extnID = {2,5,29,15}, + critical = false, + extnValue = [keyCertSign,cRLSign]}, + #'Extension'{ + extnID = {2,5,29,14}, + critical = false, + extnValue = [27,217,65,152,6,30,142,132,245|...]}, + #'Extension'{ + extnID = {2,5,29,17}, + critical = false, + extnValue = [{rfc822Name,"peter@erix.ericsson.se"}]}]}, + signatureAlgorithm = + #'SignatureAlgorithm'{ + algorithm = {1,2,840,113549,1,1,5}, + parameters = 'NULL'}, + signature = + {0, + <<163,186,7,163,216,152,63,47,154,234,139,73,154,96,120, + 165,2,52,196,195,109,167,192,...>>}} + + +

This call is equivalent to public_key:pem_entry_decode(CertEntry1)

+ 5> public_key:pkix_decode_cert(DerCert, plain). +#'Certificate'{ ...} + +
+ +
+ Encoding public key data to PEM format + +

If you have public key data and and want to create a PEM file + you can do that by calling the functions + public_key:pem_entry_encode/2 and pem_encode/1 and then saving the + result to a file. For example assume you have PubKey = + 'RSAPublicKey'{} then you can create a PEM-"RSA PUBLIC KEY" file + (ASN.1 type 'RSAPublicKey') or a PEM-"PUBLIC KEY" file + ('SubjectPublicKeyInfo' ASN.1 type).

+ +

The second element of the PEM-entry will be the ASN.1 DER encoded + key data.

+ + 1> PemEntry = public_key:pem_entry_encode('RSAPublicKey', RSAPubKey). +{'RSAPublicKey', <<48,72,...>>, not_encrypted} + +2> PemBin = public_key:pem_encode([PemEntry]). +<<"-----BEGIN RSA PUBLIC KEY-----\nMEgC...>> + +3> file:write_file("rsa_pub_key.pem", PemBin). +ok + +

or

+ + 1> PemBin = public_key:pem_entry_encode('SubjectPublicKeyInfo', RSAPubKey). +{'SubjectPublicKeyInfo', <<48,92...>>, not_encrypted} + +2> PemBin = public_key:pem_encode([PemEntry]). +<<"-----BEGIN PUBLIC KEY-----\nMFw...>> + +3> file:write_file("pub_key.pem", PemBin). +ok + +
+
+ +
+ RSA public key cryptography +

Suppose you have PrivateKey = #'RSAPrivateKey{}' and the + plaintext Msg = binary() and the corresponding public key + PublicKey = #'RSAPublicKey'{} then you can do the following. + Note that you normally will only do one of the encrypt or + decrypt operations and the peer will do the other. +

+ +

Encrypt with the private key

+ RsaEncrypted = public_key:encrypt_private(Msg, PrivateKey), +Msg = public_key:decrypt_public(RsaEncrypted, PublicKey), + +

Encrypt with the public key

+ RsaEncrypted = public_key:encrypt_public(Msg, PublicKey), +Msg = public_key:decrypt_private(RsaEncrypted, PrivateKey), +
+ +
+ Digital signatures + +

Suppose you have PrivateKey = #'RSAPrivateKey{}'or + #'DSAPrivateKey'{} and the plaintext Msg = binary() and the + corresponding public key PublicKey = #'RSAPublicKey'{} or + {integer(), #'DssParams'{}} then you can do the following. Note + that you normally will only do one of the sign or verify operations + and the peer will do the other.

+ + Signature = public_key:sign(Msg, sha, PrivateKey), +true = public_key:verify(Msg, sha, Signature, PublicKey), + +

It might be appropriate to calculate the message digest before + calling sign or verify and then you can use the none as second + argument.

+ + Digest = crypto:sha(Msg), +Signature = public_key:sign(Digest, none, PrivateKey), +true = public_key:verify(Digest, none, Signature, PublicKey), + + +
+ +
+ SSH files + +

SSH typically uses PEM files for private keys but has its + own file format for storing public keys. The erlang public_key + application can be used to parse the content of SSH public key files.

+ +
+ RFC 4716 SSH public key files + +

RFC 4716 SSH files looks confusingly like PEM files, + but there are some differences.

+ 1> {ok, SshBin} = file:read_file("ssh2_rsa_pub"). +{ok, <<"---- BEGIN SSH2 PUBLIC KEY ----\nAAAA"...>>} + +

This is equivalent to calling public_key:ssh_decode(SshBin, rfc4716_public_key). +

+ 2> public_key:ssh_decode(SshBin, public_key). +[{#'RSAPublicKey'{modulus = 794430685...91663, + publicExponent = 35}, []}] + + +
+ +
+ Openssh public key format + 1> {ok, SshBin} = file:read_file("openssh_dsa_pub"). +{ok,<<"ssh-dss AAAAB3Nza"...>>} + +

This is equivalent to calling public_key:ssh_decode(SshBin, openssh_public_key). +

+ 2> public_key:ssh_decode(SshBin, public_key). +[{{15642692...694280725, + #'Dss-Parms'{p = 17291273936...696123221, + q = 1255626590179665817295475654204371833735706001853, + g = 10454211196...480338645}}, + [{comment,"dhopson@VMUbuntu-DSH"}]}] + +
+ +
+ Known hosts - openssh format + + 1> {ok, SshBin} = file:read_file("known_hosts"). +{ok,<<"hostname.domain.com,192.168.0.1 ssh-rsa AAAAB...>>} + +

Returns a list of public keys and their related attributes + each pair of key and attributes corresponds to one entry in + the known hosts file.

+ + 2> public_key:ssh_decode(SshBin, known_hosts). +[{#'RSAPublicKey'{modulus = 1498979460408...72721699, + publicExponent = 35}, + [{hostnames,["hostname.domain.com","192.168.0.1"]}]}, + {#'RSAPublicKey'{modulus = 14989794604088...2721699, + publicExponent = 35}, + [{comment,"foo@bar.com"}, + {hostnames,["|1|BWO5qDxk/cFH0wa05JLdHn+j6xQ=|rXQvIxh5cDD3C43k5DPDamawVNA="]}]}] + +
+ +
+ Authorized keys - openssh format + + 1> {ok, SshBin} = file:read_file("auth_keys"). +{ok, <<"command=\"dump /home\",no-pty,no-port-forwarding ssh-rsa AAA...>>} + +

Returns a list of public keys and their related attributes + each pair of key and attributes corresponds to one entry in + the authorized key file.

+ + 2> public_key:ssh_decode(SshBin, auth_keys). +[{#'RSAPublicKey'{modulus = 794430685...691663, + publicExponent = 35}, + [{comment,"dhopson@VMUbuntu-DSH"}, + {options,["command=\"dump/home\"","no-pty", + "no-port-forwarding"]}]}, + {{1564269258491...607694280725, + #'Dss-Parms'{p = 17291273936185...763696123221, + q = 1255626590179665817295475654204371833735706001853, + g = 10454211195705...60511039590076780999046480338645}}, + [{comment,"dhopson@VMUbuntu-DSH"}]}] + +
+ +
+ Creating an SSH file from public key data + +

If you got a public key PubKey and a related list of + attributes Attributes as returned + by ssh_decode/2 you can create a new ssh file for example

+ N> SshBin = public_key:ssh_encode([{PubKey, Attributes}], openssh_public_key), +<<"ssh-rsa "...>> +N+1> file:write_file("id_rsa.pub", SshBin). +ok +
+
+
-- cgit v1.2.3